当前位置: 移动技术网 > IT编程>移动开发>Android > 详解Android中用于线程处理的AsyncTask类的用法及源码

详解Android中用于线程处理的AsyncTask类的用法及源码

2019年07月24日  | 移动技术网IT编程  | 我要评论

学府卡,邪恶漫画 女体觉醒,本机的ip地址

为什么要用asynctask
我们写app都有一个原则,主线程不能够运行需要占用大量cpu时间片的任务,如大量复杂的浮点运算,较大的磁盘io操作,网络socket等,这些都会导致我们的主线程对用户的响应变得迟钝,甚至anr,这些会使应用的用户体验变差,但是有时又的确需要执行这些耗时的任务,那么我们通常可以使用asynctask或者new thread
来处理,这样把任务放入工作线程中执行,不会占用主线程的时间片,所以主线程会及时响应用户的操作,如果使用new thread来执行任务,那么如果需要中途取消任务执行或者需要返回任务执行结果,就需要我们自己维护很多额外的代码,而asynctask是基于concurrent架包提供的并发类实现的,上面的二个需求都已经帮我们封装了,这也是我们选择asynctask的原因。

怎么用asynctask
我们还是简单介绍下asynctask一些使用示例。我们先新建一个类demoasynctask继承asynctask,因为asynctask是抽象类,其中doinbackground方法必须重写。

private class demoasynctask extends asynctask<string, void, void> {
 @override
 protected void onpreexecute() {
  super.onpreexecute();
 }

 @override
 protected void doinbackground(string... params) {
  return null;
 } 

 @override
 protected void onpostexecute(void avoid) {
  super.onpostexecute(avoid);
 }

 @override
 protected void onprogressupdate(void... values) {
  super.onprogressupdate(values);
 }

 @override
 protected void oncancelled(void avoid) {
  super.oncancelled(avoid);
 }

 @override
 protected void oncancelled() {
  super.oncancelled();
 }
}

demoasynctask task = new demoasynctask();
task.execute("demo test asynctask");
//task.executeonexecutor(asynctask.serial_executor, "test");
//mytask.executeonexecutor(asynctask.thread_pool_executor, "test");

简单分析下
上面就是asynctask最简单的使用方法,我们上面重写的方法中,oninbackground方法运行在工作线程,其他的方法全部运行在主线程,另外它的运行方式android提供给我们2个方法,上面都列出了。

1.第一个方法会使用默认的executor执行我们的任务, 其实也就是serial_executor,serial_executor我们其实也是可以通过方法去自定义的,android帮我们的默认实现是逐个执行任务,也就是单线程的,关于asynctask的任务执行是单线程实现还是多线程实现还有一段很有意思的历史,较早的版本是单线程实现,从android2.x开始,google又把它改为多线程实现,后来google发现,多线程实现的话,会有很多需要保证线程安全的额外工作留给开发者,所以从android3.0开始,又把默认实现改为单线程了,今天我们演示的是framwork代码版本是21(android5.0)。
2.同时也提供了多线程实现的接口,即我们上面写的最后一行代码 asynctask.thread_pool_executor。

public final asynctask<params, progress, result> execute(params... params) {
 return executeonexecutor(sdefaultexecutor, params);
}
private static volatile executor sdefaultexecutor = serial_executor;

其实相信大家平时工作学习中经常使用asynctask,我们直接去看asynctask类源码(插一句题外话,平时大家也可以把自己工作学习中的心得体会总结一下,记下来~~)

public abstract class asynctask<params, progress, result> {
....
}

asynctask抽象类,有三个泛型参数类型,第一个是你需要传递进来的参数类型,第二个是任务完成进度的类型一般是integer,第三个是任务完成结果的返回类型,有时这些参数不是全部需要,不需要的设为void即可,另外result只有一个,但params可以有多个。
我们可以看到asynctask的默认构造器初始化了二个对象,mworker和mfuture。

private final workerrunnable<params, result> mworker;
private final futuretask<result> mfuture;
 mworker = new workerrunnable<params, result>() {
  public result call() throws exception {
   mtaskinvoked.set(true);

   process.setthreadpriority(process.thread_priority_background);
   //noinspection unchecked
   return postresult(doinbackground(mparams));
  }
 };

 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);
   }
  }
 };

mwoker实现了callback接口,callback接口是jdk1.5加入的高级并发架包里面的一个接口,它可以有一个泛型返回值。

public interface callable<v> {
/**
 * computes a result, or throws an exception if unable to do so.
 *
 * @return computed result
 * @throws exception if unable to compute a result
 */
v call() throws exception;
}

futuretask实现了runnablefuture接口,runnablefuture接口是jdk提供的,看名称就知道它继承了runnable和future接口,mfuture是futuretask的一个实例,可以直接被executor类实例execute。我们来继续看asynctask的execute方法。

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;
}

先调用onpreexecute()方法,此时还在主线程(严格来说是调用asynctask执行的线程),然后exec.execute(mfuture),把任务交给exec处理,execute mfuture其实就是invoke mwoker,然后调用postresult(doinbackground(mparams)),此时已经运行在工作线程池,不会阻塞主线程。然后给mhandler发送message_post_result消息,然后调用finish方法,如果iscancelled,回调oncancelled,否则回调onpostexecute。

