多线程访问数据库


android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)

上面的问题,是因为每次创建SQLiteOpenHelper对象时,实际上也是在新建一个数据库连接。如果通过多个连接同时对数据库进行写数据操作,则一定会失败。为确保多线程能安全地操作数据库,则需要保证只有一个数据库连接被占用。


java.lang.IllegalStateException:attempt to re-open an already-closed object: SQLiteDatabase

既然只有一个数据库连接,不同的线程会取得一样的SQLiteDatabase对象实例。当线程1尝试管理数据库连接时,线程2却仍然在使用该数据库连接。这就导致了上面问题的原因。因此我们只能在确保数据库没有被占用的情况下,才去关闭它。

DatabaseManager:

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

}

helper文件:

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

}

DAO文件:

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 map) {
        ContentValues values = new ContentValues();
        try {
            mDatabase.beginTransaction();
            for (Entry 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();
        }
    }
}

调用用例:

public class MainActivity extends Activity {

    private HashMap map;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DatabaseManager.initInstance(new AddPathToDBHelper(
                getApplicationContext()));
        map = new HashMap();
        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);
            }
        });
    }

}

你可能感兴趣的:(Android)