• [翻译]The dark side of AsyncTask


      很好的一篇文章,非常适合初学者了解 AsyncTask,居然百度不出来。这篇博文是从俄语翻译成英语的,再从英语二次翻译过来。

      作者:+Fré Dumazy(?)

      原文链接:http://bon-app-etit.blogspot.jp/2013/04/the-dark-side-of-asynctask.html

      AsyncTask的黑暗面

      大家好,在这篇博文里我会向大家介绍一些 AsyncTask少有人知的黑暗面。半年前我第一次发文介绍 AsyncTask的用法。现在我要告诉你们这些用法可能会导致什么样的问题,以及如何解决这些问题。

      AsyncTask

      AsyncTask是在 Android1.5版本加入的,初衷是为了方便开发者管理线程。早在 Android1.0和1.1版本,AsyncTask也被称为 UserTask。你可以在你的工程源码里直接替换 AsyncTask为 UserTask以便支持低版本,但根据 Android官方统计,目前只有不到0.1%的设备运行在这些低版本上,你完全可以选择不支持低版本。

      现在使用 AsyncTask可能是最常用的 Android后台处理方法。它确实很容易上手,但这个类存在着一些容易被忽略的问题。

      LifeCycle

      这是 AsyncTask很容易被误解的一点。程序猿们可能认为当创建 AsyncTask的 Activity被销毁时,AsyncTask对象也会被销毁。但通常情况下,AsyncTask会继续执行它的 doInBackGround方法直到结束。AsyncTask的结束有两种情况:1. 外部调用了 cancel方法,会执行 onCancelled;2. 正常执行完成并调用 onPostExecute方法。

      假设 AsyncTask在 Activity销毁之前没有被取消,这可能会引起 AsyncTask的 crash,原因是 AsyncTask里所引用的 Activity中的 View等对象已经不存在了。所以我们需要确保 Activity销毁时调用了 AsyncTask的 cancel方法。cancel方法需要传入一个 boolean参数(mayInterruptIfRunning),来标识是否打断正在执行的 task。如果设置为 false,那么即使调用了 cancel方法,正在执行中的 AsyncTask也会执行完成。如果你的 doInBackGround方法中有循环语句的话,你需要在每次循环的时候都检查一下这个标志位。

      因此,我们应当确保使用正确的方法来取消 AsyncTask。

      

      Does cancel() really work?

      简而言之:有时候会。

      如果使用 cancel(false),正在执行中的 AsyncTask会一直执行直到完成,但这个方法可以阻止 onPostExecute方法被调用。因此很多情况下使用 cancel(false)是没有意义的(注:我们做 cancel操作不只是阻止 onPostExecute中的更新,也非常希望能立即停止 doInBackGround中那些开销较大的动作)。那你可能会想,无论如何都使用 cancel(true)不就可以了?错,如果 mayInterruptIfRunning标志位被置为 true,那只代表系统会尽可能早的结束 AsyncTask,如果在 doInBackGround中的操作是不可打断的,那 cancel(true)实际上也起不到立即结束 AsyncTask的功能。

      Memory leaks

      AsyncTask的 doInBackGround方法运行在后台线程,而其他一些方法运行在主线程,因此在 AsyncTask的生命周期内会始终保留一个对 Activity的引用,即便是 Activity已经被销毁,但在 AsyncTask中还是保留着引用。这可能会导致内存泄露。

      Losing your results

      另一个问题是随着 Activity的重启,我们可能会失去 AsyncTask的运行结果。例如横竖屏切换时,Activity被销毁然后重建,但 AsyncTask中存有的还是原来被销毁的那个 Activity的引用,所以 onPostExecute方法会不起作用。解决方法是,你可以在 Application等不会被销毁的对象中创建一个 AsyncTask。Activity.onRetainNonConfigurationInstance()和 Fragment.setRetainedInstance(true) 可能也会对这种情况有所帮助。

      Serial or parallel?

      很多人都对 AsyncTask是并行还是串行心存疑惑,原因是 AsyncTask的运行方式随着 Android版本的不同变更过几次。你可能会质疑我所说的“AsyncTask可能是并行或是串行”。假设你的方法中有如下两行代码:

    new AsyncTask1().execute();
    new AsyncTask2().execute();

      Task2会与 Task1同时开始吗?还是 Task2会在 Task1结束之后才开始运行?

      答案是:依赖于它们编译的 Android版本。

      在 Android1.6之前:

      第一版的 AsyncTask是串行的,意味着只有前一个 Task结束后下一个 Task才能开始运行。这可不是一种好的方式。

      Android1.6至 Android2.3:

      Android开发团队决定把 AsyncTask的运行方式改为并行,这样多个 AsyncTask可以同时运行在不同的后台线程中。这有一个问题,很多开发者已经习惯于串行的方式,这种改变会导致很多并发问题。

      Android3.0至今:

      “嘿嘿,他们习惯并发了?那我们改回去~!Let's Rock...” AsyncTask又被改为串行方式了。当然 Android开发团队也提供了并行的方式,就是使用 This is done by the method executeOnExecutor(Executor)方法,可以去 API文档中了解更多有关这个方法的信息。

      如果我们想自己控制运行方式,保证 AsyncTask始终并行,那可以用以下的代码(API level1~3不适用):

    public static void execute(AsyncTask as) {
     if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.HONEYCOMB_MR1) {
      as.execute();
     } else {
      as.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
     }
    }

      Do we need AsyncTask?

      并不是这样的。用少量代码实现后台运行实际上并不困难,但我们也知道要想不出错的话需要考虑的非常周全。另一种执行后台操作的方法是使用 Loaders。这个类在 Android3.0被引入,也可以通过使用 library导入。我可能今后还会在写一篇关于如何使用 Loaders的博文,所以多关注我的博客吧:) (最后一句是作者的原话 哈哈)

      

  • 相关阅读:
    Springboot 之 自定义配置文件及读取配置文件
    SQLSERVER系统视图 sql server系统表详细说明
    MySQL Workbench建表时 PK NN UQ BIN UN ZF AI 的含义
    使用Ecplise git commit时出现"There are no stages files"
    maven添加sqlserver的jdbc驱动包
    java将XML文档转换成json格式数据
    java将XML文档转换成json格式数据
    cannot be resolved. It is indirectly referenced from required .class files
    org.codehaus.jackson.map.JsonMappingException: Can not construct instance of java.util.Date from String value '2012-12-12 12:01:01': not a valid representation (error: Can not parse date "2012-12-
    @Autowired注解和静态方法 NoClassDefFoundError could not initialize class 静态类
  • 原文地址:https://www.cnblogs.com/haitong/p/3821704.html
Copyright © 2020-2023  润新知