My app seems to have one major cause of crashes in the reports: java.lang.IllegalStateException
I can see where it is coming from, it is the functionality that constructs notifications from DB content in a background process during the day, when the app is not active. The interesting thing is that this seems to happen only on the newest Android 10 version:
Did something change about how you process database data in a non-active app state?
Here is the crash report:
java.lang.RuntimeException:
at android.os.AsyncTask$4.done (AsyncTask.java:399)
at java.util.concurrent.FutureTask.finishCompletion (FutureTask.java:383)
at java.util.concurrent.FutureTask.setException (FutureTask.java:252)
at java.util.concurrent.FutureTask.run (FutureTask.java:271)
at android.os.AsyncTask$SerialExecutor$1.run (AsyncTask.java:289)
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:919)
Caused by: java.lang.IllegalStateException:
at androidx.room.RoomOpenHelper.checkIdentity (RoomOpenHelper.java:139)
at androidx.room.RoomOpenHelper.onOpen (RoomOpenHelper.java:119)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.onOpen (FrameworkSQLiteOpenHelper.java:151)
at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked (SQLiteOpenHelper.java:428)
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase (SQLiteOpenHelper.java:317)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase (FrameworkSQLiteOpenHelper.java:96)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase (FrameworkSQLiteOpenHelper.java:54)
at androidx.room.RoomDatabase.query (RoomDatabase.java:256)
at androidx.room.util.DBUtil.query (DBUtil.java:54)
(the next 3 lines are where it's at)
at com.dev.solidmind.db.dao.MNotificationDao_Impl.getNextNotification (MNotificationDao_Impl.java:75)
at com.dev.solidmind.repository.NotificationRepository$FetchNextNotifAsyncTask.doInBackground (NotificationRepository.java:58)
at com.dev.solidmind.repository.NotificationRepository$FetchNextNotifAsyncTask.doInBackground (NotificationRepository.java:41)
at android.os.AsyncTask$3.call (AsyncTask.java:378)
at java.util.concurrent.FutureTask.run (FutureTask.java:266)
As for the code, I have an AlarmManager
that triggers a listener at some point during the day. In that listener I call an AsyncTask
that retrieves data from a DB in the background and calls an interface method to notify the listener when it's done:
public class NotificationReceiver extends BroadcastReceiver implements NotificationRepository.FetchNextNotifAsyncTask.AsyncResponse {
private Context context;
private NotificationRepository repository;
private MessageNotification nextNotification;
@Override
public void onReceive(Context context, Intent intent) {
this.context = context;
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putLong("lastTimeOfNotifTrigger", System.currentTimeMillis());
editor.apply();
repository = new NotificationRepository(context);
repository.startGetNextNotificationAsync(this);
}
@Override
//Callback for when notification object has been retrieved from DB
public void processFinish(MessageNotification notification) {
if (notification != null) {
nextNotification = notification;
constructNotification();
setNextAlarm();
}
}
...
public class NotificationRepository {
private MNotificationDao mNotificationDao;
private SuggestionDao suggestionDao;
public NotificationRepository(Context context) {
AppRoomDatabase db = DatabaseCopier.getInstance(context).getRoomDatabase(); //get db from existing file
mNotificationDao = db.mNotificationDao();
suggestionDao = db.suggestionDao();
}
public void startGetNextNotificationAsync(FetchNextNotifAsyncTask.AsyncResponse delegate) {
new FetchNextNotifAsyncTask(delegate, mNotificationDao).execute();
}
public void setNewNextNotification(int id) {
new UpdateNextNotifAsyncTask(mNotificationDao).execute(id);
}
public void revealSuggestion(int id) {
new UpdateIsRevealedAsyncTask(suggestionDao).execute(id);
}
public void revealPremiumSuggestions() {
new RevealPremiumAsyncTask(suggestionDao).execute();
}
//=== Inner AsyncTask class to run updates in the background ===
public static class FetchNextNotifAsyncTask extends AsyncTask<Void, Void, MessageNotification> {
//Interface delegate to notify Activity when done
public interface AsyncResponse {
void processFinish(MessageNotification notification);
}
private AsyncResponse delegate;
private MNotificationDao mAsyncTaskDao;
FetchNextNotifAsyncTask(AsyncResponse delegate, MNotificationDao dao) {
this.delegate = delegate;
mAsyncTaskDao = dao;
}
@Override
protected MessageNotification doInBackground(Void... voids) {
return mAsyncTaskDao.getNextNotification();
}
@Override
protected void onPostExecute(MessageNotification notification) {
delegate.processFinish(notification);
}
}
...
I've found related questions, but they cover other specific aspects like dialog dismissal or ClickListener:
IllegalStateException on AsyncTask
IllegalStateException asynctask - onPostExecute
Play Store Crash Report: IllegalStateException on android.view.View$DeclaredOnClickListener.onClick
As stated on the Android Dev page:
Throws IllegalStateException
If getStatus() returns either AsyncTask.Status#RUNNING or AsyncTask.Status#FINISHED.
So that seems to be the cause? Or maybe the interface delegate?