小白到大神,你需要了解的 sqlite 最佳实践
本文微信公众号「AndroidTraveler」首发。 背景本文是对一篇英文文档的翻译,原文请见文末链接。 并发数据库访问假设你实现了自己的 SQLiteOpenHelper。 public class DatabaseHelper extends SQLiteOpenHelper { ... } 现在你想要在多个线程中对数据库写入数据。 // Thread 1 Context context = getApplicationContext(); DatabaseHelper helper = new DatabaseHelper(context); SQLiteDatabase database = helper.getWritableDatabase(); database.insert(…); database.close(); // Thread 2 Context context = getApplicationContext(); DatabaseHelper helper = new DatabaseHelper(context); SQLiteDatabase database = helper.getWritableDatabase(); database.insert(…); database.close(); 你将会在你的 logcat 中发现下面信息,并且你的其中一个改变不会写入数据库: android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5) 产生这个错误的原因是因为,每次你创建新的
让我们构造单例类 public class DatabaseManager { private static DatabaseManager instance; private static SQLiteOpenHelper mDatabaseHelper; public static synchronized void initializeInstance(SQLiteOpenHelper helper) { if (instance == null) { instance = new DatabaseManager(); mDatabaseHelper = helper; } } public static synchronized DatabaseManager getInstance() { if (instance == null) { throw new IllegalStateException(DatabaseManager.class.getSimpleName() + " is not initialized,call initialize(..) method first."); } return instance; } public synchronized SQLiteDatabase getDatabase() { return mDatabaseHelper.getWritableDatabase(); } } 在多个线程中对数据库写入数据,修改后的代码如下所示。 // In your application class DatabaseManager.initializeInstance(new DatabaseHelper()); // Thread 1 DatabaseManager manager = DatabaseManager.getInstance(); SQLiteDatabase database = manager.getDatabase() database.insert(…); database.close(); // Thread 2 DatabaseManager manager = DatabaseManager.getInstance(); SQLiteDatabase database = manager.getDatabase() database.insert(…); database.close(); 这会带来另一个奔溃。 java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase 由于我们只使用了一个数据库连接,Thread1 和 Thread2 的 我们需要确保没有人正在使用数据库,这个时候我们才可以关闭它。stackoveflow 上有人推荐永远不要关闭你的 SQLiteDatabase。这会让你看到下面的 logcat 信息。所以我一点也不认为这是一个好的想法。 Leak found Caused by: java.lang.IllegalStateException: SQLiteDatabase created and never closed 实战例子一种可能的解决方案是使用计数器跟踪打开/关闭的数据库连接。 public class DatabaseManager { private AtomicInteger mOpenCounter = new AtomicInteger(); private static DatabaseManager instance; private static SQLiteOpenHelper mDatabaseHelper; private SQLiteDatabase mDatabase; public static synchronized void initializeInstance(SQLiteOpenHelper helper) { if (instance == null) { instance = new DatabaseManager(); mDatabaseHelper = helper; } } public static synchronized DatabaseManager getInstance() { if (instance == null) { throw new IllegalStateException(DatabaseManager.class.getSimpleName() + " is not initialized,call initializeInstance(..) method first."); } return instance; } public synchronized SQLiteDatabase openDatabase() { if(mOpenCounter.incrementAndGet() == 1) { // Opening new database mDatabase = mDatabaseHelper.getWritableDatabase(); } return mDatabase; } public synchronized void closeDatabase() { if(mOpenCounter.decrementAndGet() == 0) { // Closing database mDatabase.close(); } } } 然后如下所示来使用。 SQLiteDatabase database = DatabaseManager.getInstance().openDatabase(); database.insert(...); // database.close(); Don't close it directly! DatabaseManager.getInstance().closeDatabase(); // correct way 每当你需要使用数据库的时候你应该调用 对于 现在你能够使用你的数据库并且确保是线程安全的。 原文:Concurrent database access 由于本人翻译水平有限,如果你有更好的翻译文案,欢迎在 GitHub 提 PR。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |