WorkManager実行時にSQLiteの外部キー制約に失敗する
はじめに
Androidアプリ開発とjavaについて学習中のQiita初投稿者です。
学習中に発生したある現象について質問させていただきます。
問題
Android Javaで使えるWorkManagerについて学習する為に、下記の方の記事を参考にWorkManagerを動かすアプリを作成してみました。
https://qiita.com/park3taro/items/3890debe0baf7944e282
しかし、動かしていたところ、WorkManagerにてSQLite関連のExceptionが発生してアプリがクラッシュしました。
ログを見ると、「デバイスのファイルシステムに不正があり、WorkManagerがアプリの内部データにアクセスできない」「SQLiteの外部キー制約に失敗」といったような内容が出力されていました。
一度アプリクラッシュを確認後、再度ビルドして数回動かしてみましたが、何故か再発しなくなり、正常に動作するようになりました。
自分なりに公式のドキュメント等を出来る限りは調べてみましたが、WorkManagerへの知見が薄い為、どういう操作をしたら発生するのかすら分かっていない状況です。
ここで知りたいことは、発生原因と再度発生させない為の対処方法です。
ご存知の方いましたら、こちらの問題について少しでも教えてくださると幸いです。
発生している問題・エラー
Fatal Exception: java.lang.IllegalStateException: The file system on the device is in a bad state. WorkManager cannot access the app's internal data store.
at androidx.work.impl.utils.ForceStopRunnable.run(ForceStopRunnable.java:128)
at androidx.work.impl.utils.SerialExecutor$Task.run(SerialExecutor.java:91)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
Caused by android.database.sqlite.SQLiteConstraintException: FOREIGN KEY constraint failed (code 787 SQLITE_CONSTRAINT_FOREIGNKEY)
at android.database.sqlite.SQLiteConnection.nativeExecuteForChangedRowCount(SQLiteConnection.java)
at android.database.sqlite.SQLiteConnection.executeForChangedRowCount(SQLiteConnection.java:890)
at android.database.sqlite.SQLiteSession.executeForChangedRowCount(SQLiteSession.java:756)
at android.database.sqlite.SQLiteStatement.executeUpdateDelete(SQLiteStatement.java:66)
at android.database.sqlite.SQLiteDatabase.executeSql(SQLiteDatabase.java:1920)
at android.database.sqlite.SQLiteDatabase.execSQL(SQLiteDatabase.java:1841)
at androidx.sqlite.db.framework.FrameworkSQLiteDatabase.execSQL(FrameworkSQLiteDatabase.java:242)
at androidx.work.impl.WorkDatabase$2.onOpen(WorkDatabase.java:167)
at androidx.work.impl.WorkDatabase_Impl$1.onOpen(WorkDatabase_Impl.java:113)
at androidx.room.RoomOpenHelper.onOpen(RoomOpenHelper.java:136)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.onOpen(FrameworkSQLiteOpenHelper.java:195)
at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:427)
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:316)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:145)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:106)
at androidx.room.RoomDatabase.inTransaction(RoomDatabase.java:476)
at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.java:281)
at androidx.work.impl.model.SystemIdInfoDao_Impl.getWorkSpecIds(SystemIdInfoDao_Impl.java:120)
at androidx.work.impl.background.systemjob.SystemJobScheduler.reconcileJobs(SystemJobScheduler.java:298)
at androidx.work.impl.utils.ForceStopRunnable.cleanUp(ForceStopRunnable.java:249)
at androidx.work.impl.utils.ForceStopRunnable.forceStopRunnable(ForceStopRunnable.java:215)
at androidx.work.impl.utils.ForceStopRunnable.run(ForceStopRunnable.java:110)
at androidx.work.impl.utils.SerialExecutor$Task.run(SerialExecutor.java:91)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
このログを見ると、SQLiteで外部キーの制約に引っ掛かっていることで発生しているように見えます。
何らかの理由でForceStopRunnableクラスが呼び出された時にDBを操作する処理が呼び出され、現象が発生するのではないかと推測しています。
実行環境
WorkManagerのバージョンです。
implementation "androidx.work:work-runtime:2.7.1"
該当するソースコード
参考にさせていただいた方とほぼ同一です。
バックキー押下時にWorkManagerを呼び出すようにしています。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
Log.d(ServiceWorker.class.getName(), "## getInstance");
if (keyCode == KeyEvent.KEYCODE_BACK) {
OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(ServiceWorker.class)
.setInitialDelay(10, TimeUnit.SECONDS)
.build();
WorkManager.getInstance(getApplicationContext()).enqueue(workRequest);
}
return super.onKeyDown(keyCode, event);
}
}
public class ServiceWorker extends Worker {
public ServiceWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@NonNull
@Override
public Result doWork() {
Log.d(ServiceWorker.class.getName(), "## バックグラウンドタスク実行");
return Result.success();
}
}
その他参考サイト
関係あるかは分かりませんが、調べていて見つけた同じような現象のQA記事を添付させていただきます。