2013-07-18 72 views
7

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ó a Lock

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.

  1. 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
  2. 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óa synchronized 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?
  3. 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.
  4. 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?
  5. Bạn có nên tạo DbManager một đĩa đơn không?

Trả lời

0

Để truy cập nhiều chủ đề, bạn nên sử dụng mẫu đơn.

Như vậy, các cuộc gọi liên tiếp đến cùng một cơ sở dữ liệu sẽ được tuần tự liên tục.

Tuy nhiên, không thể không có một số chèn NullPointerException s. Vì vậy, để mở rộng logic "Thread.sleep", bạn có thể sử dụng mã này:

@Override 
public SQLiteDatabase getWritableDatabase() { 
    while (true) { 
     try { 
      return super.getWritableDatabase(); 
     } catch (SQLiteDatabaseLockedException e) { 
      System.err.println(e); 
     } 

     try { 
      Thread.sleep(500); 
     } catch (InterruptedException e) { 
      System.err.println(e); 
     } 
    } 
} 
Các vấn đề liên quan