Android SQLite DB 종료시기


96

Android에서 SQLite 데이터베이스로 작업하고 있습니다. 내 데이터베이스 관리자는 싱글 톤이며 현재 초기화 될 때 데이터베이스에 대한 연결을 엽니 다. 누군가가 내 클래스를 호출하여 데이터베이스 작업을 할 때 이미 열려 있도록 데이터베이스를 항상 열어 두는 것이 안전합니까? 또는 각 액세스가 필요하기 전과 후에 데이터베이스를 열고 닫아야합니다. 계속 열어두면 해가 되는가?

감사!

답변:


60

나는 그것을 항상 열어두고 onStop또는 같은 수명주기 방법으로 닫을 것입니다 onDestroy. 이렇게하면 사용하기 전에 매번 isDbLockedByCurrentThread또는 isDbLockedByOtherThreads단일 SQLiteDatabase객체 를 호출하여 데이터베이스가 이미 사용 중인지 쉽게 확인할 수 있습니다 . 이것은 데이터베이스에 대한 다중 조작을 방지하고 잠재적 인 충돌로부터 애플리케이션을 저장합니다.

따라서 싱글 톤에서 단일 SQLiteOpenHelper객체 를 가져 오는 방법은 다음과 같습니다 .

private SQLiteDatabase db;
private MyDBOpenHelper mySingletonHelperField;
public MyDBOpenHelper getDbHelper() {
    db = mySingletonHelperField.getDatabase();//returns the already created database object in my MyDBOpenHelper class(which extends `SQLiteOpenHelper`)
    while(db.isDbLockedByCurrentThread() || db.isDbLockedByOtherThreads()) {
        //db is locked, keep looping
    }
    return mySingletonHelperField;
}

따라서 열린 도우미 개체를 사용하고 싶을 때마다이 getter 메서드를 호출하십시오 (스레딩되었는지 확인하십시오).

싱글 톤의 다른 메소드는 다음과 같습니다 (위의 getter를 호출하기 전에 매번 호출 됨).

public void setDbHelper(MyDBOpenHelper mySingletonHelperField) {
    if(null == this.mySingletonHelperField) {
        this.mySingletonHelperField = mySingletonHelperField;
        this.mySingletonHelperField.setDb(this.mySingletonHelperField.getWritableDatabase());//creates and sets the database object in the MyDBOpenHelper class
    }
}

싱글 톤에서 데이터베이스를 닫고 싶을 수도 있습니다.

public void finalize() throws Throwable {
    if(null != mySingletonHelperField)
        mySingletonHelperField.close();
    if(null != db)
        db.close();
    super.finalize();
}

애플리케이션 사용자가 많은 데이터베이스 상호 작용을 매우 빠르게 생성 할 수있는 능력이 있다면 위에서 설명한 것과 같은 것을 사용해야합니다. 그러나 최소한의 데이터베이스 상호 작용이 있다면 걱정하지 않고 매번 데이터베이스를 만들고 닫습니다.


나는 덕분에이 방법 (KEEP 데이터베이스 개방) 2의 요인에 의해 데이터베이스 액세스 속도를 높일 수
teh.fonsi

2
회전은 매우 나쁜 기술입니다. 아래 내 대답을 참조하십시오.
mixel 2015 년

1
@mixel 좋은 지적. 나는 API (16)를 사용할 전에 내가이 답변을 게시 생각하지만, 내가 잘못 될 수
제임스

1
@binnyb 사람들을 오도하지 않도록 답변을 업데이트하는 것이 더 낫다고 생각합니다.
mixel 2015 년

3
WARN isDbLockedByOtherThreads ()는 Depricated이며 API 16부터 false를 반환합니다. 또한 isDbLockedByCurrentThread ()를 확인하면 현재 스레드가 중지되고이 메서드가 true를 반환하도록 DB 잠금을 해제 할 수 없기 때문에 무한 루프가 발생합니다.
matreshkin

20

현재로서는 데이터베이스가 다른 스레드에 의해 잠겼는지 확인할 필요가 없습니다. 모든 스레드에서 단일 SQLiteOpenHelper를 사용하는 동안 안전합니다. 에서 isDbLockedByCurrentThread문서 :

이 메소드의 이름은 데이터베이스에 대한 활성 연결이 스레드가 데이터베이스에 대한 실제 잠금을 보유하고 있음을 의미 할 때 유래되었습니다. 요즘에는 특정 작업을 수행하기 위해 데이터베이스 연결을 획득 할 수없는 경우 스레드가 차단 될 수 있지만 더 이상 진정한 "데이터베이스 잠금"이 없습니다.

isDbLockedByOtherThreads API 레벨 16부터 더 이상 사용되지 않습니다.


