Tôi đang cố gắng lấy mẫu không truy cập đa luồng vào cơ sở dữ liệu sqlite của mình. Ngoài ra, những gì đang thúc đẩy tôi hạt là tôi không thể tái tạo vấn đề.Cơ sở dữ liệu SQLite, đa luồng, Khóa và đồng bộ hóa tài khoản trên android
Tôi có một ứng dụng sử dụng DB, nhưng cũng đồng bộ hóa Tài khoản Android và Android để đồng bộ hóa dữ liệu của ứng dụng của tôi. Tôi đoán là khi cả hai xảy ra cùng một lúc, nó bị treo. Tôi nhận được rất nhiều lỗi như:
* android.database.sqlite.SQLiteDatabaseLockedException: database is locked
* android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)
* android.database.sqlite.SQLiteDatabaseLockedException: error code 5: database is locked
* android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5): , while compiling: PRAGMA journal_mode
* android.database.sqlite.SQLiteDiskIOException: disk I/O error (code 778)
* android.database.sqlite.SQLiteException: Failed to change locale for db '/data/data/net.bicou.redmine/databases/redmine.db' to 'en_US'. \n Caused by: android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)
Có thể không phải tất cả chúng đều liên quan đến cùng một nguyên nhân gốc, tuy nhiên tôi bị mất.
Những gì tôi có là:
- một lớp cơ sở trừu tượng,
DbAdapter
, mà được mở rộng bởi lớp con mà muốn quản lý một bảng duy nhất - một lớp quản lý cơ sở dữ liệu SQLite, gọi
DbManager
, trong đó có aLock
Ngay bây giờ người dùng có phiên bản DbManager
không phải là đơn. Tôi đang lập kế hoạch để làm cho DbManager
một singleton, để tất cả các chủ đề chia sẻ cùng một đối tượng. Đây không phải là một vấn đề, bởi vì theo như tôi đã hiểu/nhìn thấy, đồng bộ hóa nền và ứng dụng chia sẻ cùng một quá trình.
Dưới đây là các lớp (chỉ những phần có liên quan):
public abstract class DbAdapter {
Context mContext;
protected DbManager mDbManager;
SQLiteDatabase mDb;
public static final String KEY_ROWID = "_id";
public DbAdapter(final Context ctx) {
mContext = ctx;
}
public DbAdapter(final DbAdapter other) {
mContext = other.mContext;
mDb = other.mDb;
mDbManager = other.mDbManager; // removed with singleton version
}
public synchronized DbAdapter open() throws SQLException {
if (mDb != null) {
return this;
}
mDbManager = new DbManager(mContext); // currently in production
mDbManager = DbManager.instance(mContext); // currently investigating this singleton solution
try {
mDb = mDbManager.getWritableDatabase();
} catch (final SQLException e) {
L.e("Unable to open DB, trying again in 1 second", e);
try {
Thread.sleep(1000);
} catch (final InterruptedException e1) {
L.e("Could not wait 1 second " + e1);
}
mDb = mDbManager.getWritableDatabase();// This may crash
}
return this;
}
public synchronized void close() {
mDbManager.close();
mDbManager = null;
mDb = null;
}
}
Một lớp học mà cần phải xử lý một bảng cơ sở dữ liệu sẽ mở rộng DbAdapter
, và thực hiện các phương pháp như select
, insert
, delete
vv
Dưới đây là người quản lý DB:
public class DbManager extends SQLiteOpenHelper {
private static final String DB_FILE = "db";
private static final int DB_VERSION = 15;
Context mContext;
Lock mLock = new ReentrantLock();
// Currently in prod
public DbManager(final Context context) {
super(context, DB_FILE, null, DB_VERSION);
mContext = context;
}
// singleton version will make this constructor private and add:
private static DbManager mInstance;
public static synchronized DbManager instance(Context context) {
if (instance == null) {
instance = new DbManager(context);
}
return instance;
}
@Override
public SQLiteDatabase getWritableDatabase() {
mLock.lock();
return super.getWritableDatabase();
}
@Override
public void close() {
super.close();
mLock.unlock();
}
@Override
public void onCreate(final SQLiteDatabase db) {
// ...
}
@Override
public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) {
// ...
}
private void createTables(final SQLiteDatabase db, final String[] statements) {
for (final String sql : statements) {
try {
db.execSQL(sql);
} catch (final Exception e) {
L.e("Unable to create table: " + sql, e);
}
}
}
}
OK, bây giờ, các câu hỏi.
- Khóa của tôi có được triển khai đúng cách không? Tôi thực sự mới với điều này, tôi không biết nếu
ReentrantLock
là một lựa chọn tốt và nếu tôi đang khóa/mở khóa vào đúng thời điểm - Phương pháp
synchronized
của tôi có được triển khai đúng không? Ý tôi là, tôi đã đặt từ khóasynchronized
xung quanh các phương thức mà tôi không muốn bị gián đoạn bởi các chuỗi đồng thời. Thê nay đung không? Bạn có thể tư vấn về việc sử dụng sốsynchronized
của tôi không? - Làm cách nào để tái tạo sự cố? Tôi đã tạo ra một thử nghiệm sử dụng 3 chủ đề mà làm cho đồng thời đọc/ghi truy cập vào DB, và sử dụng một số
Thread.sleep
để đảm bảo rằng db mở/đóng từ mỗi thread chồng lên nhau, nhưng nó không sụp đổ. Điều này thực sự làm tôi thất vọng, tôi không nghĩ có nhiều người có vấn đề, vì vậy tôi không biết cách tái sản xuất. - Có phải
DbAdapter
+DbManager
lựa chọn kỹ thuật của tôi là một ý tưởng hay không? Có mô hình tốt hơn không? - Bạn có nên tạo
DbManager
một đĩa đơn không?