楼主: 熊猫儿

[转载] 【Android的一些重要知识7。。。。】

[复制链接]
论坛徽章:
1682
九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-27 15:37:10九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55玉石琵琶
日期:2014-06-26 16:52:29玉石琵琶
日期:2014-06-26 16:52:29
91#
 楼主| 发表于 2018-3-9 09:25 | 只看该作者
哈哈,出现了我们的handleMessage,可以看到,在接收到MESSAGE_POST_RESULT消息时,执行了result.mTask.finish(result.mData[0]);其实就是我们的AsyncTask.this.finish(result),于是看finish方法

[java] view plain copy


  • private void finish(Result result) {  
  •         if (isCancelled()) {  
  •             onCancelled(result);  
  •         } else {  
  •             onPostExecute(result);  
  •         }  
  •         mStatus = Status.FINISHED;  
  •     }  


可以看到,如果我们调用了cancel()则执行onCancelled回调;正常执行的情况下调用我们的onPostExecute(result);主要这里的调用是在handler的handleMessage中,所以是在UI线程中。如果你对异步消息机制不理解请看:Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
最后将状态置为FINISHED。

mWoker看完了,应该到我们的mFuture了,依然实在构造方法中完成mFuture的初始化,将mWorker作为参数,复写了其done方法。


[java] view plain copy


  • public AsyncTask() {  
  •     ...  
  •         mFuture = new FutureTask<Result>(mWorker) {  
  •             @Override  
  •             protected void done() {  
  •                 try {  
  •                     postResultIfNotInvoked(get());  
  •                 } catch (InterruptedException e) {  
  •                     android.util.Log.w(LOG_TAG, e);  
  •                 } catch (ExecutionException e) {  
  •                     throw new RuntimeException("An error occured while executing doInBackground()",  
  •                             e.getCause());  
  •                 } catch (CancellationException e) {  
  •                     postResultIfNotInvoked(null);  
  •                 }  
  •             }  
  •         };  
  • }  


使用道具 举报

回复
论坛徽章:
1682
九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-27 15:37:10九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55玉石琵琶
日期:2014-06-26 16:52:29玉石琵琶
日期:2014-06-26 16:52:29
92#
 楼主| 发表于 2018-3-9 09:27 | 只看该作者
16行:任务执行结束会调用:postResultIfNotInvoked(get());get()表示获取mWorker的call的返回值,即Result.然后看postResultIfNotInvoked方法

[java] view plain copy


  • private void postResultIfNotInvoked(Result result) {  
  •         final boolean wasTaskInvoked = mTaskInvoked.get();  
  •         if (!wasTaskInvoked) {  
  •             postResult(result);  
  •         }  
  • }  

如果mTaskInvoked不为true,则执行postResult;但是在mWorker初始化时就已经将mTaskInvoked为true,所以一般这个postResult执行不到。
好了,到了这里,已经介绍完了execute方法中出现了mWorker和mFurture,不过这里一直是初始化这两个对象的代码,并没有真正的执行。下面我们看真正调用执行的地方。
execute方法中的:
还记得上面的execute中的23行:exec.execute(mFuture)

exec为executeOnExecutor(sDefaultExecutor, params)中的sDefaultExecutor

下面看这个sDefaultExecutor
[java] view plain copy


  • private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;  
  • public static final Executor SERIAL_EXECUTOR = new SerialExecutor();  
  • private static class SerialExecutor implements Executor {  
  •         final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();  
  •         Runnable mActive;  
  •         public synchronized void execute(final Runnable r) {  
  •             mTasks.offer(new Runnable() {  
  •                 public void run() {  
  •                     try {  
  •                         r.run();  
  •                     } finally {  
  •                         scheduleNext();  
  •                     }  
  •                 }  
  •             });  
  •             if (mActive == null) {  
  •                 scheduleNext();  
  •             }  
  •         }  
  •         protected synchronized void scheduleNext() {  
  •             if ((mActive = mTasks.poll()) != null) {  
  •                 THREAD_POOL_EXECUTOR.execute(mActive);  
  •             }  
  •         }  
  • }  


使用道具 举报