스레드 당 하나의 인스턴스를 사용하는 것은 의미가 없습니다. SQLiteOpenHelper는 스레드로부터 안전합니다. 이것은 또한 매우 비효율적 인 메모리입니다. 대신 앱은 데이터베이스 당 SQLiteOpenHelper의 단일 인스턴스를 유지해야합니다. 더 나은 동시성을 위해 최대 4 개의 연결에 대한 연결 풀링을 제공하는 WriteAheadLogging을 사용하는 것이 좋습니다.
ejboy

@ejboy 나는 그것을 의미했다. "스레드 당"이 아니라 "모든 스레드에서 단일 SQLiteOpenHelper를 사용하십시오".
mixel

15

질문에 관하여 :

내 데이터베이스 관리자는 싱글 톤이며 현재 초기화 될 때 데이터베이스에 대한 연결을 엽니 다.

'DB 개설'과 '연결 개설'을 나누어야합니다. SQLiteOpenHelper.getWritableDatabase ()는 열린 DB를 제공합니다. 그러나 내부적으로 수행되므로 연결을 제어 할 필요가 없습니다.

누군가가 내 클래스를 호출하여 데이터베이스 작업을 할 때 이미 열려 있도록 데이터베이스를 항상 열어 두는 것이 안전합니까?

네, 그렇습니다. 트랜잭션이 제대로 닫히면 연결이 중단되지 않습니다. GC가 완료하면 DB도 자동으로 닫힙니다.

또는 각 액세스가 필요하기 전과 후에 데이터베이스를 열고 닫아야합니다.

SQLiteDatabase 인스턴스를 닫는 것은 연결을 닫는 것 외에는 아무 것도 제공하지 않지만 현재 연결이 있으면 개발자에게 나쁜 일입니다. 또한 SQLiteDatabase.close () 이후 SQLiteOpenHelper.getWritableDatabase ()는 새 인스턴스를 반환합니다.

계속 열어두면 해가 되는가?

아니, 없습니다. 또한 Activity.onStop ()과 같이 관련없는 순간에 DB를 닫고 스레드를 닫으면 활성 연결이 닫히고 데이터가 일관성없는 상태로 남을 수 있습니다.


감사합니다. "SQLiteDatabase.close () 후 SQLiteOpenHelper.getWritableDatabase ()가 새 인스턴스를 반환합니다"라는 말을 읽은 후 마침내 응용 프로그램의 오래된 문제에 대한 답을 찾았다는 것을 깨달았습니다. 문제에 대해 Android 9로 마이그레이션 한 후 심각해졌습니다 ( stackoverflow.com/a/54224922/297710 참조 )
yvolk


1

성능 관점에서 최적의 방법은 애플리케이션 수준에서 SQLiteOpenHelper의 단일 인스턴스를 유지하는 것입니다. 데이터베이스를 여는 것은 비용이 많이 들고 차단 작업이므로 주 스레드 및 / 또는 활동 수명주기 메서드에서 수행해서는 안됩니다.

setIdleConnectionTimeout () 메서드 (Android 8.1에 도입 됨)를 사용하여 데이터베이스를 사용하지 않을 때 RAM을 확보 할 수 있습니다. 유휴 시간 제한이 설정되어 있으면 데이터베이스 연결이 일정 시간 동안 사용하지 않으면 (예 : 데이터베이스에 액세스하지 않은 경우) 닫힙니다. 새 쿼리가 실행되면 연결이 앱에 투명하게 다시 열립니다.

또한 앱 이 백그라운드로 들어가거나 메모리 부족을 감지 할 때 releaseMemory ()를 호출 할 수 있습니다 ( 예 : onTrimMemory ()).



-9

고유 한 응용 프로그램 컨텍스트를 만든 다음 거기에서 데이터베이스를 열고 닫습니다. 이 개체에는 연결을 닫는 데 사용할 수있는 OnTerminate () 메서드도 있습니다. 나는 아직 시도하지 않았지만 더 나은 접근 방식으로 보입니다.

@binnyb : 나는 finalize ()를 사용하여 연결을 닫는 것을 좋아하지 않습니다. 작동 할 수도 있지만 Java finalize () 메서드에서 코드를 작성하는 것을 이해하는 것은 나쁜 생각입니다.


언제 호출 될지 모르기 때문에 finalize에 의존하고 싶지 않습니다. 객체는 GC가 그것을 정리하기로 결정할 때까지 림보에 머물 수 있으며 그 후에 만 ​​finalize ()가 호출됩니다. 그것이 쓸모없는 이유입니다. 이를 수행하는 올바른 방법은 객체가 더 이상 사용되지 않을 때 (가비지 수집 여부에 관계없이) 호출되는 메서드를 갖는 것입니다. 이것이 바로 onTerminate ()입니다.
erwan

5
문서는 꽤 명확하게 말 그 onTerminate생산 환경에서 호출되지 않습니다 - developer.android.com/reference/android/app/...
엘리
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.