Android多线程系列(一) AsyncTask基本使用以及源码解析

xiaoxiao2021-02-28  34

前言

Android中,线程是操作系统调度的最小单位。线程分为主线程和子线程。主线程用来处理界面的交互,而耗时操作(网络请求,复杂的数据库查询)必须在子线程中来完成。通过Handler消息机制完成主线程和子线程之间的通信。

每个任务都需要一个线程去执行,但是不可能每个任务的执行都是伴随着线程的销毁和重新创建,十分的耗费性能。所以用线程池去缓存一定数目的线程,由线程池来管理执行任务的线程,避免了频繁的创建和销毁。Android中提供了AsyncTask类,内部就是由线程池和Handler实现的。各位大佬,这篇看了包会!

(一) AsyncTask使用

public abstract class AsyncTask<Params, Progress, Result>

AsyncTask是一个抽象类,如果使用的话需要自定义一个类继承它。它提供了4个核心的方法。这里用一个示例来做介绍(Kotlin环境下)。

一.使用和核心方法介绍

1.1 自定义类继承AsyncTask

/*静态属性声明处,此处MyAsyncTask为静态类*/ companion object { class MyAsyncTask(private var taskName: String) : AsyncTask<String, Int, String>() { /*核心方法1*/ override fun onPreExecute() { super.onPreExecute() Log.d("MyAsyncTask", taskName + " OnPreExecute") } /*核心方法2*/ override fun doInBackground(vararg params: String?): String { try { Thread.sleep(5000) } catch (e: Exception) { e.printStackTrace() } /*辅助方法*/ publishProgress(50) Log.d("MyAsyncTask", taskName + " doInBackground") return taskName } /*核心方法3*/ override fun onPostExecute(result: String?) { super.onPostExecute(result) Log.d("MyAsyncTask", "I am Task" + result + " executed at " + SimpleDateFormat("yyyy-mm-dd HH:mm:ss").format(Date())) } /*核心方法4*/ override fun onProgressUpdate(vararg values: Int?) { super.onProgressUpdate(*values) Log.d("MyAsyncTask", taskName + " onProgressUpdate") } } }

声明好了静态内部类MyAsyncTask后,小手一点Button执行异步任务。

/*execute是Button控件的id,这里没有使用findViewById()*/ execute!!.setOnClickListener({ MyAsyncTask("AsyncTask1").execute() })

介绍一下上面点击按钮执行任务操作,在项目中是没有写findViewById()去初始化Button,通过Kotlin Android Extensions拓展插件直接可以使用xml布局里Button的id来调用相应的方法,两个字:简便。有兴趣的同学可以去Kotlin官网了解一波。下面是MyAsyncTask执行的结果。

根据图示介绍一下AsyncTask的核心方法

1-2.onPreExecute()

在主线中执行,用于异步任务执行之前做一些准备工作,比如初始化加载进度条之类。

1-3.doInBackground(vararg params: String?)

在子线程中执行,真正执行异步任务的方法,参数是异步任务输入参数。在此方法中可以通过 publishProgress()方法来更新异步任务当前的进度,调用publishProgress()方法会同步调用onProgressUpdate()。

1-4. onProgressUpdate(vararg values: Int?)

在主线程中执行,此方法用来实时显示当前任务的执行进度。作用是可以在此处更新UI上的进度条进度或者做其他操作。

1-5. onPostExecute(result: String?)

在主线程中执行,异步任务执行结束之后会执行此方法。参数是doInBackground()的返回值。

AsyncTask的核心方法就是上面所述,执行任务是在子线程中,而任务准备工作,进度更新等全是在主线程中完成。AsyncTask实例的创建也是要在主线程中去执行。

二.源码解析

根据代码执行顺序,我们首先看下AsyncTask的 execute()方法。

/*注解,主线程中执行*/ @MainThread public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); } @MainThread 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 后台执行任务所需参数*/ mWorker.mParams = params; /*线程池执行线程任务,mFuture封装了开启线程的mWorker*/ exec.execute(mFuture); return this; }

