Android SQLite DB Ne Zaman Kapatılır


98

Android üzerinde bir SQLite veritabanı ile çalışıyorum. Veritabanı yöneticim tek bir kişidir ve şu anda başlatıldığında veritabanına bir bağlantı açar. Veritabanını tüm zaman boyunca açık bırakmak güvenlidir, böylece birisi veritabanıyla çalışmak için sınıfımı aradığında zaten açık olur? Ya da her erişimden önce ve sonra veritabanını açıp kapatmalı mıyım? Her zaman açık bırakmanın bir zararı var mı?

Teşekkürler!

Yanıtlar:


60

tüm zaman boyunca açık tutardım ve onStopveya gibi bazı yaşam döngüsü yöntemlerinde kapatırdım onDestroy. bu şekilde, kullanmadan önce her seferinde tek bir nesneyi arayarak isDbLockedByCurrentThreadveya veritabanının zaten kullanımda olup olmadığını kolayca kontrol edebilirsiniz . bu, veri tabanında birden fazla değişiklik yapılmasını önleyecek ve uygulamanızı olası bir çökmeden kurtaracaktır.isDbLockedByOtherThreadsSQLiteDatabase

Dolayısıyla, tekliğinizde, tek SQLiteOpenHelpernesnenizi elde etmek için bunun gibi bir yönteminiz olabilir :

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;
}

bu nedenle, açık yardımcı nesnenizi her kullanmak istediğinizde, bu alıcı yöntemini çağırın (iş parçacığına sahip olduğundan emin olun)

tekliğinizde başka bir yöntem olabilir (yukarıdaki alıcıyı aramadan önce HER ZAMAN denir):

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
    }
}

Veritabanını tekli olarak da kapatmak isteyebilirsiniz:

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

Uygulamanızın kullanıcıları çok hızlı bir şekilde birçok veritabanı etkileşimi oluşturma yeteneğine sahipse, yukarıda gösterdiğim gibi bir şey kullanmalısınız. ancak minimum veritabanı etkileşimi varsa, bunun için endişelenmem ve her seferinde veritabanını oluşturup kapatın.


Bu yaklaşımla veritabanı erişimini 2 kat hızlandırabilirim (veritabanını açık tut), teşekkürler
teh.fonsi

2
Eğirme çok kötü bir tekniktir. Aşağıdaki cevabıma bakın.
mixel

1
@mixel iyi nokta. Sanırım bu yanıtı API 16 kullanıma sunulmadan önce göndermiştim, ancak yanılıyor olabilirim
james

1
@binnyb İnsanları yanıltmaması için cevabınızı güncellemenin daha iyi olacağını düşünüyorum.
mixel

3
WARN isDbLockedByOtherThreads () Depricated ve API 16'dan beri false döndürüyor. Ayrıca isDbLockedByCurrentThread () 'in kontrol edilmesi, mevcut iş parçacığını durdurduğundan ve bu yöntemin true döndürmesi için hiçbir şey' DB'nin kilidini açamayacağından 'sonsuz döngü verecektir.
matreshkin

20

Şu an itibariyle veritabanının başka bir iş parçacığı tarafından kilitlenip kilitlenmediğini kontrol etmeye gerek yoktur. Her iş parçacığında singleton SQLiteOpenHelper kullanırken güvendesiniz. Gönderen isDbLockedByCurrentThreadbelgeler:

Bu yöntemin adı, veritabanına aktif bir bağlantıya sahip olduğu ve iş parçacığının veritabanında gerçek bir kilit tuttuğu anlamına geldiği zamandan gelir. Günümüzde artık gerçek bir "veritabanı kilidi" yoktur, ancak iş parçacıkları belirli bir işlemi gerçekleştirmek için bir veritabanı bağlantısı elde edemezlerse engelleyebilir.

isDbLockedByOtherThreads API Seviye 16'dan beri kullanımdan kaldırılmıştır.


İş parçacığı başına bir örnek kullanmanın bir anlamı yoktur. SQLiteOpenHelper iş parçacığı açısından güvenlidir. Bu aynı zamanda çok yetersiz hafıza. Bunun yerine, bir uygulama veritabanı başına tek bir SQLiteOpenHelper örneğini tutmalıdır. Daha iyi eşzamanlılık için, 4 bağlantıya kadar bağlantı havuzu sağlayan WriteAheadLogging kullanılması önerilir.
ejboy

@ejboy bunu kastetmiştim. "Her iş parçacığında" tekil (= bir) SQLiteOpenHelper kullanın "," iş parçacığı başına "değil.
mixel

