导语:之前做习惯了Framework层的开发,今天在武汉斗鱼公司面试APP客户端的开发,其中一道题是讲述Asynctask的优缺点,我靠,我只是知道有这么一个东西,会用而已,看来之前的生活太过于安逸,这么基础的东西都不会,特意回家总结下。

转载请注明原文链接:http://www.cnblogs.com/yanyojun/p/6414919.html

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:1
02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:2
02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:3
02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:4
02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:5
02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:6
02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:7
02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:8
02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:9
02-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:1
02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:2
02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:3
02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:4
02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:5
02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:6
02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:7
02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:8
02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:9
02-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:1
02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:2
02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:3
02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:4
02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:5
02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:6
02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:7
02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:8
02-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的源码分析,还有一篇博客写的很好,请参看:

http://www.infocool.net/kb/OtherMobile/201610/197431.html

备注:

源码里面还有三个地方值得深究下,分别是:

  • FutureTask值得看下,回头写了博客我把链接贴在这里
  • AsyncTask中的SerialExecutor类写的太漂亮了,回头单独写一个博客欣赏下。
  • 关于上面的ThreadPollExecutor我其实没有研究。。。回头写个博客研究下。

2、优点

简单,快捷

这个说法就是近乎于扯淡吧,主要还是看使用习惯,我就挺喜欢用Handler的。

但是Android定义了这个东西,可以看到各种消息封装的还是很不错的,很规范。大家可以按照这个“优美的框架”来写,代码不会太出格。

3、缺点

3.1  AsyncTask实际上后台线程之后一个!!!

今天仔细研究了下源码,发现网上写的大部分是错的,AsyncTask的真正的后台线程只有一个!!不信,看下面的代码:

我们首先定义一个IAsyncTAsk,其中的doInBackground方法这么写:

    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");*/ 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:1
02-20 21:48:08.211 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:2
02-20 21:48:08.211 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:3
02-20 21:48:08.211 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:4
02-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:0
02-20 21:48:08.294 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:1
02-20 21:48:08.294 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:1
02-20 21:48:08.315 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:1
02-20 21:48:08.335 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:1
02-20 21:48:08.356 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:1
02-20 21:48:08.377 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:2
02-20 21:48:08.377 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:2
02-20 21:48:08.397 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:2
02-20 21:48:08.417 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:2
02-20 21:48:08.438 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:2
02-20 21:48:08.462 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:3
02-20 21:48:08.462 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:3
02-20 21:48:08.483 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:3
02-20 21:48:08.504 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:3
02-20 21:48:08.524 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:3
02-20 21:48:08.545 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:4
02-20 21:48:08.545 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:4
02-20 21:48:08.565 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:4
02-20 21:48:08.585 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:4
02-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的优缺点的更多相关文章

  1. android Asynctask的优缺点?是否能同一时候并发100+asynctask呢?

    一  Asynctask的优缺点? AsyncTask,是android提供的轻量级的异步类,能够直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步运行的程度(能够通过接口实现UI ...

  2. 再谈Android AsyncTask的优缺点

    导语:之前做习惯了Framework层的开发,今天在武汉斗鱼公司面试APP客户端的开发,其中一道题是讲述Asynctask的优缺点,我靠,我只是知道有这么一个东西,会用而已,看来之前的生活太过于安逸, ...

  3. android AsyncTask介绍(转)

    android AsyncTask介绍 AsyncTask和Handler对比 1 ) AsyncTask实现的原理,和适用的优缺点 AsyncTask,是android提供的轻量级的异步类,可以直接 ...

  4. Android AsyncTask异步加载WebAPI

    之前做的程序一直存在很多问题,因为需要加载的Activity需要从网络加载数据.并没有完全正确的使用异步的方法去加载! 之前用的虽然是AsyncTask,但是在加载完成的时候还是并没有使用AsyncT ...

  5. Android -- AsyncTask 使用和缺陷

    一.AsyncTask的基本用法 由于AsyncTask是一个抽象类,所以如果我们想使用它,就必须要创建一个子类去继承它.在继承时我们可以为AsyncTask类指定三个泛型参数,这三个参数的用途如下: ...

  6. Android Asynctask与Handler的比较,优缺点区别,Asynctask源码

    1  AsyncTask实现的原理,和适用的优缺点 AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可以 ...

  7. android AsyncTask 的使用(转载)

    1 ) AsyncTask实现的原理,和适用的优缺点 AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可 ...

  8. android AsyncTask介绍 转载

    http://www.cnblogs.com/devinzhang/archive/2012/02/13/2350070.html AsyncTask和Handler对比 1 ) AsyncTask实 ...

  9. android AsyncTask介绍 AsyncTask和Handler对比

    1 ) AsyncTask实现的原理,和适用的优缺点 AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可 ...

随机推荐

  1. jQuery插件的开发

    jQuery插件的开发包括两种: 一种是类级别的插件开发,即给jQuery添加新的全局函数,相当于给jQuery类本身添加方法.jQuery的全局函数就是属于jQuery命名空间的函数,另一种是对象级 ...

  2. 浅谈MySQL分表

    关于分表:顾名思义就是一张数据量很大的表拆分成几个表分别进行存储. 我们先来大概了解以下一个数据库执行SQL的过程: 接收到SQL --> 放入SQL执行队列 --> 使用分析器分解SQL ...

  3. 【Xilinx-Petalinux学习】-05-OpenCV程序测试

    占位, 通过上一次编译的opencv库,运行程序,实现图像处理

  4. 1)Java学习笔记:接口和抽象类的异同

    Java接口和抽象类很像,他们有哪些相同点和异同点呢,下面我们做一个小结 相同 ① 都不能被实例化,都位于继承树的顶端,用于被实现或者继承 ② 都可以包含抽象方法,实现接口或者继承抽象类的普通子类都必 ...

  5. 计时器chronometer补充

    项目中要实现关于安卓控件chronometer这部分的功能需求: 1.计时器的功能对用户答题时间进行时间统计,用户答完该题,进入下一题,计时器接续上一题的结束时间继续计时: 2.用户可以跳出答题界面, ...

  6. MAC + java 环境配置

    1. 下载安装 jdk 2. 配置环境 2.1. cd到目录 etc/profile 2.2. 使文件可读:chmod 666 profile model 2.3. 添加环境变量,要切换到etc目录: ...

  7. SQL四种语言

    1.DDL(Data Definition Language)数据库定义语言statements are used to define the database structure or schema ...

  8. AutoLayout没有相对比例布局

    怎么实现相对比例布局 比如我一个控件相对上边距的位置在整个屏幕的比例 可以用stack view来管理相对布局

  9. List与Linkedlist、Arrylist、Vector、Map应用

    1.List与LinkedList      List是数组链表     LinkedList是指针链表     选择List还是LinkedList要看你的使用特点.       数组链表访问快,复 ...

  10. Flex timer使用 keydown事件注册到stage

    Flex timer使用 keydown事件注册到stage: <?xml version="1.0" encoding="utf-8"?> < ...