【问题标题】:Stange onPause / onResume behaviour斯坦奇 onPause / onResume 行为
【发布时间】:2013-12-07 03:02:27
【问题描述】:

我在我的应用中看到 onPause / onResume 出现一些奇怪的行为,无法弄清楚发生了什么。

我在onResume 中执行一个数据库查询(AsyncTask 的简单子类),如果它仍在执行,则在onPause 中取消它。我收到了一个崩溃报告,让我想知道任务取消是否有效,因此添加了一个分析事件来记录 onPostExecuteonPause 取消任务后被调用。

在过去的一个月里,我看到了 140 次此类事件,页面浏览量达到了 4,100 次。

@Override
protected void onResume() {
    super.onResume();
    mIsResumed = true;
    if (mReverseCardsTask == null) {
        mReverseCardsTask = new TcgCursorTask(this) {
            @Override
            protected Cursor doInBackground(Void... params) {
                return mDb.reverseFetchCards();
            }
            @Override
            protected void onPostExecute(Cursor cursor) {
                if (mIsResumed) {
                    onReverseCardsCursor(cursor);
                } else {
                    EasyTracker.getTracker().sendEvent("error", "on-post-execute", "called after paused", 1L);
                }
            }
        };
        mReverseCardsTask.execute();
    }
}

@Override
protected void onPause() {
    super.onPause();
    mIsResumed = false;
    if (mReverseCardsTask != null) {
        mReverseCardsTask.cancel(false);
        mReverseCardsTask = null;
    }
}

我有一种感觉,我在这里遗漏了一些非常简单的东西,但看不到它。

我刚刚注意到我没有在onPostExecute 中清除mReverseCardsTask,但这应该没关系。

【问题讨论】:

  • 你能发布堆栈跟踪吗?这会很有帮助。
  • 我没有堆栈跟踪。这发生在我发布的应用程序中。我从来没有亲眼看到它发生过,我只能看到我放入代码中的分析事件来证实我的怀疑。
  • 啊啊啊。我刚刚查看了 Gingerbread 的 AsyncTask 代码。它有一个错误,即在处理来自后台线程的MESSAGE_POST_RESULT 消息之前,它不会检查任务是否已被取消。这真的烦人。这意味着存在使cancel() 语义更加复杂的竞争条件。

标签: android activity-lifecycle


【解决方案1】:

只是调用 cancel() 不会做任何事情。您实际上必须在流程中进行检查以确定是否要取消它并执行取消它的工作。

操作系统不知道在停止之前您可能需要做什么来清理(例如关闭文件或打开网络连接)。

【讨论】:

  • github.com/android/platform_frameworks_base/blob/master/core/… 根据上面的代码,调用cancel() 应该会阻止AsyncTask 在收到来自线程的MESSAGE_POST_RESULT 消息时调用onPostExecute()
  • 嗯,确实是这么说的。但是我对 AsyncTaskLoader 也有同样的问题。阻止它抛出各种异常的唯一方法是让 onStopLoading() 方法也在 SQLiteDatabaseHelper 中设置一个标志,这样我就可以在必须将项目添加到数据库的各种循环中检查它。如果没有任何检查,它永远不会停止线程,并且在我下次启动它时它仍然会运行,从而导致 SQLite 异常。
  • 可能是并发问题。在某些设备上,在 UI 线程上设置该标志的速度可能不足以让后台线程在调用 onPostExecute() 之前看到它。众所周知,AsyncTask 存在问题和错误。
  • 我可能误解了Handler 的工作原理,但我认为重点在于handleMessage() 方法在GUI 线程上执行。这意味着我的取消任务的onPause() 代码和调用onCancelled()onPostExecute() 的代码之间不会存在竞争。
  • 我在 AsyncTask 上遇到了另一个问题,我把它贴在这里:stackoverflow.com/questions/19994187/…
【解决方案2】:

好的。我已经解决了。我不确定它是在哪个 API 版本中修复的,但如果您查看 Gingerbread 的代码,cancel() 处理中就会出现明显的竞争条件。处理来自后台的MESSAGE_POST_RESULT 消息的GUI 线程代码调用onPostExecute(),无论任务是否被取消。

事实证明,修复非常简单。我需要做的就是在执行我的onPostExecute() 逻辑之前添加我自己对isCancelled() 的检查。

Gingerbread code 接收MESSAGE_POST_RESULT 并调用finish()。然后finish() 调用onPostExecute()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多