AsyncTask源码分析

AsyncTask源码分析


谷歌从Android3.0开始,Android系统要求网络访问必须在子线程中进行,否则网络访问将会失败并抛出NetworkOnMainThreadException这个异常,这样做是为了避免主线程由于耗时操作所阻塞从而出现ANR现象。AsyncTask封装了线程池和Handler。AsyncTask有两个线程池:SerialExecutor和THREAD_POOL_EXECUTOR。前者是用于任务的排队,默认是串行的线程池:后者用于真正的执行任务。AsyncTask还有一个Handler,叫InternalHandler,用于将执行环境从线程池切换到主线程。AsyncTask内部就是通过InternalHandler来发送任务执行的进度以及执行结束等消息。

AsyncTask排队执行过程:系统先把参数Params封装为FutureTask对象,它相当于Runnable,接着FutureTask交给SerialExcutor的execute方法,它先把FutureTask插入到任务队列tasks中,如果这个时候没有正在活动的AsyncTask任务,那么就会执行下一个AsyncTask任务,同时当一个AsyncTask任务执行完毕之后,AsyncTask会继续执行其他任务直到所有任务都被执行为止。


关于线程池,AsyncTask对应的线程池ThreadPoolExecutor都是进程范围内共享的,都是static的,所以是AsyncTask控制着进程范围内所有的子类实例。由于这个限制的存在,当使用默认线程池时,如果线程数超过线程池的最大容量,线程池就会爆掉(3.0默认串行执行,不会出现这个问题)。针对这种情况。可以尝试自定义线程池,配合AsyncTask使用。


关于AsyncTask的缺陷
这个问题博主在面试的时候也碰到过,面试官会问AsyncTask运行的原理是什么?有什么缺陷?—当时给出的答案是:AsyncTask在并发执行多个任务时发生异常。其实还是存在的,在3.0以前的系统中还是会以支持多线程并发的方式执行,支持并发线程数:128,阻塞队列可以存放10个;也就是同时执行138个任务是没有问题的;当超过138会马上出现RejectedExecutionException异常。
具体见如下测试代码:

public class TestMainActivity extends Activity{

        private static final String TAG = "MainActivity";  
        private ProgressDialog mDialog;  
        private TextView mTextView;  

        @Override  
        protected void onCreate(Bundle savedInstanceState)  
        {  
            super.onCreate(savedInstanceState);  
            setContentView(R.layout.activity_main);  

            mTextView = (TextView) findViewById(R.id.id_tv);  

            mDialog = new ProgressDialog(this);  
            mDialog.setMax(100);  
            mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);  
            mDialog.setCancelable(false);  


            for(int i = 1 ;i <= 138 ; i++)  
            {  
                new MyAsyncTask2().execute();  
            }  
        }  

        private class MyAsyncTask2 extends AsyncTask<Void,Void, Void>  
        {  

            @Override  
            protected Void doInBackground(Void... params)  
            {  
                try  
                {  
                    Log.e(TAG, Thread.currentThread().getName());  
                    Thread.sleep(10000);  
                } catch (InterruptedException e)  
                {  
                    e.printStackTrace();  
                }  
                return null;  
            }  

        }  
}

由上述测试代码可以看到程序执行138个异步任务,每个异步任务的执行需要10s,输出结果为:
AsyncTask#1 - AsyncTask #128同时输出
然后10s后,另外10个任务输出。
可以分析结果,得到结论:AsyncTask在2.2的系统中同时支持128个任务并发,至少支持10个任务等待;
下面将138个任务,改成139个任务:
for(int i = 1 ;i <= 139 ; i++)
{
new MyAsyncTask2().execute();
}
运行结果:会发生异常:java.util.concurrent.RejectedExecutionException ; 于是可以确定仅支持10个任务等待,超过10个则立即发生异常。
简述原因:现在是139个任务,几乎同时提交,线程池支持128个的并发,然后阻塞队列数量为10,此时当第11个任务提交的时候则会发生异常。
源码片段:
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

看ThreadPoolExecutor的execute方法:
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);

当阻塞队列满的时候workQueue.offer(command)返回false;然后执行addWorker(command,false)方法,如果返回false则执行reject()方法.
private boolean addWorker(Runnable firstTask, boolean core) {

可以看到当任务数目大于容量则返回false,最终在reject()中抛出异常。
把线程数改为139甚至1000,你可以看到任务一个接一个的在那缓慢的执行,不会抛什么异常,不过线程倒是1个1个的在那执行;
简述AsyncTask的缺陷问题
AsyncTask的缺陷,可以分为两个部分说,在3.0以前,最大支持128个线程的并发,10个任务的等待。在3.0以后,无论有多少任务,都会在其内部单线程执行;
今天就写到这里!如果有什么不足的地方还请见谅!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值