<span style="font-size:18px;">
</span>
android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)
上面的问题,是因为每次创建SQLiteOpenHelper对象时,实际上也是在新建一个数据库连接。如果通过多个连接同时对数据库进行写数据操作,则一定会失败。为确保多线程能安全地操作数据库,则需要保证只有一个数据库连接被占用。
<span style="font-size:18px;">
</span>
java.lang.IllegalStateException:attempt to re-open an already-closed object: SQLiteDatabase
既然只有一个数据库连接,不同的线程会取得一样的SQLiteDatabase对象实例。当线程1尝试管理数据库连接时,线程2却仍然在使用该数据库连接。这就导致了上面问题的原因。因此我们只能在确保数据库没有被占用的情况下,才去关闭它。
DatabaseManager:
<span style="font-size:18px;">public class DatabaseManager {
private AtomicInteger mOpenCounter = new AtomicInteger();
private static DatabaseManager mInstance;
private SQLiteOpenHelper mDatabaseHelper;
private SQLiteDatabase mDatabase;
private DatabaseManager(SQLiteOpenHelper helper) {
mDatabaseHelper = helper;
}
public static synchronized void initInstance(SQLiteOpenHelper helper) {
if (mInstance == null) {
mInstance = new DatabaseManager(helper);
}
}
public static synchronized DatabaseManager getInstance() {
if (mInstance == null) {
throw new IllegalStateException(
"DatabaseManager: 请先调用始化操作initInstance");
}
return mInstance;
}
public void executeQuery(IQueryExecutor executor) {
SQLiteDatabase database = openDatabase();
executor.run(database);
closeDatabase();
}
public void executeQueryTask(final IQueryExecutor executor) {
new Thread(new Runnable() {
@Override
public void run() {
executeQuery(executor);
}
}).start();
}
private synchronized SQLiteDatabase openDatabase() {
if (mOpenCounter.incrementAndGet() == 1) {
mDatabase = mDatabaseHelper.getWritableDatabase();
}
return mDatabase;
}
private synchronized void closeDatabase() {
if (mOpenCounter.decrementAndGet() == 0) {
mDatabase.close();
}
}
}</span>
helper文件:
<span style="font-size:18px;">public class AddPathToDBHelper extends SQLiteOpenHelper {
private static final int START_DATABASE_VERSION = 1;
private static final int CURRENT_DATABASE_VERSION = 1;
private static final String DB_NAME = "paths.db";
public AddPathToDBHelper(Context context) {
super(context, DB_NAME, null, CURRENT_DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table if not exists " + Table.TABLE_NAME + " ( "
+ Table._ID + " integer primary key autoincrement , "
+ Table.PATH + " text unique not null , " + Table.LAST_MODIFIED
+ " long );");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
switch (oldVersion) {
case START_DATABASE_VERSION:
break;
default:
break;
}
}
}</span>
DAO文件:
<span style="font-size:18px;">public class AddPathDAO {
interface Table {
String TABLE_NAME = "allPaths";
String _ID = "_id";
String PATH = "path";
String LAST_MODIFIED = "last_modified";
}
private SQLiteDatabase mDatabase;
public AddPathDAO(SQLiteDatabase database) {
mDatabase = database;
}
public void insertPath(HashMap<String, Long> map) {
ContentValues values = new ContentValues();
try {
mDatabase.beginTransaction();
for (Entry<String, Long> item : map.entrySet()) {
values.put(Table.PATH, item.getKey());
values.put(Table.LAST_MODIFIED, item.getValue());
mDatabase.insert(Table.TABLE_NAME, null, values);
}
mDatabase.setTransactionSuccessful();
} catch (Exception e) {
e.printStackTrace();
} finally {
mDatabase.endTransaction();
}
}
}</span>
调用用例:
<span style="font-size:18px;">public class MainActivity extends Activity {
private HashMap<String, Long> map;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DatabaseManager.initInstance(new AddPathToDBHelper(
getApplicationContext()));
map = new HashMap<String, Long>();
map.put("sdcard0/tencet", 1234354566L);
map.put("sdcard0/tencet1", 1234354566L);
map.put("sdcard0/tencet2", 1234354566L);
DatabaseManager.getInstance().executeQuery(new IQueryExecutor() {
@Override
public void run(SQLiteDatabase database) {
AddPathDAO addPathDAO = new AddPathDAO(database);
addPathDAO.insertPath(map);
}
});
}
}</span>