【问题标题】:Android Screen Rotation firing multiple AsyncTask ThreadsAndroid 屏幕旋转触发多个 AsyncTask 线程
【发布时间】:2012-05-05 00:08:51
【问题描述】:

在 Activity 中,我正在创建一个处理程序,以每 45 秒触发一次 AsyncTask,以刷新 ListView 的 DataAdapter 的内容。 AsyncTask 工作得很好,并通过 ProgressUpdates 和 Toast 消息让用户了解进度。

由于线程的 doInBackground 是触发后忘记且不可重复使用的,因此我必须从我的 Hander 创建一个 AsyncTask 的新实例,该实例每 45 秒触发一次。问题是当屏幕旋转时,我收到并发消息,因为重新创建了 Hander 并创建了 AsyncTask 的新实例,因此通过 ProgressUpdates 和 Toast 消息的友好用户进度是压倒性的,并且使得使用 ListView 变得困难。

请不要将此作为解决方案:android:screenOrientation="portrait" 不是一个选项。

对于必须如此频繁运行的东西,我应该只使用自定义线程而不是 AsyncTask 类吗? ToDo:未显示,我必须稍后从传感器的 onSensorChanged 事件更新适配器以更新 ListView 中每个位置的方位,我打算在单独的 AsyncTask 类上运行它,因为我不需要每次都通知用户设备方位发生了变化。

既然 AsyncThread 不能被重用,我做错了吗?简而言之,让 Activity 刷新 ListView 并在这样做时远离 UI 线程的最佳方法是什么?

【问题讨论】:

  • 您能告诉我们您的活动的 onCreate() 和 onStart() 方法吗?

标签: android android-listview android-asynctask


【解决方案1】:

问题是当屏幕旋转时,我收到并发消息,因为重新创建了 Hander 并创建了 AsyncTask 的新实例。

引用 API Activity - Configuration Changes 的原因:

除非您另外指定,否则配置更改(例如屏幕方向、语言、输入设备等的更改)将导致您当前的活动被破坏,并通过 onPause() 的正常活动生命周期过程,onStop( ) 和 onDestroy() (视情况而定)。

因此,每个对象都有一个活动范围的生命周期(即在您的活动类中定义的 Handler、AsyncTask 等),因此该活动重新创建会受到影响。但是,您可以绕过此活动重新创建,如后面的“活动 - 配置更改”部分所述:

在某些特殊情况下,您可能希望根据一种或多种类型的配置更改绕过重新启动活动。这是通过清单中的 android:configChanges 属性完成的。对于您说在那里处理的任何类型的配置更改,您将收到对当前活动的 onConfigurationChanged(Configuration) 方法的调用,而不是重新启动。但是,如果配置更改涉及您未处理的任何内容,则该活动仍将重新启动,并且不会调用 onConfigurationChanged(Configuration)。

与主题无关,但作为一个好习惯,您应该始终在活动即将结束时正确销毁已使用的对象(Handler、AsyncTask 等)(即在 onDestroy() 方法中)。

对于必须如此频繁运行的东西,我应该只使用自定义线程而不是 AsyncTask 类吗?

AsyncTask 非常方便,但不适合周期性任务,在这种情况下我会使用 ScheduledExecutorService 或 TimerTask,查看my answer here 获取示例代码。

【讨论】:

  • 感谢 York,在进一步阅读之后,我没有使用最好的线程模型来完成这项任务,并且发现 Timer 将更适合我想要完成的任务。这是 AsyncTask 的主要参考来源:codeproject.com/Articles/162201/…
  • 我还发现这对于在 TimerTask 或 ScheduledExecutorService 之间做出决定很有用:stackoverflow.com/questions/409932/…
【解决方案2】:

您能发布一些您的代码吗?了解您的问题出在哪里可能会很有用。

正如 york 所指出的,您可能应该使用TimerTask。似乎它更适合您正在尝试做的事情。

如果是创建问题的处理程序的新实例,您可以尝试这样的事情:

private Handler mHandler = null;

@Override
public void onCreate(Bundle _savedInstanceState) {
    super.onCreate(_savedInstanceState);
    setContentView(R.layout.my_layout);

    if (mHandler == null) {
        // TODO create your handler here
    }
}

编辑: 您也可以测试 _savedInstanceState == null。
_savedInstanceState 用于保存活动的状态,因此转动手机不再是问题。 但是,如果您离开活动然后返回到它,它将创建一个新的处理程序(除非您将其实例化为静态变量)。

【讨论】:

  • 是的,我同意约克的观点,我将使用定时器来代替。顺便说一句,我确实尝试过您的建议,在创建 Handler 的新实例之前检查 mHandler 是否为空,该实例会创建 Runnable 的新实例......但我仍然遇到启动多个 AsynTask 线程的问题,而且我很容易崩溃通过来回翻转手机应用程序:
  • 致命异常:com.echo5.appname.ActivityStationsList.onTaskComplete(ActivityStationsList.java:152) 处的主要 java.lang.NullPointerException com.echo5.appname.THREADS.AsyncTaskManager.onComplete(AsyncTaskManager.java :61) 在 com.echo5.appname.ActivityStationsList$UpdateStationTask.onPostExecute(ActivityStationsList.java:573) 在 com.echo5.appname.ActivityStationsList$UpdateStationTask.onPostExecute(ActivityStationsList.java:1) 在 android.os.AsyncTask.finish( AsyncTask.java:417) 在 android.os.AsyncTask.access$300(AsyncTask.java:127)
  • 在 android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:429) 在 android.os.Handler.dispatchMessage(Handler.java:99) 在 android.os.Looper.loop(Looper .java:130) 在 android.app.ActivityThread.main(ActivityThread.java:3687) 在 java.lang.reflect.Method.invokeNative(Native Method) 在 java.lang.reflect.Method.invoke(Method.java:507 ) 在 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:842) 在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600) 在 dalvik.system.NativeStart.main(本机方法)
  • 我编辑了我的帖子。如果您尝试对 _savedInstanceState 和处理程序进行一项或两项测试作为静态变量,请告诉我它是否适合您。
猜你喜欢
  • 1970-01-01
  • 2012-09-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-16
  • 2011-12-24
相关资源
最近更新 更多