回复
论坛徽章:
1682
九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-27 15:37:10九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55玉石琵琶
日期:2014-06-26 16:52:29玉石琵琶
日期:2014-06-26 16:52:29
93#
 楼主| 发表于 2018-3-9 09:28 | 只看该作者
可以看到sDefaultExecutor其实为SerialExecutor的一个实例,其内部维持一个任务队列;直接看其execute(Runnable runnable)方法,将runnable放入mTasks队尾;
16-17行:判断当前mActive是否为空,为空则调用scheduleNext方法
20行:scheduleNext,则直接取出任务队列中的队首任务,如果不为null则传入THREAD_POOL_EXECUTOR进行执行。
下面看THREAD_POOL_EXECUTOR为何方神圣:
[java] view plain copy


  • public static final Executor THREAD_POOL_EXECUTOR  
  •           =new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,  
  •                     TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);  

可以看到就是一个自己设置参数的线程池,参数为:
[java] view plain copy


  • private static final int CORE_POOL_SIZE = 5;  
  • private static final int MAXIMUM_POOL_SIZE = 128;  
  • private static final int KEEP_ALIVE = 1;  
  • private static final ThreadFactory sThreadFactory = new ThreadFactory() {  
  • private final AtomicInteger mCount = new AtomicInteger(1);  
  • public Thread newThread(Runnable r) {  
  •      return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());  
  •     }  
  • };  
  • private static final BlockingQueue<Runnable> sPoolWorkQueue =  
  •             new LinkedBlockingQueue<Runnable>(10);  


看到这里,大家可能会认为,背后原来有一个线程池,且最大支持128的线程并发,加上长度为10的阻塞队列,可能会觉得就是在快速调用138个以内的AsyncTask子类的execute方法不会出现问题,而大于138则会抛出异常。http://blog.csdn.net/lmj623565791/article/details/38614699

使用道具 举报

回复
论坛徽章:
1682
九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-27 15:37:10九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55玉石琵琶
日期:2014-06-26 16:52:29玉石琵琶
日期:2014-06-26 16:52:29
94#
 楼主| 发表于 2018-3-11 21:50 | 只看该作者
其实不是这样的,我们再仔细看一下代码,回顾一下sDefaultExecutor,真正在execute()中调用的为sDefaultExecutor.execute:
[java] view plain copy


  • private static class SerialExecutor implements Executor {  
  •         final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();  
  •         Runnable mActive;  
  •         public synchronized void execute(final Runnable r) {  
  •             mTasks.offer(new Runnable() {  
  •                 public void run() {  
  •                     try {  
  •                         r.run();  
  •                     } finally {  
  •                         scheduleNext();  
  •                     }  
  •                 }  
  •             });  
  •             if (mActive == null) {  
  •                 scheduleNext();  
  •             }  
  •         }  
  •         protected synchronized void scheduleNext() {  
  •             if ((mActive = mTasks.poll()) != null) {  
  •                 THREAD_POOL_EXECUTOR.execute(mActive);  
  •             }  
  •         }  
  • }  


可以看到,如果此时有10个任务同时调用execute(s synchronized)方法,第一个任务入队,然后在mActive = mTasks.poll()) != null被取出,并且赋值给mActivte,然后交给线程池去执行。然后第二个任务入队,但是此时mActive并不为null,并不会执行scheduleNext();所以如果第一个任务比较慢,10个任务都会进入队列等待;真正执行下一个任务的时机是,线程池执行完成第一个任务以后,调用Runnable中的finally代码块中的scheduleNext,所以虽然内部有一个线程池,其实调用的过程还是线性的。一个接着一个的执行,相当于单线程。

使用道具 举报

回复
论坛徽章:
1682
九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-27 15:37:10九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55玉石琵琶
日期:2014-06-26 16:52:29玉石琵琶
日期:2014-06-26 16:52:29
95#
 楼主| 发表于 2018-3-11 21:51 | 只看该作者
4、总结

到此源码解释完毕,由于代码跨度比较大,我们再回顾一下:


