欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android AsyncTask的优缺点详解

程序员文章站 2023-12-16 23:40:28
1、asynctask简介 1.1 使用方法简介 asynctask作为android的基础之一,怎么使用就不多讲解了,网上到处都是教程,建议查看android官方ap...

1、asynctask简介

1.1 使用方法简介

asynctask作为android的基础之一,怎么使用就不多讲解了,网上到处都是教程,建议查看android官方api文档:https://developer.android.google.cn/reference/android/os/asynctask.html

这里只实现一个小demo程序,供大家赏玩:

界面:

Android AsyncTask的优缺点详解

这个程序其实特别简单,就是两个按钮,点击分别用来测试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方法后,都发生了什么,下面是执行的序列图。

Android AsyncTask的优缺点详解

我知道我画的不够标准了,凑合着看吧。下面关于这个图的一些说明。

  • 在第4步,execute的时候,这个时候可以看到,doinbackground已经转到子线程中执行了,这个是很关键的一个点,我特意用了一个异步处理的箭头标注了。
  • 在第9步,当doinbackground执行完,执行到finish方法的时候,由通过sendmessage的方法回到了主线程中了,所以后面的onpostexecute和oncanceled都是在主线程中执行的。

嗯,就这么多吧。关于asynctask的源码我上传到github中了,大家对照着源码看会更清楚一点。

https://github.com/yanyojun/androidsource/blob/master/asynctask.java

关于asynctask的源码分析,还有一篇博客写的很好,请参看:

备注:

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

  • 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

你本来希望系统应该这么执行

Android AsyncTask的优缺点详解

但是实际上系统是这么执行的:

Android AsyncTask的优缺点详解

那么从源码看下为啥会这样吧。

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

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持!

上一篇:

下一篇: