处理屏幕旋转的最佳实践:Android

34
我正在学习如何使用线程和AsyncTask处理屏幕方向变化的各种做法。我发现以下解决方案:
  1. 附加-分离模式:将活动附加到线程和AsyncTask中并保留其实例,而不影响它们。(来源:12)

  2. 无UI片段方法:使用非UI/无UI片段执行所有与线程相关的操作,并在配置更改时保留其实例。(来源:12)

还有其他处理这种情况的方法吗?推荐使用哪种做法?我问这个问题是因为我在Android文档中找不到通用的解决方案。

你不会找到比@vogella或CommonsWare提供的更好的东西。他们所说的几乎是通用的Android解决方案。 - Carlos Robles
6个回答

43

一些总结

上述提到的几种方法都是良好的实践方法,但我认为可以通过简短的解释来概括它们。以下是目前用于http网络、异步工作/线程和缓存的最流行的库。

我的当前项目(只是个人偏好)

我个人目前正在使用OttoLoadersVolleyOrmlite以及基于ApacheService的网络栈。我希望在某个时候能够用VolleyRetrofit或者最终用Robospice替换网络栈。

我个人非常喜欢Otto和Volley。


RoboSpice(模块化)

Retrofit(REST)

Volley (网络数据和图像)

Picasso (图像)

Loaders (Android)

  • 得到很好的支持
  • 通过方向更改和保存/加载片段状态进行持久化
  • 可能很难正确使用
  • 没有缓存

AsyncTask (Android)

  • 从UI线程进行后台工作的简单方法
  • 必须取消并小心处理在活动或片段被拆除后返回的任务。

