最佳实践:屏幕方向改变时使用AsyncTask

8 浏览
0 Comments

最佳实践:屏幕方向改变时使用AsyncTask

AsyncTask是在另一个线程中运行复杂任务的好方式。\n但是,当AsyncTask仍在运行时发生屏幕方向或其他配置更改时,当前的Activity将被销毁并重新启动。由于AsyncTask实例与该活动相连,它会失败并导致“强制关闭”消息窗口。\n因此,我正在寻找一种“最佳实践”来避免这些错误并防止AsyncTask失败。\n我目前看到的方法有:\n

    \n

  • 禁用屏幕方向更改(当然不是正确的处理方式)
  • \n

  • 让任务继续存在,并通过onRetainNonConfigurationInstance方法将其与新的活动实例进行更新
  • \n

  • Activity被销毁时取消任务,并在Activity重新创建时重新启动任务
  • \n

  • 将任务绑定到应用程序类而不是活动实例
  • \n

  • 在“shelves”项目中使用的一些方法(通过onRestoreInstanceState)
  • \n

\n一些代码示例:\nAndroid屏幕旋转期间的AsyncTasks,第一部分第二部分\nShelvesActivity.java\n你能帮助我找到解决问题最佳且易于实现的方法吗?代码本身也很重要,因为我不知道如何正确解决这个问题。

0
0 Comments

在处理屏幕旋转等运行时配置更改时,使用AsyncTask的最佳实践是使用“保留的Fragment”。这是因为AsyncTask在处理长时间运行的任务时会有一些问题,同时Android官方文档也建议使用其他API来处理长时间运行的任务。

在解决这个问题的过程中,首先需要创建一个基本的AsyncTask内部类,用于加载网络图片。然后,需要创建一个继承自Fragment的保留的Fragment内部类,并在onCreate事件中设置setRetainInstance(true)来保留数据。在外部的Activity类的onCreate()方法中处理保留的Fragment:如果它已经存在,则引用它并获取数据来设置UI;如果它不存在,则创建并添加它。最后,通过UI界面触发AsyncTask来加载图片。

另外,还可以通过添加一个确定性进度条来提供进度更新。具体操作包括在UI布局中添加进度条,获取它的引用,在AsyncTask的doInBackground()方法中定期更新进度,并在onProgressUpdate()方法中更新UI。此外,还需要将AsyncTask的第二个泛型参数从Void更改为可以处理进度更新的类型(例如Integer),并在doInBackground()方法中使用publishProgress()方法来发布进度。

通过使用保留的Fragment来处理AsyncTask的运行时配置更改,同时添加确定性进度条来提供进度更新,可以实现最佳实践。以下是完整的代码和布局示例。

0
0 Comments

在进行屏幕方向切换时,AsyncTask可能会出现问题。一种解决方法是在AsyncTask的.onPostExecute()回调中使用广播Intent,这样它们就不会直接修改启动它们的Activity。Activity通过动态的BroadcastReceiver监听这些广播,并根据需要进行处理。这样一来,AsyncTask就不需要关心处理其结果的具体Activity实例。它们在完成时只需“喊出来”,如果有一个Activity正在运行(活跃和焦点状态 / 在其恢复状态),并且对任务结果感兴趣,则会进行处理。

这种方法会增加一些开销,因为运行时需要处理广播,但我通常不介意。我认为使用LocalBroadcastManager而不是默认的系统广播可以加快速度。

这可能是解决方案的一部分,但似乎不能解决在屏幕方向切换后重新创建AsyncTask的问题。

那么,如果在广播期间不幸既没有一个Activity存在呢?(即在旋转过程中)

0
0 Comments

Best practice: AsyncTask 在屏幕旋转时的处理方法

在处理屏幕旋转时,不要使用android:configChanges这个方法,这是非常不好的做法。

也不要使用Activity#onRetainNonConfigurationInstance()方法,这个方法对于基于Fragment的应用来说不是很合适,也不够模块化。

你可以阅读我写的一篇文章,描述了如何使用保留的Fragment来处理配置变化的问题。它很好地解决了在旋转屏幕时保留AsyncTask的问题。你只需要将AsyncTask放在一个Fragment中,调用setRetainInstance(true)方法,然后通过保留的FragmentAsyncTask的进展/结果报告给它的Activity

很好的想法,但并不是每个人都使用Fragment。在Fragment可用之前,有很多传统的代码。

