千家信息网

如何从源码层面解析android异步消息机制

发表于:2026-11-02 作者:千家信息网编辑
千家信息网最后更新 2026年11月02日,本篇文章为大家展示了如何从源码层面解析android异步消息机制,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。AsyncTask什么是AsyncTaskAsy
千家信息网最后更新 2026年11月02日如何从源码层面解析android异步消息机制

本篇文章为大家展示了如何从源码层面解析android异步消息机制,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。

AsyncTask

什么是AsyncTask

AsyncTask是一个轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的进度和结果传递给主线程并在主线程中更新UI。

AsyncTask这个类的声明如下

public abstract class AsyncTask

它提供了Params, Progress和 Result三个泛型参数,在下面会仔细分析这三个泛型参数的具体含义。

AsyncTask提供了四个核心方法

onPreExecute()

此方法在主线程中执行,在异步任务执行之前,此方法会被调用,一般用于一些准备工作,例如下载进度条的初始化。

doInBackground(Params… params)

此方法在子线程中执行,用于执行异步任务,注意这里的params就是AsyncTask的第一个参数类型。在此方法中可以通过调用publicProgress方法来更新任务进度,publicProgress会调用onProgressUpdate方法。

onProgressUpdate(Progress… values)

此方法在主线程中执行,values的类型就是AsyncTask传入的第二个参数类型,当后台任务的执行进度发生变化时此方法执行。

onPostExecute(Result result)

此方法在主线程中执行,在doInBackground方法执行完成以后此方法会被调用,其中result的类型就是AsyncTask传入的第三个参数类型,它的值就是doInBackground方法的返回值。

接着还是看一下AsyncTask最常见的用法,该事例就是下载一张图片到手机内存的cache目录下,下载开始时会弹出进度框,在下载过程中显示下载的进度,下载完成后关闭进度框,如成功则出现下载成功的Toast,失败则弹出失败的Toast。

class MyAsyncTask extends AsyncTask { @Override protected void onPreExecute() { progressDialog = new ProgressDialog(MainActivity.this); progressDialog.setTitle("下载进度"); progressDialog.setMax(100); progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); progressDialog.setCancelable(true); progressDialog.show(); } @Overrideprotected Boolean doInBackground(Void... params) { HttpURLConnection conn = null; try { conn = (HttpURLConnection) params[0].openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); final int contentLength; if (conn.getResponseCode() == 200) { contentLength = conn.getContentLength(); File file = new File(getCacheDir(),"hh.jpg"); InputStream is= null; FileOutputStream fos= new FileOutputStream(file); is = conn.getInputStream(); int len = 0; long totalSize = 0; byte[] bytes = new byte[102]; while ((len = is.read(bytes))!=-1) { fos.write(bytes,0,len); totalSize += len*100; int progress = (int) (totalSize/contentLength); publishProgress(progress); } } } catch (IOException e) { e.printStackTrace(); } return true;} @Override protected void onProgressUpdate(Integer... values) { progressDialog.setProgress(values[0]); } @Override protected void onPostExecute(Boolean aBoolean) { progressDialog.dismiss(); if (result) { Toast.makeText(context, "下载成功", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(context, "下载失败", Toast.LENGTH_SHORT).show(); } } }

这个类主要用于模拟文件的下载过程,它输入参数为图片url地址,后台的进程参数为Integer类型,后台任务的返回结果为bollean类型。当要执行上述下载任务时,可以通过如下方式来完成:

//在主线程中调用,执行后就会执行doInBackground等方法URL url = new URL("http://192.168.43.21:8080/ditu.jpg"); new MyAsyncTask().execute(url);

源码分析

首先我们从AsyncTask的构造方法着手。

