当前位置: 移动技术网 > IT编程>移动开发>Android > Android AsyncTask源码分析

Android AsyncTask源码分析

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

世界天才俱乐部,爱神奔驰主题曲,四人帮覆灭记全集

android中只能在主线程中进行ui操作,如果是其它子线程,需要借助异步消息处理机制handler。除此之外,还有个非常方便的asynctask类,这个类内部封装了handler和线程池。本文先简要介绍asynctask的用法,然后分析具体实现。

基本用法
asynctask是一个抽象类,我们需要创建子类去继承它,并且重写一些方法。asynctask接受三个泛型参数:

params: 指定传给任务执行时的参数的类型
progress: 指定后台任务执行时将任务进度返回给ui线程的参数类型
result: 指定任务完成后返回的结果的类型
除了指定泛型参数,还需要根据需要重写一些方法,常用的如下:

onpreexecute(): 这个方法在ui线程调用,用于在任务执行前做一些初始化操作,如在界面上显示加载进度控件
doinbackground: 在onpreexecute()结束之后立刻在后台线程调用,用于耗时操作。在这个方法中可调用publishprogress方法返回任务的执行进度
onprogressupdate: 在doinbackground调用publishprogress后被调用,工作在ui线程
onpostexecute: 后台任务结束后被调用,工作在ui线程
源码分析
下面分析这个类的实现,主要有线程池以及handler两部分。

1、线程池
当执行一个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,也可以自己定义。

2、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的主要逻辑就如上面所分析的,总结几个需要注意的地方:

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

其它还有一些细节可以自行研究源码,另外推荐几篇不错的文章:

android asynctask完全解析,带你从源码的角度彻底理解

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

相关文章:

验证码:
移动技术网