内容简介:本文概要:在阅读本文之前,需要对Handler消息机制有所了解才能深入理解,如果对Handler了解还不够深入,可以先阅读这篇文章-Android提供的一个异步类,它封装了handler和线程池,从而简化了更新UI的问题.
本文概要:
- AsyncTask
- HandlerThread与IntentService
在阅读本文之前,需要对Handler消息机制有所了解才能深入理解,如果对Handler了解还不够深入,可以先阅读这篇文章- Android消息机制Handler
一. AsyncTask
Android提供的一个异步类,它封装了handler和线程池,从而简化了更新UI的问题.
1.1基本使用
AsyncTask是一个抽象的泛型类提供三个泛型参数分别为 Params , Progress, Result.
- Params : 输入的参数类型
- Progress : 执行任务的进度
- Result : 返回结果的类型
public abstract class AsyncTask<Params, Progress, Result> 复制代码
提供了4个核心方法:
-
onPreExecute:主线程中执行,任务开始前.
-
Result doInBackground(Params... params): 子线程执行,执行任务时.
- 当我们调用publishProgress(Progress... values)更新任务进度时,会回调onProgressUpdate方法.
- 返回计算结果Result给onPostExecute方法.
-
onProgressUpdate(Progress... values): 主线程中执行,任务的执行进度更新时.
-
onPostExecute(Result result):主线程中执行,任务执行完毕时.
使用示例
public class AsyncTaskActivity extends AppCompatActivity implements View.OnClickListener { //...省略部分代码 class MyAsyncTask extends AsyncTask<String, Integer, Robot> { @Override protected void onPreExecute() { mProgressDialog = new ProgressDialog(AsyncTaskActivity.this); mProgressDialog.setMessage("正在加载"); mProgressDialog.setMax(10); mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); mProgressDialog.show(); } @Override protected Robot doInBackground(String... strings) { Robot robot = null; if (strings != null && strings.length > 0) { for (int i = 0; i < 11; i++) { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } publishProgress(i); if (isCancelled()) { break; } else if (i == 10) {{ robot = new Robot("i", strings[0]); } } } else { throw new IllegalArgumentException("please set the params"); } return robot; } @Override protected void onProgressUpdate(Integer... values) { if (values != null && values.length > 0) { mProgressDialog.setProgress(values[0]); } } @Override protected void onPostExecute(Robot robot) { mProgressDialog.dismiss(); tvName.setText(robot == null ? "参数不详" : robot.getName()); } @Override protected void onCancelled() { tvName.setText("任务被取消"); } } } 复制代码
首先在onPreExecute初始化了ProgressDialog控件,接着通过doInBackground模拟执行耗时的机器人构造流程.在构造流程中调用了publishProgress去更新执行任务的进度.最后当任务执行完后在onPostExecute更新了机器人名,这时如果点击了取消任务,那么onCancelled则会调用.
关于AsyncTask的使用比较简单就到这里点到为止.我们具体来看它实现的原理.
1.2基本原理
源码版本基于Android 8.0
1. 我们首先从AsyncTask的构造开始,看具体做了哪些操作.
对应源码
public AsyncTask() { this((Looper) null); } public AsyncTask(@Nullable Looper callbackLooper) { //1.初始化Handler mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper() ? getMainHandler() : new Handler(callbackLooper); //2.初始化WorkerRunnable,实际是一个实现Callable接口的类. mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { //...省略具体实现代码,后面再分析. return result; } }; //3.初始化FutureTask封装了WorkerRunnable. //在run方法中调用了mWorker的Call方法. mFuture = new FutureTask<Result>(mWorker) { @Override protected void done() { //...省略具体实现代码,后面再分析. } }; } 复制代码
这里主要干了三个初始化工作.
- 初始化Handler,绑定主线程的Looper.
-
初始化WorkerRunnable,实际是一个实现Callable接口的类.
- (如果对Callable还比较陌生,可以看前面的文章介绍.)
-
初始化FutureTask封装了WorkerRunnable.
- 这里简单复习下FutureTask,它既实现了Runnable接口,又实现了Future接口.所以它既可以实现执行线程,又可以获取线程执行完后的返回值.(如果对FutureTask还比较陌生,可以看前面的文章介绍.)
2. 当我们实例化一个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;//将输入参数封装到WorkRunnable exec.execute(mFuture);//执行任务. return this; } 复制代码
在excute方法中调用了executeOnExecutor方法,对这个流程做个小结.
- 检查状态.
- FutureTask持有一个WorkRunnable,WorkRunnable持有传入的参数.
- 最后利用线程池,去执行futureTask任务,这里可以将FutureTask看成相当于Runnable的作用.
3. 线程池的处理流程.
在executeOnExecutor(sDefaultExecutor, params)方法中用到了sDefaultExecutor线程池.我们接着看线程池的实现.
对应源码
/** * 用于任务的排队的线程池 */ private static class SerialExecutor implements Executor { final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>(); Runnable mActive; public synchronized void execute(final Runnable r) { //1. 插入到任务队列 mTasks.offer(new Runnable() { public void run() { try { r.run();//调用futureTask的run方法 } finally {//任务执行完毕,继续执行下一个任务. scheduleNext(); } } }); if (mActive == null) {//无任务,执行下一个. scheduleNext(); } } protected synchronized void scheduleNext() { if ((mActive = mTasks.poll()) != null) {//不断从队列中取任务 THREAD_POOL_EXECUTOR.execute(mActive); } } } /** * 执行任务的线程池THREAD_POOL_EXECUTOR */ 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; private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(128); 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; } 复制代码
这里涉及到两个线程池,也是AsyncTask的核心之处.
-
SerialExecutor
: 用于任务的排队.
- 插入到任务队列.
- 无任务或者任务执行完后,不断从队列中取新任务到另一线程池中执行.
-
threadPoolExecutor
: 用于真正执行任务.
- CORE_POOL_SIZE :核心线程数与CPU核有关
- MAXIMUM_POOL_SIZE : 最大线程数与CPU核有关
-
KEEP_ALIVE_SECONDS, TimeUnit.SECONDS :超时机制为30秒
- 超时机制也作用于核心线程.
- sPoolWorkQueue : 阻塞队列
正因为SerialExecutor的存在,从上面可以看出3.0之后是串行执行,所以不会有并发问题(执行饱和策略).
4. 通过Call方法最后我们来看执行任务后是如何回调给主线程的.
在futureTask的run方法会回调WorkerRunnable的call方法.这里再回顾一下上一篇FutureTask的run方法源码.
对应源码
public void run() { if (state != NEW || !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread())) return; try { Callable<V> c = callable; if (c != null && state == NEW) { V result; boolean ran; try { //回调callable的Call方法,获取异步任务返回值. result = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } if (ran) set(result); } } finally { //...省略部分代码 } } 复制代码
这里继续回到最开始初始化WorkerRunnable时代码.
mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { mTaskInvoked.set(true); Result result = null; try { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); /** * 1.回调doInBackground方法 */ result = doInBackground(mParams); Binder.flushPendingCommands(); } catch (Throwable tr) { mCancelled.set(true); throw tr; } finally { postResult(result); } return result; } }; /** * 2.通过handler将result传递出去 */ private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); message.sendToTarget(); return result; } /** * 3. 处理消息 */ private static class InternalHandler extends Handler { public InternalHandler(Looper looper) { super(looper); } @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 //调用finish result.mTask.finish(result.mData[0]); break; //...省略部分代码 } } } private void finish(Result result) { if (isCancelled()) { //回调已取消 onCancelled(result); } else { //回调onPostExecute onPostExecute(result); } mStatus = Status.FINISHED; } 复制代码
Call方法流程小结.
- 回调doInBackground方法.
- 发送消息,通过handler将result传递出去.
- 处理消息,回调onCancelled或onPostExecute.
最后给出一张简略版的AsyncTask的工作流程图
asyntask工作流程简略图
到这里AsyncTask的实现原理基本分析完成了,至于之前提到的3.0之前是并行的,3.0之后是串行的,如果想要实现并行可以采用如下方式.
//方式一 executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,params); //方式二 executeOnExecutor(自定义的线程池,params); 复制代码
对于3.0以下是并行的效果,了解即可.这里就不演示了.因为现在APP基本上5.0以下都不适配了,我们又何必螳臂当车,节约宝贵的时间看未来趋势的知识.
二. HandlerThread与IntentService
2.1 HandlerThread
是一个消息循环的线程,这样就可以在该线程中使用Handler了.
对应源码
@Override public void run() { mTid = Process.myTid(); Looper.prepare();//创建消息队列 synchronized (this) { mLooper = Looper.myLooper(); //Looper初始化完成,唤醒因此条件阻塞的线程 notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop();//开启消息循环 mTid = -1; } public Looper getLooper() { if (!isAlive()) { return null; } //线程已经启动,等待looper初始化完成 synchronized (this) { while (isAlive() && mLooper == null) { try { wait(); } catch (InterruptedException e) { } } } return mLooper; } 复制代码
与普通线程相比,它的特点.
- 普通线程是在run方法执行耗时任务
- 它可以在此线程中使用handler来执行耗时任务,由于是无限循环,不再使用时,也需调用quit或者quitSafely来终止线程的执行.
- 具体使用场景见IntentService。
对应源码
2.2 IntentService
是一个Service,封装了handlerThread与Handler.
正因为它是一个Service,所以不会容易被系统杀死.具有以下特点.
特点:
- 相对于服务,能够在服务中执行耗时任务.
- 相对于线程,优先级比单纯的线程高.
- 执行完后会自动停止.
基本使用
public class MyIntentService extends IntentService { private static final String TAG = "MyIntentService"; public MyIntentService() { super(TAG); } @Override protected void onHandleIntent(@Nullable Intent intent) { String taskName = intent.getStringExtra("taskName"); Log.d(TAG, "taskName: " + taskName); SystemClock.sleep(2500); if ("org.jason.taskOne".equals(taskName)){ Log.d(TAG, "do task: "+taskName); } } @Override public void onDestroy() { Log.d(TAG, "onDestroy: "); super.onDestroy(); } } private void doIntentService() { //连续开三个服务测试 Intent intent = new Intent(this, MyIntentService.class); intent.putExtra("taskName", "org.jason.taskOne"); startService(intent); intent.putExtra("taskName", "org.jason.taskTw0"); startService(intent); intent.putExtra("taskName", "org.jason.taskThree"); startService(intent); } //调用输出 12-27 14:34:01.338 D/MyIntentService: taskName: org.jason.taskOne 12-27 14:34:03.839 D/MyIntentService: do task: org.jason.taskOne 12-27 14:34:03.840 D/MyIntentService: taskName: org.jason.taskTw0 12-27 14:34:06.341 D/MyIntentService: taskName: org.jason.taskThree 12-27 14:34:08.841 D/MyIntentService: onDestroy: 复制代码
从上面日志除了可以看出它的特点外,还能发现任务都是按顺序依次执行的.这与它内部的hanlder处理消息有关,因为handler的looper就是按顺序处理消息的,接着我们去看是如何实现的.
基本原理
IntentService是一个继承Service的抽象类.既然是Service我们就按照Service的生命周期来分析.
1. 首先看OnCreate方法流程
对应源码
public abstract class IntentService extends Service { @Override public void onCreate() { super.onCreate(); //1.初始化一个HandlerThread HandlerThread thread = new HandlerThread("IntentService[" + mName + "]"); thread.start(); //2.初始化一个Handler,绑定HandlerThread的Looper.这样就能使用handler,给HandlerThread线程发消息了. //(也就是说绑定了在哪个线程的looper,那么发送的消息就在哪个线程处理) mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); } } 复制代码
2. 接着OnStart方法流程
onStartCommand里面调用了onStart方法,这里直接看此方法.
对应源码
@Override public void onStart(@Nullable Intent intent, int startId) { Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; msg.obj = intent; //1.利用handler发送消息,消息内容就是我们传入的intent以及服务id标识. mServiceHandler.sendMessage(msg); } private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } //2.处理消息, //注意:此handler绑定了HandlerThread的looper,所以是在子线程处理消息. @Override public void handleMessage(Message msg) { //a.回调onHandleIntent onHandleIntent((Intent)msg.obj); //b.停止服务 stopSelf(msg.arg1); } } 复制代码
3.最后看onDestroy方法
@Override public void onDestroy() { //最后退出looper,这样消息队列才能退出,最终线程才会销毁.不然一直处于阻塞等待状态. mServiceLooper.quit(); } 复制代码
前面讲解HandlerThread时也有提过,当不使用时,需调用quit或者quitSafely来终止线程的执行.可以看出系统源码也是有这一步,所以当我们自定义一个具有消息循环的线程一定记得退出,这是良好的编程习惯.
关于HandlerThread以及它的应用IntentService就介绍到这里了.
由于本人技术有限,如有错误的地方,麻烦大家给我提出来,本人不胜感激,大家一起学习进步.
参考链接:
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 各个线程 Autorelease 对象的内存管理
- Java并发编程(01):线程的创建方式,状态周期管理
- [译] Go 语言的协程,系统线程以及 CPU 管理
- java中线程安全,线程死锁,线程通信快速入门
- ObjC 多线程简析(一)-多线程简述和线程锁的基本应用
- Java多线程之线程中止
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。