/** * Creates a new asynchronous task. This constructor must be invoked on the UI thread. */ public AsyncTask() { mWorker = new WorkerRunnable() { public Result call() throws Exception { mTaskInvoked.set(true); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //noinspection unchecked Result result = doInBackground(mParams); Binder.flushPendingCommands(); return postResult(result); } }; mFuture = new FutureTask(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 occurred while executing doInBackground()", e.getCause()); } catch (CancellationException e) { postResultIfNotInvoked(null); } } }; }

1.如注释所说,这个构造方法必须在UI线程中调用。 2.构造函数主要是创建了两个实例。一个是WorkerRunnable,它是一个Callback对象。另一个是FutureTask,它的参数就是前面创建的WorkerRunnable对象。

接下来我们再看AsyncTask的execute()方法,这个方法是整个异步任务的入口。

@MainThread public final AsyncTask execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); } @MainThread public final AsyncTask 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(); //这里的参数由AsyncTask构造函数传进来的第一个参数,传入到doInBackground(mParams)中 mWorker.mParams = params; //******************* exec.execute(mFuture); return this; }

我们看到了onPreExecute()被调用了,证明它确实是在执行后台任务之前调用了,且是在主线程调用。此时我们执行了实例代码中的进度条显示框的显示操作,此时的界面如下

,在此方法中还进行了一个操作exec.execute(mFuture),首先我们得知道exec到底是什么?在下面源码中我们就能得到答案。

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

根据如上代码以及executeOnExecutor传进来的参数,我们看到这里的exec其实就是SerialExecutor,那我们就来看看SerialExecutor的execute方法

public synchronized void execute(final Runnable r) { mTasks.offer(new Runnable() { public void run() { try { r.run(); } finally { scheduleNext(); } } }); if (mActive == null) { scheduleNext(); } }

我们看到在子线程里执行了r.run(),根据execute()方法传入的参数可知这里的r就是最开始在构造方法中创建的FutureTask,当然接下来就该看看FutureTask的run()方法了

public void run() { if (state != NEW || !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread())) return; try { Callable c = callable; if (c != null && state == NEW) { V result; boolean ran; try { //**************************** result = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } if (ran) set(result); } } finally { // runner must be non-null until state is settled to // prevent concurrent calls to run() runner = null; // state must be re-read after nulling runner to prevent // leaked interrupts int s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } }

这里有一个result = c.call()方法,而这里的c就是Callable c = callable;里传进来的callable,而callable就是我们最开始在AsyncTask构造方法里传进来的WorkerRunnable,所以接着就该看看WorkerRunnable的call()方法,这个call()方法主要是调用了doInBackground(mParams)和postResult(result)方法

mWorker = new WorkerRunnable() { public Result call() throws Exception { mTaskInvoked.set(true); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //noinspection unchecked //********************************* Result result = doInBackground(mParams); Binder.flushPendingCommands(); return postResult(result); }

1.在这里我们看到doInBackground(mParams)方法的调用,并且此时仍然是在子线程中,所以在这里可以执行耗时操作。在上面的案例中doInBackground方法里的代码将会执行,当执行到publishProgress()方法时,会不断将进度信息传递给onProgressUpdate()方法(后面的分析中会解释为什么publishProgress()的执行能导致onProgressUpdate()方法的调用),让此方法执行更新UI.执行到这里时,案例中的下载界面如下,此时进度正好走到50%:

2.方法的最后执行了postResult(result)方法,这里面的result参数就是我们doInBackground(mParams)的返回值,这个方法的主要作用就是创建一个InternalHandler实例并发送what=MESSAGE_POST_RESULT的消息,我们接着看这个方法的源码

private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult(this, result)); message.sendToTarget(); return result; } //message方法的sendToTarget(),最终还是handler发送信息个消息队列 public void sendToTarget() { target.sendMessage(this); }

熟悉handler的朋友知道,target.sendMessage(this)方法就是将消息发送给了消息队列,而这个携带的消息就是message本身,message当中携带有MESSAGE_POST_RESULT和new AsyncTaskResult(this, result))对象,而这个对象由getHandler发送,最后也会由它来handleMessage()(对这个消息进行处理),所以我们必须找到这个handler

private static Handler getHandler() { synchronized (AsyncTask.class) { if (sHandler == null) { //在这里创建了名为sHandler的InternalHandler实例 sHandler = new InternalHandler(); } return sHandler; } }

根据上面getHandler()方法可知这个handler对象就是InternalHandler,那接下来的任务就是分析InternalHandler的handlerMessage()方法,这个方法的作用就是根据postResult(Result result)方法中的sHandler发送的不同消息进行判断来执行不同的逻辑。

@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})@Overridepublic 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; }}//result.mTask.finish(result.mData[0])方法如下 private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }//result.mTask.onProgressUpdate(result.mData) @MainThread protected void onProgressUpdate(Progress... values) { }

由上面源码可知,它根据msg携带的不同what信息进行不同的消息处理,当what = MESSAGE_POST_RESULT时,最终会执行onCancelled(result)或者onPostExecute(result),这两个方法执行时全部都是在主线程,而MESSAGE_POST_RESULT就是刚刚获取信息时传入的。执行在这里时我们下载事例的代码就走到了onPostExecute(result),我们进行了进度框的隐藏操作,执行的界面如下:

但是MESSAGE_POST_PROGRESS参数又是哪里传来的,没错,就是publishProgress(Progress… values)

@WorkerThreadprotected final void publishProgress(Progress... values) { if (!isCancelled()) { getHandler().obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult(this, values)).sendToTarget(); } }

这里传入MESSAGE_POST_PROGRESS,并且此方法在WorkerThread中调用。这样onProgressUpdate(Progress… values)方法就能顺利执行。整个流程结束

总结

1.AsyncTask的对象只能在主线程中创建 2.execute()方法只能在UI线程执行 3.不要在程序中直接调用onPreExecute,onPostExecute,doInBackground,onProgressUpdate方法 4.一个AsyncTask对象只能执行一次execute方法,否者会报运行时错误,在执行execute方法时,会调用到如下代码证明这个结论

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

5.在Android1.6之前,AsyncTask是串行执行任务,在1.6以后,改为在线程池里并行处理任务,在3.0以后,又改为在线程池里串行执行任务。

在AsyncTask中有两个线程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一个handler(InternalHandler). 其中SerialExecutor用于任务的排队。在上面代码中,我们已经看到,在AsyncTask.execute的执行过程中我们会调用它的execute方法

private static class SerialExecutor implements Executor { final ArrayDeque mTasks = new ArrayDeque(); 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() { if ((mActive = mTasks.poll()) != null) { THREAD_POOL_EXECUTOR.execute(mActive); } } }

我们看到首先会将FutureTask对象插入到队列mTask中,如果这个时候没有正在执行的AsyncTask活动,就会调用scheduleNext();执行下一个任务,当一个任务执行完成后又会执行下一个,从这可以看出AsyncTask默认是串行执行的

上述内容就是如何从源码层面解析android异步消息机制,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注行业资讯频道。

方法 就是 线程 任务 参数 进度 此方法 消息 对象 类型 源码 代码 后台 不同 信息 实例 是在 分析 成功 接下来 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 百单网互联网科技有限公司怎么样 湖南永感互联网科技有限公司 蓝思科技飞鸽互联网 河南真二互联网科技有限公司 互联网与科技金融数据 奇酷互联网络科技 广州凡科互联网科技 互联网金融 科技创新 镁科技互联网资讯平台 互联网科技素材视频竖屏 富国互联网科技创新基金 崇明区互联网教育科技售后服务 华夏恒生互联网科技013171 互联网科技大赛获奖感言 互联网科技如何创业 工业互联网山东科技大学 工业互联网科技成果转化 互联网科技包装股 西安左小互联网科技有限公司 利用互联网等科技手段 互联网科技工作委员会毛军 互联网科技活动banner 苏州大垚互联网科技 广州紫鲸互联网科技 长亮科技互联网金融 开启互联网科技时代小说 温州喔噻互联网科技有限公司 互联网公司黑科技创始人 广州燕处互联网科技有限公司 优雇互联网科技有限公司
0