zoukankan      html  css  js  c++  java
  • AsyncTask的doInBackground不工作原因分析

    转自http://blog.csdn.net/mddy2001/article/details/17127065

    故 事背景:一个HttpURLConnection的请求没有发送出去,导致failhandler、errorhandler、 timeoutHandler没有回调,发现是AsyncTask没有调用doInBackground方法,查找文章,找到解决方法,线程多的话会出现 阻塞,貌似只能有4、5个线程,解决方法是不能调用excute方法,调用task.executeOnExecutor(Executors.newCachedThreadPool());即可。以下是原文,重点已用红色标注。

    深入解析AsyncTask(doInBackground不工作)   

    |字号 订阅

     
    近日开发遇到AsyncTask的doInBackground()方法不执行的问题,所以在网上查找原因,以下博文解决了我的问题,我用Thread代替了AysncTask进行工作。博文如下:

    地址:http://blog.csdn.net/hitlion2008/article/details/7983449

    AsyncTask引发的一个问题

    上周遇到了一个极其诡异的问题,一个小功能从网络上下 载一个图片,然后放到ImageView中,是用AsyncTask来实现的,本身逻辑也很简单,仅是在doInBackground中用HTTP请求把 图片的输入流取出,然后用BitmapFactory去解析,然后再把得到的Bitmap放到ImageView中。这个应用是用4.0的SDK开发的, 也是运行在4.0上面的。但是有时候下载这张图片去要用很久很久,甚至要等上几分钟。通过调试发现一个令人难以接受的事实:竟然是 doInBackground()未及时执行,也就是它并没有在#execute()调用之后马上执行,而是等待了很久才得以执行。

    神马情况,难道AsyncTask不是线程,难道不是异步,难道AsyncTask另有内幕?

    AsyncTask的内幕

    AsyncTask主要有二个部分:一个是与主线各的 交互,另一个就是线程的管理调度。虽然可能多个AsyncTask的子类的实例,但是AsyncTask的内部Handler和 ThreadPoolExecutor都是进程范围内共享的,其都是static的,也即属于类的,类的属性的作用范围是CLASSPATH,因为一个进 程一个VM,所以是AsyncTask控制着进程范围内所有的子类实例。

    与主线程交互

    与主线程交互是通过Handler来进行的,因为本文主要探讨AsyncTask在任务调度方面的,所以对于这部分不做细致介绍,感兴趣的朋友可以去看AsyncTask的源码

     

    线程任务的调度

    内部会创建一个进程作用域的线程池来管理要运行的任 务,也就就是说当你调用了AsyncTask#execute()后,AsyncTask会把任务交给线程池,由线程池来管理创建Thread和运行 Therad。对于内部的线程池不同版本的Android的实现方式是不一样的:

    Android2.3以前的版本,也即SDK/API 10和以前的版本

    内部的线程池限制是5个,也就是说同时只能有5个线程 运行,超过的线程只能等待,等待前面的线程某个执行完了才被调度和运行。换句话说,如果一个进程中的AsyncTask实例个数超过5个,那么假如前5个 都运行很长时间的话,那么第6个只能等待机会了。这是AsyncTask的一个限制,而且对于2.3以前的版本无法解决。如果你的应用需要大量的后台线程 去执行任务,那么你只能放弃使用AsyncTask,自己创建线程池来管理Thread,或者干脆不用线程池直接使用Thread也无妨。不得不说,虽然 AsyncTask较Thread使用起来比较方便,但是它最多只能同时运行5个线程,这也大大局限了它的实力,你必须要小心的设计你的应用,错开使用 AsyncTask的时间,尽力做到分时,或者保证数量不会大于5个,否则就可能遇到上面提到的问题。要不然就只能使用JavaSE中的API了。


    Android 3.0以后,也即SDK/API 11和以后的版本

    可能是Google意识到了AsyncTask的局限性了,从Android 3.0开始对AsyncTask的API做出了一些调整:

    1. #execute()提交的任务,按先后顺序每次只运行一个

      也就是说它是按提交的次序,每次只启动一个线程执行一个任务,完成之后再执行第二个任务,也就是相当于只有一个后台线程在执行所提交的任务(Executors.newSingleThreadPool())。


    2. 新增了接口#executeOnExecutor()

      这 个接口允许开发者提供自定义的线程池来运行和调度Thread,如果你想让所有的任务都能并发同时运行,那就创建一个没有限制的线程池 (Executors.newCachedThreadPool()),并提供给AsyncTask。这样这个AsyncTask实例就有了自己的线程池 而不必使用AsyncTask默认的。

    3. 新增了二个预定义的线程池SERIAL_EXECUTORTHREAD_POOL_EXECUTOR

      其实THREAD_POOL_EXECUTOR并不是新增的,之前的就有,只不过之前(Android 2.3)它是AsyncTask私有的,未公开而已。THREAD_POOL_EXECUTOR是一个corePoolSize为5的线程池,也就是说最多只有5个线程同时运行,超过5个的就要等待。所以如果使用executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)就跟2.3版本的AsyncTask.execute()效果是一样的。


      SERIAL_EXECUTOR是 新增的,它的作用是保证任务执行的顺序,也就是它可以保证提交的任务确实是按照先后顺序执行的。它的内部有一个队列用来保存所提交的任务,保证当前只运行 一个,这样就可以保证任务是完全按照顺序执行的,默认的execute()使用的就是这个,也就是 executeOnExecutor(AsyncTask.SERIAL_EXECUTOR)与execute()是一样的。

     

    前面问题的解法

    了解了AsyncTask的内幕就知道了前面问题的原 因:因为是4.0平台,所以所有的AsyncTask并不都会运行在单独的线程中,而是被SERIAL_EXECUTOR顺序的使用线程执行。因为应用中 可能还有其他地方使用AsyncTask,所以到网络取图片的AsyncTask也许会等待到其他任务都完成时才得以执行而不是调用executor() 之后马上执行。

    那么解决方法其实很简单,要么直接使用Thread, 要么创建一个单独的线程池(Executors.newCachedThreadPool())。或者最简单的解法就是使用 executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR),这样起码不用等到前面的都结束了再执行。

    AsyncTask的使用注意事项

    前面的文章曾建议使用AsyncTask而不是使用Thread,但是AsyncTask似乎又有它的限制,这就要根据具体的需求情况而选择合适的工具,No Silver Bullet。下面是一些建议:

    • 改善你的设计,少用异步处理

      线程的开销是非常大的,同时异步处理也容易出错,难调试,难维护,所以改善你的设计,尽可能的少用异步。对于一般性的数据库查询,少量的I/O操作是没有必要启动线程的。

    • 与主线程有交互时用AsyncTask,否则就用Thread

      AsyncTask被设计出来的目的就是为了满足Android的特殊需求:非主线程不能操作(UI)组件,所以AsyncTask扩展Thread增强了与主线程的交互的能力。如果你的应用没有与主线程交互,那么就直接使用Thread就好了。

    • 当有需要大量线程执行任务时,一定要创建线程池

      线程的开销是非常大的,特别是创建一个新线程,否则就 不必设计线程池之类的工具了。当需要大量线程执行任务时,一定要创建线程池,无论是使用AsyncTask还是Thread,因为使用AsyncTask 它内部的线程池有数量限制,可能无法满足需求;使用Thread更是要线程池来管理,避免虚拟机创建大量的线程。比如从网络上批量下载图片,你不想一个一 个的下,或者5个5个的下载,那么就创建一个CorePoolSize为10或者20的线程池,每次10个或者20个这样的下载,即满足了速度,又不至于 耗费无用的性能开销去无限制的创建线程。

    • 对于想要立即开始执行的异步任务,要么直接使用Thread,要么单独创建线程池提供给AsyncTask

      默认的AsyncTask不一定会立即执行你的任务,除非你提供给他一个单独的线程池。如果不与主线程交互,直接创建一个Thread就可以了,虽然创建线程开销比较大,但如果这不是批量操作就没有问题。

    • Android的开发没有想像中那样简单,要多花心思和时间在代码上和测试上面,以确信程序是优质的

    附上相关资源:

    使用自定义的CorePoolSize为7的Executor(Executors.newFixedThreadPool(7)):


    使用未设限制的Executor(Executors.newCachedThreadPool()):


    这些例子所用的代码:

     

    1. publicclassextends privatestaticint0 privatestaticfinalint9 privatestatic privatestatic privatestatic static 7 @Override publicvoid super "AsyncTask of API " final new privateclassextends private private privateint publicint new @Override publicint return @Override publicint return @Override publiclongint return @Override publicint ifnull null new /* 
    2.                  * It only supports five tasks at most. More tasks will be scheduled only after 
    3.                  * first five finish. In all, the pool size of AsyncTask is 5, at any time it only 
    4.                  * has 5 threads running. 
    5.                  */ //                task.execute(); // use AsyncTask#SERIAL_EXECUTOR is the same to #execute(); //                task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); // use AsyncTask#THREAD_POOL_EXECUTOR is the same to older version #execute() (less than API 11) // but different from newer version of #execute() //                task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); // one by one, same to newer version of #execute() //                task.executeOnExecutor(SINGLE_TASK_EXECUTOR); // execute tasks at some limit which can be customized //                task.executeOnExecutor(LIMITED_TASK_EXECUTOR); // no limit to thread pool size, all tasks run simultaneously return privateclassextends private private public "Task #" @Override protected int1 while101 100 returnnull @Override protectedvoid @Override protectedvoid @Override protectedvoid 0 classextends private private public super public super publicvoid ifnull publicvoidint ifnull }  

    1. <? = =?> < =  
    2. android:layout_width"match_parent" android:layout_height"match_parent" android:paddingLeft"10dip" android:paddingRight"10dip" android:orientation"vertical"> < =  
    3. android:layout_width"fill_parent" android:layout_height"wrap_content" android:divider"#cccccc" android:dividerHeight"0.6dip" android:footerDividersEnabled"true" android:headerDividersEnabled"true"/> </>  

    1. <? = =?> < =  
    2. android:layout_width"match_parent" android:layout_height"50dip" android:gravity"center_vertical" android:layout_gravity"center_vertical" android:orientation"horizontal"> < =  
    3. android:layout_width"wrap_content" android:layout_height"wrap_content" android:textColor"#ffff00" android:textSize"26sp"/> < =  
    4. android:layout_width"fill_parent" android:layout_height"15dip" android:max"100" style"@android:style/Widget.ProgressBar.Horizontal"/> </   
  • 相关阅读:
    Linux编程 22 shell编程(输出和输入重定向,管道,数学运算命令,退出脚本状态码)
    mysql 开发进阶篇系列 46 物理备份与恢复( xtrabackup的 选项说明,增加备份用户,完全备份案例)
    mysql 开发进阶篇系列 45 物理备份与恢复(xtrabackup 安装,用户权限,配置)
    mysql 开发进阶篇系列 44 物理备份与恢复( 热备份xtrabackup 工具介绍)
    Linux编程 21 shell编程(环境变量,用户变量,命令替换)
    Linux编程 20 shell编程(shell脚本创建,echo显示信息)
    mysql 开发进阶篇系列 43 逻辑备份与恢复(mysqldump 的基于时间和位置的不完全恢复)
    Linux编程 19 编辑器(vim 用法)
    (网页)angularjs中的interval定时执行功能(转)
    (网页)在SQL Server中为什么不建议使用Not In子查询(转)
  • 原文地址:https://www.cnblogs.com/seemann/p/4485652.html
Copyright © 2011-2022 走看看