15

Sorularla ilgili olarak:

Veritabanı yöneticim tek bir kişidir ve şu anda başlatıldığında veritabanına bir bağlantı açar.

'DB'yi açmayı', 'bağlantı açmayı' bölmeliyiz. SQLiteOpenHelper.getWritableDatabase (), açık bir DB verir. Ancak dahili olarak yapıldığı için bağlantıları kontrol etmemiz gerekmez.

Veritabanını tüm zaman boyunca açık bırakmak güvenlidir, böylece birisi veritabanıyla çalışmak için sınıfımı aradığında zaten açık olur?

Evet öyle. İşlemler düzgün bir şekilde kapatılırsa bağlantılar takılmaz. GC sonlandırırsa DB'nizin de otomatik olarak kapatılacağını unutmayın.

Ya da her erişimden önce ve sonra veritabanını açıp kapatmalı mıyım?

SQLiteDatabase örneğini kapatmak, bağlantıları kapatmak dışında çok büyük bir şey vermez, ancak şu anda bazı bağlantılar varsa, bu bir geliştiricinin kötüdür. Ayrıca, SQLiteDatabase.close () 'den sonra, SQLiteOpenHelper.getWritableDatabase () yeni bir örnek döndürecektir.

Sürekli açık bırakmanın bir zararı olur mu?

Hayır yok. Ayrıca DB'yi ilgisiz bir anda kapatmanın ve örneğin Activity.onStop () iş parçacığının etkin bağlantıları kapatabileceğini ve verileri tutarsız durumda bırakabileceğini unutmayın.


Teşekkürler, sözcüklerinizi okuduktan sonra "SQLiteDatabase.close () 'den sonra, SQLiteOpenHelper.getWritableDatabase () yeni bir örnek döndürecek" Sonunda uygulamamın eski bir sorununa bir yanıtım olduğunu fark ettim. Android 9'a geçişten sonra kritik hale gelen bir sorun için (bkz. Stackoverflow.com/a/54224922/297710 )
yvolk


1

Performans açısından en uygun yol, tek bir SQLiteOpenHelper örneğini uygulama düzeyinde tutmaktır. Veritabanını açmak pahalı olabilir ve engelleyici bir işlemdir, bu nedenle ana iş parçacığında ve / veya etkinlik yaşam döngüsü yöntemlerinde yapılmamalıdır.

setIdleConnectionTimeout () yöntemi (Android 8.1'de tanıtılmıştır) veritabanı kullanılmadığında RAM'i boşaltmak için kullanılabilir. Boşta kalma zaman aşımı ayarlanırsa, veritabanı bağlantıları belirli bir süre kullanılmadığında, yani veritabanına erişilmediğinde kapatılır. Yeni bir sorgu yürütüldüğünde, bağlantılar uygulamaya şeffaf bir şekilde yeniden açılacaktır.

Buna ek olarak, bir uygulama çağırabilirsiniz releaseMemory () arka planda gider veya örneğin bellek baskısı, algıladığında onTrimMemory) (


0

Ayrıca ContentProvider'ı da kullanabilirsiniz. Bu şeyleri sizin için yapacak.


-9

Kendi Uygulama bağlamınızı oluşturun, ardından veritabanını buradan açıp kapatın. Bu nesnede ayrıca bağlantıyı kapatmak için kullanabileceğiniz bir OnTerminate () yöntemi vardır. Henüz denemedim ama daha iyi bir yaklaşım gibi görünüyor.

@binnyb: Bağlantıyı kapatmak için finalize () kullanmayı sevmiyorum. İşe yarayabilir, ancak anladığım kadarıyla bir Java finalize () yönteminde kod yazmak kötü bir fikir.


Sonlandırmaya güvenmek istemezsiniz çünkü ne zaman çağrılacağını asla bilemezsiniz. Bir nesne, GC onu temizlemeye karar verene kadar belirsizlikte kalabilir ve ancak o zaman finalize () çağrılacaktır. Bu yüzden işe yaramaz. Bunu yapmanın doğru yolu, nesne artık kullanılmadığında (çöp toplanıp toplanmadığına bakılmaksızın) çağrılan bir yönteme sahip olmaktır ve tam olarak onTerminate () budur.
erwan

6
Dokümanlar onTerminate, bunun bir üretim ortamında çağrılmayacağını açıkça söylüyor - developer.android.com/reference/android/app/…
Eliezer
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.