【发布时间】:2020-06-02 08:55:03
【问题描述】:
报告中我的应用似乎有一个导致崩溃的主要原因:java.lang.IllegalStateException
我可以看到它来自哪里,它是在白天,当应用程序不活动时,在后台进程中从数据库内容构造 通知 的功能。有趣的是,这似乎只发生在最新的 Android 10 版本上:
您在非活动应用状态下处理数据库数据的方式是否发生了变化?
这是崩溃报告:
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)
至于代码,我有一个AlarmManager,它会在一天中的某个时间点触发一个监听器。在那个监听器中,我调用了一个AsyncTask,它在后台从数据库中检索数据,并在完成时调用一个接口方法来通知监听器:
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);
}
}
...
我找到了相关问题,但它们涵盖了其他特定方面,例如对话框解除或 ClickListener:
IllegalStateException on AsyncTask
IllegalStateException asynctask - onPostExecute
Play Store Crash Report: IllegalStateException on android.view.View$DeclaredOnClickListener.onClick
如 Android Dev 页面所述:
抛出 IllegalStateException
如果 getStatus() 返回 AsyncTask.Status#RUNNING 或 AsyncTask.Status#FINISHED。
所以这似乎是原因?或者接口委托?
【问题讨论】:
-
通常不鼓励直接从广播接收器类调用 AsyncTask(后台 API 调用)。您可以尝试调用一个服务,该服务反过来调用 api。你可以在这里阅读更多内容stackoverflow.com/questions/21743800/…
-
不建议启动 AsyncTask 并提供您的 BroadcastReceiver 作为回调,因为在 onReceive 方法完成之后,您的广播接收器类的执行将不再有效。尝试使用一个单独的类来执行此操作。我可以从您的代码中看到您不需要操作上下文,因此最好将其放在其他类中关于后台进程,尝试使用 java Executors 非常简单
-
@SridharS 谢谢你的解释。你能写一个更详细的答案(和一个例子)吗?当涉及到这样的后端时,我不是很专业,所以我会很感激一些起点。
-
@Big_Chair : 我不能在评论部分写太多,我将在这里给出一个关于 Executor Service 的要点,你可以从我的一个实际应用程序中找到如何在 android 中有效地使用它已在 Github 中分享 github.com/half-blood-prince/Contacts/blob/master/app/src/main/… Executors.newFixedThreadPool(1).execute(new Runnable() { public void run() { .... /* 在这里执行你的长时间运行操作 */ .... } });您可以使用 Executors 在后台执行任何任务,而不是 AsyncTask
标签: android push-notification android-asynctask illegalstateexception