[java] view plain copy


  • public final AsyncTask<Params, Progress, Result> execute(Params... params) {  
  •         return executeOnExecutor(sDefaultExecutor, params);  
  • }  
  • public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,  
  •             Params... params) {  
  •         if (mStatus != Status.PENDING) {  
  •             switch (mStatus) {  
  •                 case RUNNING:  
  •                     throw new IllegalStateException("Cannot execute task:"  
  •                             + " the task is already running.");  
  •                 case FINISHED:  
  •                     throw new IllegalStateException("Cannot execute task:"  
  •                             + " the task has already been executed "  
  •                             + "(a task can be executed only once)");  
  •             }  
  •         }  
  •   
  •         mStatus = Status.RUNNING;  
  •   
  •         onPreExecute();  
  •   
  •         mWorker.mParams = params;  
  •         exec.execute(mFuture);  
  •   
  •         return this;  
  •     }  


18行:设置当前AsyncTask的状态为RUNNING,上面的switch也可以看出,每个异步任务在完成前只能执行一次。
20行:执行了onPreExecute(),当前依然在UI线程,所以我们可以在其中做一些准备工作。
22行:将我们传入的参数赋值给了mWorker.mParams ,mWorker为一个Callable的子类,且在内部的call()方法中,调用了doInBackground(mParams),然后得到的返回值作为postResult的参数进行执行;postResult中通过sHandler发送消息,最终sHandler的handleMessage中完成onPostExecute的调用。
23行:exec.execute(mFuture),mFuture为真正的执行任务的单元,将mWorker进行封装,然后由sDefaultExecutor交给线程池进行执行。

使用道具 举报

回复
论坛徽章:
1682
九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-27 15:37:10九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55玉石琵琶
日期:2014-06-26 16:52:29玉石琵琶
日期:2014-06-26 16:52:29
96#
 楼主| 发表于 2018-3-11 21:52 | 只看该作者
5、publishProgress

说了这么多,我们好像忘了一个方法:publishProgress


[java] view plain copy


  • protected final void publishProgress(Progress... values) {  
  •         if (!isCancelled()) {  
  •             sHandler.obtainMessage(MESSAGE_POST_PROGRESS,  
  •                     new AsyncTaskResult<Progress>(this, values)).sendToTarget();  
  •         }  
  • }  

也很简单,直接使用sHandler发送一个消息,并且携带我们传入的值;

[java] view plain copy


  • private static class InternalHandler extends Handler {  
  •         @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})  
  •         @Override  
  •         public void handleMessage(Message msg) {  
  •             AsyncTaskResult result = (AsyncTaskResult) msg.obj;  
  •             switch (msg.what) {  
  •                 case MESSAGE_POST_RESULT:  
  •                     // There is only one result  
  •                     result.mTask.finish(result.mData[0]);  
  •                     break;  
  •                 case MESSAGE_POST_PROGRESS:  
  •                     result.mTask.onProgressUpdate(result.mData);  
  •                     break;  
  •             }  
  •         }  
  • }  


在handleMessage中进行了我们的onProgressUpdate(result.mData);的调用。

使用道具 举报

回复
论坛徽章:
1682
九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-27 15:37:10九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55玉石琵琶
日期:2014-06-26 16:52:29玉石琵琶
日期:2014-06-26 16:52:29
97#
 楼主| 发表于 2018-3-11 21:54 | 只看该作者
6、AsyncTask曾经缺陷

记得以前有个面试题经常会问道:AsyncTask运行的原理是什么?有什么缺陷?

以前对于缺陷的答案可能是:AsyncTask在并发执行多个任务时发生异常。其实还是存在的,在3.0以前的系统中还是会以支持多线程并发的方式执行,支持并发数也是我们上面所计算的128,阻塞队列可以存放10个;也就是同时执行138个任务是没有问题的;而超过138会马上出现java.util.concurrent.RejectedExecutionException;

而在在3.0以上包括3.0的系统中会为单线程执行(即我们上面代码的分析);

空说无凭:下面看测试代码:


[java] view plain copy


  • package com.example.zhy_asynctask_demo01;  
  •   
  • import android.app.Activity;  
  • import android.app.ProgressDialog;  
  • import android.os.AsyncTask;  
  • import android.os.Bundle;  
  • import android.util.Log;  
  • import android.widget.TextView;  
  •   
  • public class MainActivity 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();  
  •         }  
  •          
  •         //new MyAsyncTask().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;  
  •         }  
  •          
  •     }  
  • }  


