You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android端SQLiteDatabaseLockedException(code 5 SQLITE_BUSY)崩溃问题求助

排查SQLiteDatabaseLockedException崩溃问题

部分设备出现崩溃,触发Fatal Exception: android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5 SQLITE_BUSY),具体堆栈如下:

Fatal Exception: android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5 SQLITE_BUSY)
   at android.database.sqlite.SQLiteConnection.nativeExecute(SQLiteConnection.java)
   at android.database.sqlite.SQLiteConnection.execute(SQLiteConnection.java:715)
   at android.database.sqlite.SQLiteConnection.setLocaleFromConfiguration(SQLiteConnection.java:468)
   at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:269)
   at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:211)
   at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:505)
   at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:206)
   at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:198)
   at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:919)
   at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:899)
   at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:763)
   at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:752)
   at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:373)
   at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:316)
   at com.zingoy.app.getlink.GetLinkStoreDBHandler.addC2CStore(GetLinkStoreDBHandler.java:94)
   at com.zingoy.app.home.HomeActivity.lambda$setAllStores$8(HomeActivity.java:624)
   at java.lang.Thread.run(Thread.java:1012)

已查阅多种解决方案但未定位代码问题,以下是相关代码:

HomeActivity中数据库调用逻辑

public void setAllStores(final AllStoreFetchListener listener) {
    if (DateUtil.isTimeMoreThanHours(mPreference.getAllStoresFetchedTime(), 24)) {
        new CommonApi().withAllStoreListener(response -> {
            try {
                if (response == null) return;
                if (CollectionUtils.isEmpty(response.stores)) return;
                mPreference.setAllStoresFetchedTime();
                Handler handler = new Handler(Looper.getMainLooper());
                new Thread(() -> {
                    new GetLinkStoreDBHandler(HomeActivity.this).addStore(response.stores);
                    this.runOnUiThread(() -> {
                        if (listener != null) {
                            listener.onAllStoreFetched();
                        }
                    });
                }).start();
            } catch (Exception e) {
                FirebaseCrashlytics.getInstance().log(e.getMessage());
                e.printStackTrace();
            }
        }).getStoreData();
    }
    if (DateUtil.isTimeMoreThanHours(mPreference.getC2CStoresFetchedTime(), 6)) {
        new SellerApi().withSellerStoreListener(response -> {
            try {
                if (response == null) return;
                if (CollectionUtils.isEmpty(response.stores)) return;
                mPreference.setC2CStoresFetchedTime();
                Handler handler = new Handler(Looper.getMainLooper());
                new Thread(() -> {
                    new GetLinkStoreDBHandler(HomeActivity.this).addC2CStore(response.stores);
                    this.runOnUiThread(() -> {
                        if (listener != null) {
                            listener.onC2CStoreFetched();
                        }
                    });
                }).start();
            } catch (Exception e) {
                FirebaseCrashlytics.getInstance().log(e.getMessage());
                e.printStackTrace();
            }
        }).getStoreData();
    }
}

GetLinkStoreDBHandler实现

public class GetLinkStoreDBHandler extends SQLiteOpenHelper {

private static final int DATABASE_VERSION = 3;
private static final String DATABASE_NAME = "GetLinkStores";
private static final String TABLE_STORE = "Store";
private static final String TABLE_C2C_STORE = "SellerStore";

// StoreDbRows Table Columns names
private static final String KEY_STORE_ID = "storeId";
private static final String KEY_STORE_SLUG = "storeSlug";
private static final String KEY_STORE_NAME = "storeName";
private static final String KEY_STORE_LINK = "storeLink";
private static final String KEY_STORE_IMAGE_URL = "storeImageUrl";

public GetLinkStoreDBHandler(Context context) {
    super(context, DATABASE_NAME, null, DATABASE_VERSION);
}

// Creating Tables
@Override
public void onCreate(SQLiteDatabase db) {
    String CREATE_StoreDbRowS_TABLE = "CREATE TABLE " + TABLE_STORE + "("
            + KEY_STORE_ID + " INTEGER PRIMARY KEY," + KEY_STORE_NAME + " TEXT," + KEY_STORE_LINK + " TEXT,"
            + KEY_STORE_IMAGE_URL + " TEXT" + ")";
    db.execSQL(CREATE_StoreDbRowS_TABLE);
    String CREATE_SELLER_TABLE = "CREATE TABLE " + TABLE_C2C_STORE + "("
            + KEY_STORE_SLUG + " TEXT," + KEY_STORE_NAME + " TEXT," + KEY_STORE_IMAGE_URL + " TEXT" + ")";
    db.execSQL(CREATE_SELLER_TABLE);
}

// Upgrading database
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    // Drop older table if existed
    try {
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_STORE);
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_C2C_STORE);
    } catch (Exception e) {
        e.printStackTrace();
        FirebaseCrashlytics.getInstance().log(e.getMessage());
    } finally {
        // Create tables again
        onCreate(db);
    }
}