我们看下上面executeOnExecutor()方法中几个重要的属性。exec →* SerialExecutor*,mWorker → WorkerRunnable和mFuture → FutureTask。

WorkerRunnable:任务执行返回的结果

这里mWorker对象的创建是在AsyncTask的构造函数中,本质是开启一个线程,call()方法将任务执行完的结果返回。

mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { //标记任务已经被调用过 mTaskInvoked.set(true); Result result = null; try { //将线程的优先级设置为后台,为了执行完任务方便回收 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //后台执行任务 result = doInBackground(mParams); Binder.flushPendingCommands(); } catch (Throwable tr) { //有异常将此任务取消 mCancelled.set(true); throw tr; } finally { //任务结束 postResult(result); } return result; } };

FutureTask:线程任务(理解为要执行的任务)

这里将mWorker任务的参数封装成FutureTask对象,按照中式英语理解的话就是将来要执行的任务。本质是一个Runnable对象。

mFuture = new FutureTask<Result>(mWorker) { @Override /*任务完成后的回调*/ protected void done() { try { /*调用下方postResult()方法*/ postResultIfNotInvoked(get()); } catch (InterruptedException e) { android.util.Log.w(LOG_TAG, e); } catch (ExecutionException e) { throw new RuntimeException("An error occurred while executing doInBackground()", e.getCause()); } catch (CancellationException e) { /*调用下方postResult()方法*/ postResultIfNotInvoked(null); } } };

当线程任务执行结束后,调用postResult(Result result)通过InternalHandler将线程池(子线程任务)切换到主线程中。

private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); message.sendToTarget(); return result; }

getHandler()获取的就是InternalHandler对象,这是一个静态内部类。切换线程作用。

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 result.mTask.finish(result.mData[0]); break; /*任务执行过程,返回进度*/ case MESSAGE_POST_PROGRESS: result.mTask.onProgressUpdate(result.mData); break; } } }

exec:线程池执行类

exec是SerialExecutor串行线程池对象,主要作用是将要执行的任务(mFutureTask)通过此类被插入到任务队列mTasks中,整合任务执行顺序作用。会不断从队列中取出任务来执行,如果当前任务执行完毕,mTasks会查找队列顶端是否有mFutureTask对象。有的话就执行。没有任务结束。

/*源码中不是这样写的,这里为了方便理解*/ public static final Executor exec = new SerialExecutor(); 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() { public void run() { try { /*执行当前任务*/ r.run(); } finally { /*任务执行完成,准备执行下个任务*/ scheduleNext(); } } }); /*当前没有活跃的任务,执行下个任务*/ if (mActive == null) { scheduleNext(); } } /*执行下一个任务*/ protected synchronized void scheduleNext() { /*队列最上方的一个元素不为null即队列顶端有空闲的任务*/ if ((mActive = mTasks.poll()) != null) { /*真正执行任务的线程池:THREAD_POOL_EXECUTOR核心类ThreadPoolExecutor对象*/ THREAD_POOL_EXECUTOR.execute(mActive); } } }

其实真正执行任务的线程池是:ThreadPoolExecutor核心类的对象THREAD_POOL_EXECUTOR。

THREAD_POOL_EXECUTOR.execute(mActive);

目前 AsyncTask在Android3.0以上版本的执行顺序是串行的,有个多个任务添加到mTasks队列中,执行的顺序是从队列顶端往下依次执行。如果想实现多线程的并行任务。只要将

MyAsyncTask("AsyncTask1").execute()

替换为下述即可:

MyAsyncTask("AsyncTask1").executeOnExecutor()

结尾

Android中多线程任务虽然没有Java中使用频率高,但是是一个比较重要的知识点。AsyncTask可以在很多多线程任务中发挥好的作用,比如项目中的在线更新等。线程池也是特别重要的知识点,几乎是面试必问,接下来将会介绍线程池的工作原理等相关知识。

转载请注明原文地址: https://www.6miu.com/read-2619894.html

最新回复(0)