使用道具 举报

回复
论坛徽章:
1682
九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-27 15:37:10九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55玉石琵琶
日期:2014-06-26 16:52:29玉石琵琶
日期:2014-06-26 16:52:29
98#
 楼主| 发表于 2018-3-11 21:54 | 只看该作者
可以看到我for循环中执行138个异步任务,每个异步任务的执行需要10s;下面使用2.2的模拟器进行测试:

输出结果为:

AsyncTask#1 - AsyncTask #128同时输出
然后10s后,另外10个任务输出。
可以分析结果,得到结论:AsyncTask在2.2的系统中同时支持128个任务并发,至少支持10个任务等待;

下面将138个任务,改成139个任务:


[java] view plain copy


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

运行结果:会发生异常:java.util.concurrent.RejectedExecutionException ; 于是可以确定仅支持10个任务等待,超过10个则立即发生异常。
简单说一下出现异常的原因:现在是139个任务,几乎同时提交,线程池支持128个的并发,然后阻塞队列数量为10,此时当第11个任务提交的时候则会发生异常。

简单看一下源码:


[java] view plain copy


  • public static final Executor THREAD_POOL_EXECUTOR  
  •            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);  

看ThreadPoolExecutor的execute方法:

[java] view plain copy


  • 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);  
  • http://blog.csdn.net/lmj623565791/article/details/38614699


使用道具 举报

回复
论坛徽章:
1682
九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-27 15:37:10九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55玉石琵琶
日期:2014-06-26 16:52:29玉石琵琶
日期:2014-06-26 16:52:29
99#
 楼主| 发表于 2018-3-12 08:49 | 只看该作者
可以看到我for循环中执行138个异步任务,每个异步任务的执行需要10s;下面使用2.2的模拟器进行测试:

输出结果为:

AsyncTask#1 - AsyncTask #128同时输出
然后10s后,另外10个任务输出。
可以分析结果,得到结论:AsyncTask在2.2的系统中同时支持128个任务并发,至少支持10个任务等待;

下面将138个任务,改成139个任务:


[java] view plain copy


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

运行结果:会发生异常:java.util.concurrent.RejectedExecutionException ; 于是可以确定仅支持10个任务等待,超过10个则立即发生异常。
简单说一下出现异常的原因:现在是139个任务,几乎同时提交,线程池支持128个的并发,然后阻塞队列数量为10,此时当第11个任务提交的时候则会发生异常。

简单看一下源码:


[java] view plain copy


  • public static final Executor THREAD_POOL_EXECUTOR  
  •            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);  

看ThreadPoolExecutor的execute方法:

[java] view plain copy


  • 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()方法.

使用道具 举报

回复
论坛徽章:
1682
九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-27 15:37:10九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55玉石琵琶
日期:2014-06-26 16:52:29玉石琵琶
日期:2014-06-26 16:52:29
100#
 楼主| 发表于 2018-3-12 08:50 | 只看该作者


[java] view plain copy


  • private boolean addWorker(Runnable firstTask, boolean core) {  
  • …  
  • int wc = workerCountOf(c);  
  •                 if (wc >= CAPACITY ||  
  •                     wc >= (core ? corePoolSize : maximumPoolSize))  
  •                     return false;  
  • …  
  • }  


可以看到当任务数目大于容量则返回false,最终在reject()中抛出异常。

上面就是使用2.2模拟器测试的结果;

下面将系统改为4.1.1,也就是我的测试机小米2s

把线程数改为139甚至1000,你可以看到任务一个接一个的在那缓慢的执行,不会抛什么异常,不过线程倒是1个1个的在那执行;


好了,如果现在大家去面试,被问到AsyncTask的缺陷,可以分为两个部分说,在3.0以前,最大支持128个线程的并发,10个任务的等待。在3.0以后,无论有多少任务,都会在其内部单线程执行;


至此,AsyncTask源码分析完毕,相信大家对AsyncTask有了更深的理解~~~



使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表