private result postresult(result result) {
 @suppresswarnings("unchecked")
 message message = shandler.obtainmessage(message_post_result,
   new asynctaskresult<result>(this, result));
 message.sendtotarget();
 return result;
}
private static final internalhandler shandler = new internalhandler();
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;
  }
 }
}
private void finish(result result) {
 if (iscancelled()) {
  oncancelled(result);
 } else {
  onpostexecute(result);
 }
 mstatus = status.finished;
}

现在其实我们已经把asynctask整个执行任务的过程走完了,其中暴露给我们的那几个回调方法也都走到了。现在我们回过头来看,asynctask其实只是对jdk 1.5提供的高级并发特性,concurrent架包做的一个封装,方便开发者来处理异步任务,当然里面还有很多细节处理的方法值得大家学习,如任务执行进度的反馈,任务执行原子性的保证等,这些留给大家自己学习了。

源码分析
下面我们再深入一些,来看asynctask的源码。下面分析这个类的实现,主要有线程池以及handler两部分。

线程池
当执行一个asynctask的时候调用的是execute()方法,就从这个开始看:

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
 onpreexecute(); 
 
 mworker.mparams = params; 
 
 exec.execute(mfuture); 
 return this; 
} 

execute方法会调用executeonexecutor。在这个方法中先检查任务是否已经执行或者执行结束,然后把任务标记为running。最开始执行的是onpreexecute,接着把参数赋值给mworker对象。这个mworker是一个callable对象,最终被包装为futuretask,代码如下:

private static abstract class workerrunnable<params, result> implements callable<result> { 
 params[] mparams; 
} 

mworker = new workerrunnable<params, result>() { 
  public result call() throws exception { 
   mtaskinvoked.set(true); 

   process.setthreadpriority(process.thread_priority_background); 
   //noinspection unchecked 
   return postresult(doinbackground(mparams)); 
  } 
 };
 
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); 
  } 
 } 
}; 

   
从上面的代码可以看出,在mworker对象中的call()方法会调用doinbackground,返回值交给postresult方法,这个方法通过handler发送消息,这一点稍后再详细分析。

在mworker对象被封装成futuretask之后交由线程池执行,从execute方法可以看出,使用的是sdefaultexecutor,它的值默认为serial_executor,也就是串行执行器,实现如下:

 

private static class serialexecutor implements executor { 
 //线性双向队列,用来存储所有的asynctask任务 
 final arraydeque<runnable> mtasks = new arraydeque<runnable>(); 
 //当前正在执行的asynctask任务 
 runnable mactive; 

 public synchronized void execute(final runnable r) { 
  //将新的asynctask任务加入到双向队列中 
  mtasks.offer(new runnable() { 
   public void run() { 
    try { 
     //执行asynctask任务 
     r.run(); 
    } finally { 
     //当前任务执行结束后执行下一个任务
     schedulenext(); 
    } 
   } 
  }); 
  if (mactive == null) { 
   schedulenext(); 
  } 
 } 

 protected synchronized void schedulenext() { 
  //从任务队列中取出队列头部的任务,如果有就交给并发线程池去执行 
  if ((mactive = mtasks.poll()) != null) { 
   thread_pool_executor.execute(mactive); 
  } 
 } 
}

public static final executor thread_pool_executor 
  = new threadpoolexecutor(core_pool_size, maximum_pool_size, keep_alive, 
    timeunit.seconds, spoolworkqueue, sthreadfactory); 

在上面的代码中,如果有任务执行,那么serialexecutor的execute方法会被调用,它的逻辑是把runnable对象加入arraydeque队列中,然后判断mactivie是否为空。第一次执行时mactive当然为空,所以执行schedulenext,其实就是取出任务队列中的第一个任务交给线程池(thread_pool_executor)执行。加入mtask队列的runnable对象的run方法里最终一定会调用schedulenext,那么又会从任务队列中取出队头任务执行。这样便实现了单线程顺序执行任务,所以在asynctask中默认启用的是单线程执行,只有上一个任务执行后才会执行下一个任务。如果想要启用多线程执行任务,可以直接调用 executeonexecutor(executor exec, params... params),这里的executor参数可以使用asynctask自带的thread_pool_executor,也可以自己定义。

handler
asynctask内部用handler传递消息,它的实现如下:

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; 
  } 
 } 
} 

如果消息类型是任务执行后的返回值(message_post_result)将调用finish()方法:

private void finish(result result) { 
 if (iscancelled()) { 
  oncancelled(result); 
 } else { 
  onpostexecute(result); 
 } 
 mstatus = status.finished; 
} 

从上面可以知道,如果任务取消了,将调用oncancelled,否则调用onpostexecute,所以一个asynctask任务如果取消了,那么onpostexecute将不会得到执行。

如果消息类型是执行进度(message_post_progress)将调用onprogressupdate,这个方法默认是空方法,我们可以根据自己的需要重写。

总结
asynctask的主要逻辑就如上面所分析的,总结几个需要注意的地方:

  • asynctask的类必须在ui线程加载(从4.1开始系统会帮我们自动完成)
  • asynctask对象必须在ui线程创建
  • execute方法必须在ui线程调用
  • 不要手动调用onpreexecute()、doinbackground、onprogressupdate方法
  • 一个任务只能被调用一次(第二次调用会抛出异常)

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网