1、Asynctask简介
1.1 使用方法简介
Asynctask作为AndroID的基础之一,怎么使用就不多讲解了,网上到处都是教程,建议查看AndroID官方api文档:https://developer.android.google.cn/reference/android/os/AsyncTask.html
这里只实现一个小Demo程序,供大家赏玩:
界面:
这个程序其实特别简单,就是两个按钮,点击分别用来测试AysncTask和Handler两种模式的实现,点击后会有相应的Log提示。
功能简介:
Asynctask的实现:
private class IAsyncTask extends AsyncTask<String,Integer,String> { protected String doInBackground(String... args1) { Log.i(TAG,"doInBackground in:" + args1[0]); int times = 10; for (int i = 0; i < times; i++) { publishProgress(i);//提交之后,会执行onProcessUpdate方法 } Log.i(TAG,"doInBackground out"); return "over"; } /** * 在调用cancel方法后会执行到这里 */ protected voID onCancelled() { Log.i(TAG,"onCancelled"); } /** * 在doInbackground之后执行 */ protected voID onPostExecute(String args3) { Log.i(TAG,"onPostExecute:" + args3); } /** * 在doInBackground之前执行 */ @OverrIDe protected voID onPreExecute() { Log.i(TAG,"onPreExecute"); } /** * 特别赞一下这个多次参数的方法,特别方便 * @param args2 */ @OverrIDe protected voID onProgressUpdate(Integer... args2) { Log.i(TAG,"onProgressUpdate:" + args2[0]); } }
点击第一个按钮后会执行这里,点击按钮的写法如下:
mBtnSyncTask.setonClickListener(new VIEw.OnClickListener() { @OverrIDe public voID onClick(VIEw v) { new IAsyncTask().execute("yanlog test"); } });
执行结果的Log如下:
02-19 21:55:12.179 10824-11010/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in:yanlog test//doInbackground是在10824进程,11010线程中执行02-19 21:55:12.179 10824-11010/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground out 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:0//剩下的都是在10824线程中执行,AndroID特别好的是,主线程的线程号跟进程号是一致的02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:102-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:202-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:302-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:402-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:502-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:602-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:702-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:802-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:902-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onPostExecute:over
Handler+Message实现:
主要代码如下:
private class IHandler extends Handler{ @OverrIDe public voID handleMessage(Message msg){ switch(msg.what){ case 1: Log.e(TAG,"handler:"+msg.obj); break; default: break; } } }
其中,调用地方如下:
mBtnHandlerTest.setonClickListener(new VIEw.OnClickListener() { @OverrIDe public voID onClick(VIEw v) { final Handler handler = new IHandler(); new Thread(new Runnable() { @OverrIDe public voID run() { for (int i = 0; i < 10; i++) { Message msg = new Message(); msg.what = 1; msg.obj = new Integer(i); Log.e(TAG,"post message:" + i); handler.sendMessage(msg); } } }).start(); } });
可以看到Log打印结果如下:
02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:0 //可以看到提交是在9319号子进程中提交02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:102-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:202-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:302-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:402-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:502-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:602-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:702-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:802-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:902-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:0 //可以看到提交完是在9234主线程中执行。02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:102-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:202-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:302-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:402-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:502-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:602-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:702-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:802-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:9
以上,简单梳理了下怎么实现,不赘言。
1.2 AndroID 内部源码实现
关于Handler+Message+Message Queue+Looper的实现就不介绍了,老生常谈了。所以下面主要看一下AsyncTask的源码实现:
AsyncTask的核心方法应该是
public final AsyncTask<Params,Progress,Result> execute(Params... params)
那我们就看下当调用了execute方法后,都发生了什么,下面是执行的序列图。
我知道我画的不够标准了,凑合着看吧。下面关于这个图的一些说明。
在第4步,execute的时候,这个时候可以看到,doInBackground已经转到子线程中执行了,这个是很关键的一个点,我特意用了一个异步处理的箭头标注了。 在第9步,当doInbackground执行完,执行到finish方法的时候,由通过sendMessage的方法回到了主线程中了,所以后面的onPostExecute和onCanceled都是在主线程中执行的。嗯,就这么多吧。关于AsyncTask的源码我上传到github中了,大家对照着源码看会更清楚一点。
https://github.com/YanYoJun/AndroidSource/blob/master/AsyncTask.java
关于AsyncTask的源码分析,还有一篇博客写的很好,请参看:
https://www.oudahe.com/p/26369/
备注:
源码里面还有三个地方值得深究下,分别是:
FutureTask值得看下,回头写了博客我把链接贴在这里 AsyncTask中的SerialExecutor类写的太漂亮了,回头单独写一个博客欣赏下。 关于上面的ThreadPollExecutor我其实没有研究。。。回头写个博客研究下。2、优点
简单,快捷
这个说法就是近乎于扯淡吧,主要还是看使用习惯,我就挺喜欢用Handler的。
但是AndroID定义了这个东西,可以看到各种消息封装的还是很不错的,很规范。大家可以按照这个“优美的框架”来写,代码不会太出格。
3、缺点
3.1 AsyncTask实际上后台线程之后一个!!!
今天仔细研究了下源码,发现网上写的大部分是错的,AsyncTask的真正的后台线程只有一个!!不信,看下面的代码:
我们首先定义一个IAsyncTAsk,其中的doInBackground方法这么写:
private class IAsyncTask extends AsyncTask<String,String> { protected String doInBackground(String... args1) {/* Log.i(TAG,"doInBackground out");*/ Log.i(TAG,"doInBackground in thread:" + args1[0]); try { int times = 4; for (int i = 0; i < times; i++) { Log.i(TAG,"thread alive:" + i + " for times"+args1[0]); //这个doInBackground就打印一个Log,然后sleep 20 毫秒 Thread.sleep(20); } } catch (Exception e) { } return "over"; }
调用的地方这么写:
int N = 5; for (int i = 0; i < N; i++) { Log.d(TAG,"asyncTask post Task:"+i); new IAsyncTask().execute("asyncTask times:"+i); //点击button后,在onClick方法中建立5个后台子线程。 }
我们来看打印结果:
02-20 21:48:08.206 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:0 //在主线程中进行提交 *** 作02-20 21:48:08.211 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:102-20 21:48:08.211 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:202-20 21:48:08.211 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:302-20 21:48:08.211 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:402-20 21:48:08.212 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:0 //可以看到,虽然系统开起了18067、18068、02-20 21:48:08.212 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:0//18069,18070这几个子线程,但是这几个子线程02-20 21:48:08.232 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:0 //是串行执行的!!!震惊了有没有!!!02-20 21:48:08.253 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:0 //这不是巧合,试了好几次都是这样!!02-20 21:48:08.273 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:002-20 21:48:08.294 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:102-20 21:48:08.294 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:102-20 21:48:08.315 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:102-20 21:48:08.335 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:102-20 21:48:08.356 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:102-20 21:48:08.377 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:202-20 21:48:08.377 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:202-20 21:48:08.397 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:202-20 21:48:08.417 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:202-20 21:48:08.438 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:202-20 21:48:08.462 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:302-20 21:48:08.462 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:302-20 21:48:08.483 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:302-20 21:48:08.504 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:302-20 21:48:08.524 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:302-20 21:48:08.545 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:402-20 21:48:08.545 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:402-20 21:48:08.565 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:402-20 21:48:08.585 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:402-20 21:48:08.606 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:4
你本来希望系统应该这么执行
但是实际上系统是这么执行的:
那么从源码看下为啥会这样吧。
AsyncTask中默认的Exector是这个private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;看下SERIAL_EXECUTOR是这么定义的
public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); //这是一个串行处理的Executor......................... 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() { //先把要执行的子线程统一丢到mTasks队列中,这其中封装一遍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); } } }
呵呵,这下子明白了吧。
Google为什么要怎么实现我不得而知,估计有什么我没有明白的好处在里面吧。那么有没有办法规避呢?
可以看到上面有一个THREAD_POol_EXECUTOR,这个也是一个executor是这么定义的
static { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( CORE_POol_SIZE,MAXIMUM_POol_SIZE,KEEP_AliVE_SECONDS,TimeUnit.SECONDS,sPoolWorkQueue,sThreadFactory); threadPoolExecutor.allowCoreThreadTimeOut(true); THREAD_POol_EXECUTOR = threadPoolExecutor; }
可以看到这个是允许一定数量的子线程并行处理的。
其中参数是这么定义的
private static final int cpu_COUNT = Runtime.getRuntime().availableProcessors(); // We want at least 2 threads and at most 4 threads in the core pool,// preferring to have 1 less than the cpu count to avoID saturating // the cpu with background work private static final int CORE_POol_SIZE = Math.max(2,Math.min(cpu_COUNT - 1,4)); private static final int MAXIMUM_POol_SIZE = cpu_COUNT * 2 + 1; private static final int KEEP_AliVE_SECONDS = 30;
按照一般理解,允许同时运行的CORE进程是4个,MAXIMUM_POol_SIZE是17个。(注:这个数字是我用荣耀8手机跑出来的,其他手机可能会有不同)
而AndroID中的AsyncTask提供了一个方法:
public static voID setDefaultExecutor(Executor exec) { sDefaultExecutor = exec; }
所以规避方法是:通过这个方法可以设置默认的Exector,但是这个方法是hIDe的,也就是Google的隐藏方法,估计需要用一下反射来处理。我偷个懒,不去实现了。
4、总结
本来嘛,我只是想简单写一下AsyncTask的一些相关知识,copy一下网上的内容,但是没有想到写到最后,发现网上的大部分东西是错的,或者没有抓到重点。看来以后还是要自己亲自看代码,纸上得来终觉浅,便知此事要躬行。
本文中用到的工程代码可以到我的github中查看,路径:https://github.com/YanYoJun/AsyncTaskTest
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持编程小技巧!
总结以上是内存溢出为你收集整理的Android AsyncTask的优缺点详解全部内容,希望文章能够帮你解决Android AsyncTask的优缺点详解所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)