Fragment从Android 1.6版本开始就可以通过支持库使用了。你能举个仍在使用的传统代码的例子,这些代码在使用支持库的Fragment时会有困难吗?因为我真的觉得这不是问题。

如果你的传统代码使用TabActivity作为根Activity,那就是个问题了。首先,TabActivity不能包含Fragment,其次,即使子Fragment继承自FragmentActivity,你仍然不能使用它,因为它不是顶级Activity。

我觉得我不需要在我的回答中解决这些问题,因为99.9%的人已经不再使用TabActivity了。说实话,我不知道我们为什么还在讨论这个问题...每个人都同意Fragment是正确的做法。:)

如果AsyncTask必须从嵌套的Fragment中调用怎么办?

如果你阅读了我的文章,里面有一个链接到一个包含示例代码的Github仓库。那个示例代码是否回答了你的问题?我不太确定你所说的“嵌套”Fragment是什么意思。

请问你能帮助我吗?

你知道AsyncTask是否可能在不恰当的时刻返回(在旋转过程中,当任何一个Activity对象都不存在)?

我认为你问的问题在这里讨论过:stackoverflow.com/q/19964180/844882

我刚刚遇到了一个问题,使用这种方式与对话框一起使用时会出问题——不幸的是在onDestroy中移除应用的回调函数可能太晚了。我找到了一个在你的文章中发表了同样担忧的人的评论:androiddesignpatterns.com/2013/04/…

在异步回调(如onPostExecute())中提交事务是一个完全不同的问题。:)我已经回复了你链接中的评论,希望能有所帮助。如果你还没有看到,我还写了一篇关于onSaveInstanceState/activity state loss/FragmentTransactions问题的文章:here

谢谢Alex。这确实只关于是否提交了Fragment事务,还是关于编辑UI的更广泛的问题?onSaveInstanceState保存了UI的所有信息...如果我将对话框替换为一些显示"正在加载..."的文本,我会遇到同样的问题吗?我在你链接的文章中找到了你对PauseHandler的评论。似乎这可能是真正的解决方法——如果"IsChangingConfiguration"为true,则在onSaveInstanceState中将"paused"设置为true。我必须说,这感觉就像Android框架中的一些不完美之处...

注意,我还试图在保留的Fragment的onSaveInstanceState方法中取消与任务的连接,但不幸的是,这个方法在Activity的onSaveInstanceState之后调用,所以无法在保留的Fragment中隐藏这种行为。(在这个设计中,taskCancelled的回调返回到Activity中,并导致Activity的状态更改)

也许你应该在Stack Overflow上提一个新的问题,具体说明你遇到的问题,因为这个评论线程已经变得相当冗长了。如果你创建了一个问题,请留下链接,我可以看一下。

完成了:stackoverflow.com/questions/28683276/…

- “每个人都同意Fragment是正确的做法。”Squared团队的开发人员会持不同意见的!

Squared的文章主要讨论了他们更喜欢使用自定义视图来处理多窗格布局和返回栈管理等问题。他们并没有真正讨论是否推荐使用Fragment来在配置变化时保留对象,这才是这个问题在问的...所以我想我们永远不会知道。:)

使用Stack Overflow通过伪装链接来垃圾邮件式地宣传自己的产品,有点不太光彩,不是吗?

我在这个回答中看到了很多缺陷和漏洞。例如,当你在不同屏幕尺寸、旋转和框架版本下有不同的布局时,你怎么“重新加载”Fragment的布局?setRetainInstance(true)改变了Fragment的生命周期,不会调用onCreate方法。而且,不是每个人都一直使用Fragment...

好的文章。至于被弃用的函数“onRetainNonConfigurationInstance()”,还有android.support.v4.app.FragmentActivity中的“onRetainCustomNonConfigurationInstance()”和“getLastCustomNonConfigurationInstance()”。它们也可以使用。

我现在正在使用这种方法,目前一切都很好。谢谢并点赞。

问题在于你不能在AsyncTask类中使用UI ProgressBar,因为在你的示例中它被标记为static。我需要在onPreExecuteonPostExecute中隐藏/显示(隐藏按钮,显示进度)一些UI。目前,我不得不将UI组件传递给AsyncTask的构造函数。

在最糟糕的情况下,状态丢失意味着什么?你能给出一些关于这个问题的想法吗?

0