// Adding new Store row
public void addStore(List<Store> getLinkStore) {
    SQLiteDatabase db = this.getWritableDatabase();
    try {
        for (Store stores : getLinkStore) {
            ContentValues values = new ContentValues();
            values.put(KEY_STORE_ID, stores.id);
            values.put(KEY_STORE_NAME, stores.name);
            values.put(KEY_STORE_LINK, stores.url);
            values.put(KEY_STORE_IMAGE_URL, stores.photoUrl);

            if (!isStoreAvailable(stores.id)) {
                // Inserting Row
                db.insert(TABLE_STORE, null, values);
            }
        }
    } catch (Exception e) {
        if (e.getMessage() == null) return;
        FirebaseCrashlytics.getInstance().log(e.getMessage());
    } finally {
        if (db.isOpen())
            db.close();
    }
}

问题定位

核心原因是多实例并发访问SQLite数据库:

  1. setAllStores方法中,两个异步请求的回调会同时触发,各自在新线程中创建GetLinkStoreDBHandler实例,调用addStore和addC2CStore方法。
  2. SQLite是单写锁机制,当一个连接持有写锁时,另一个连接请求写锁会直接抛出SQLiteDatabaseLockedException。
  3. GetLinkStoreDBHandler未实现单例,每次操作都新建实例,无法复用数据库连接,加剧了锁冲突概率。

解决方案

1. 将DBHandler改为单例模式

确保应用中只有一个DBHandler实例,避免多实例竞争数据库锁:

public class GetLinkStoreDBHandler extends SQLiteOpenHelper {
    private static GetLinkStoreDBHandler sInstance;

    public static synchronized GetLinkStoreDBHandler getInstance(Context context) {
        if (sInstance == null) {
            // 使用Application Context避免内存泄漏
            sInstance = new GetLinkStoreDBHandler(context.getApplicationContext());
        }
        return sInstance;
    }

    private GetLinkStoreDBHandler(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    // 原有方法保持不变
}

调用时替换为:

GetLinkStoreDBHandler.getInstance(HomeActivity.this).addStore(response.stores);

2. 串行化数据库写操作

使用单线程执行器处理所有数据库写操作,确保同一时间只有一个写任务执行:

// 在HomeActivity中初始化单线程执行器
private ExecutorService mDbExecutor = Executors.newSingleThreadExecutor();

// 替换原线程创建代码
mDbExecutor.execute(() -> {
    GetLinkStoreDBHandler.getInstance(HomeActivity.this).addStore(response.stores);
    runOnUiThread(() -> {
        if (listener != null) {
            listener.onAllStoreFetched();
        }
    });
});

3. 优化批量插入逻辑

使用事务减少锁持有时间,提升插入性能:

public void addStore(List<Store> getLinkStore) {
    SQLiteDatabase db = this.getWritableDatabase();
    try {
        db.beginTransaction(); // 开启事务
        for (Store stores : getLinkStore) {
            ContentValues values = new ContentValues();
            values.put(KEY_STORE_ID, stores.id);
            values.put(KEY_STORE_NAME, stores.name);
            values.put(KEY_STORE_LINK, stores.url);
            values.put(KEY_STORE_IMAGE_URL, stores.photoUrl);

            if (!isStoreAvailable(stores.id)) {
                db.insert(TABLE_STORE, null, values);
            }
        }
        db.setTransactionSuccessful(); // 标记事务成功
    } catch (Exception e) {
        if (e.getMessage() != null) {
            FirebaseCrashlytics.getInstance().log(e.getMessage());
        }
    } finally {
        db.endTransaction(); // 结束事务
        if (db.isOpen()) {
            db.close();
        }
    }
}

内容的提问来源于stack exchange,提问作者Sam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 02:17:04