Otto (事件总线)

  • 非常强大的@Produce功能可以保留最后一个事件,并能够按需为总线上任何新的感兴趣的订阅者生成它
  • 无头片段(?)

    • 我个人从未见过除了Vogella的教程之外有人使用这种方法,所以我对此不确定。

    服务(Android)

    • 老派的方式
    • 终极控制权,你必须自己做所有的事情
    • 通常与Appache或HURL客户端一起使用,并通过意图传递包裹

    7

    为什么不尝试使用加载器,特别是AsyncTaskLoader?它们可用于在支持库中的pre-Honeycomb版本,并完美匹配Activity/Fragment生命周期。以下是官方摘要:

    • 它们可用于每个Activity和Fragment。
    • 它们提供数据的异步加载。
    • 它们监视其数据源并在内容更改时提供新结果。
    • 它们在配置更改后重新创建时自动重新连接到上一个加载器的光标。因此,它们不需要重新查询其数据。

    4
    我们实际上正在使用RoboSpice库。它在Service上运行,只提供RequestListeners对象。
    你第一种方法的问题(保留AsyncTask之间的引用)是,当AsyncTasks持有Activities的引用时,可能会导致内存泄漏。通过检查堆大小并反复旋转相同的Activity来监视应用程序,这一点需要注意。您的堆应该在正常参数范围内增长(当您必须回收的对象与新对象同时存在时),但是当GC运行时,RAM分配应该降至与开始时分配的大小相同。
    因此,如果我要推荐一些东西,那就是:
    管理API调用和流程的Activity(使用RoboSpice,让UI旋转) 在Fragments中使用retainInstance = true的简单屏幕。这使您可以直接将DTO传递给片段,并且只需在顶级Activity中管理状态。

    3
    如果处理asyncTask是您的主要关注点,即不希望每次更改方向时下载数据,则可以尝试以下方式 - (1)在oncreate之前初始化任何值,如下所示...
    Boolean android_hacker = false;
    

    (2) 现在当您使用AsyncTask类下载数据完成后,请将该值设置为true

    android_hacker = true;
    

    在这里,使用模型和数组适配器类来维护所有数据。

    (3) 现在每次更改方向时,请按照以下方式检查

    if( android_hacker = true ){
    
    // Use your saved instance ..
    
    }else{
    
    // Download data as it is yet not downloaded ..
    
    }
    

    希望能帮到您..

    1
    我正在寻找一个框架或一种好的实践方法,但我并不认为这是它们中的任何一个。不过还是谢谢你的回复! - Gaurav Arora
    我在片段中遇到了问题,每次下载数据时都会出现。更改方向时也存在相同的问题。我仅使用这种逻辑解决了这个问题。 - AndroidHacker

    2

    除了AsyncTask之外,还有很多尝试的方法。如果您想找到最佳实践,AsyncTask不是一个好选择。 这个答案 解释了为什么不应该使用AsyncTask,并建议您使用一种更好的方式来处理长时间运行的任务,RoboSpice
    我已经使用过这个库,我认为它值得一试:尊重活动的生命周期(方向更改),没有内存泄漏,支持多线程,缓存结果... 它可以通过使用缓存插入和拔出长请求任务(但对于非缓存请求效果不佳)。

    但我推荐一个来自Google的好方法:IntentServiceBroadcastReceiver。您可以在方向更改期间注册和取消注册广播以接收数据结果。所有后台任务将在IntentService中工作,并通过BroadcastReceiver通知您想要的活动。有很多示例供您尝试。类似于这样:http://mobile.tutsplus.com/tutorials/android/android-fundamentals-intentservice-basics/ 更新:
    引用: 嗨R4j,问题是我的应用程序非常复杂。而且我必须进行许多并行网络调用。您使用IntentService的方法很好,但不适合复杂的情况
    我不认为这是个问题。您可以使用IntentService执行任何任务,甚至是复杂的任务。如果您想要并行任务,可以考虑在其中具有多线程的服务,并通过Intent与活动通信。在服务和活动之间发送意图是安全和灵活的,这是Android的方式。
    如果您想要缓存(通过文件下载、流、数据库等),那么RoboSpice是您的最佳选择。

    嗨R4j,问题在于我的应用程序非常复杂。我必须进行大量并行网络调用。你使用IntentService的方法很好,但不适合复杂的情况。 - Gaurav Arora
    你可以使用常规的Service处理多个线程,IntentService是一种专门为你创建了单个后台工作线程的特殊Service - pjco
    @pjco 是的,我知道。但是你也可以在多线程中使用 IntentService(例如在 onHandleIntent 中创建新线程),以接收和执行从活动发送的许多任务。 - ductran
    不,你不能(或者不应该)。在IntentService中创建线程不是一个好主意。onHandleIntent已经从IntentService创建的工作线程中调用。onHandleIntent创建了一个顺序阻塞工作队列。如果你需要两个后台工作线程,使用IntentService将创建3个线程,并使你处于不稳定的状态,无法确定这些线程何时存活。IntentService非常好用,但是将其用于启动线程会超出其预期功能范围。如果你需要这种并发性,应该使用带有ThreadPoolExecutor的常规服务。 - pjco
    1
    谢谢,我明白了,并且已经更新了我的答案。实际上,我更喜欢Service而不是IntentService,因为它更加灵活。我不喜欢IntentService内部的工作线程队列,因为我无法控制同时执行的任务数量。 - ductran

    -3
    您可以尝试以下方法:
    1)如果您的应用程序不明确要求任何方向更改,则在应用程序执行开始时禁用方向更改,从而避免任何与方向更改有关的崩溃或相关问题。
    您可以在布局xml文件的最外层布局中使用以下行来实现此操作:
      android:orientation="vertical"
    

    (用于设置垂直方向)

    2)您可以使用Asynctask在线程执行开始时设置或保留以前的方向值,如下所示(仅为语法示例):

      setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    

      getResources().getConfiguration().orientation
    

    为什么要点踩?这两种方法在我编程时帮助了我解决问题。如果有更好的方法,请告诉我。 - SoulRayder
    3
    通过锁定方向,你可以停止Android的正常行为。用户可能想要改变方向以获得更舒适的用户界面(即使在执行任务期间)。而你的回答并没有真正涉及到gauravsapiens所想要的方向更改。附注:我没有进行反对投票。 - ductran
    4
    你忽略了平板电脑通常以横屏模式使用的事实。 - radley

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接