相信各位对 AsyncTask 不会陌生,虽然它有如下弊端:

1. 如果在activiy内部new 一个AsyncTask, 横竖屏切换生成一个新的activity,等结果返回时,处理不好容易出现NPE。

2. 容易出现内存泄漏,如果AsyncTask 进行比较耗时的IO操作(网络操作, 打开一个文件等等),在activity onDestroy的时候没有cancel的话,

导致该Activity不能被GC回收(AsyncTask 在Activity内部执行耗时操作)。

3. 如果调用 executeOnExecutor, 如果等待queue里面的请求过多没有得到及时处理,容易造成RejectException, 具体原因我在我的博客已经有所介绍(AsyncTask RejectedExecutionException 小结)。

闲话少说, 本文的重点不在于介绍AsyncTask的优缺点,而是一直有一个问题困扰我,为什么AsyncTask 里面既能进行UI 操作,又能进行耗时的操作。

让我们从代码角度来分析这个问题, 首先看他的构造函数:

public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
}; 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 occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
public class FutureTask<V> implements RunnableFuture<V>
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;
}
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}

从上面的代码可以看出, mWorker 实际上就是一个  Callable, 而 mFuture 就是一个Thread, 构造函数中将Callable 作为参数传给了 FutureTask,下面

我们看看FutureTask 中的相关实现:

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 {
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);
}
}
 protected void set(V v) {
if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
outcome = v;
U.putOrderedInt(this, STATE, NORMAL); // final state
finishCompletion();
}
}
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (U.compareAndSwapObject(this, WAITERS, q, null)) {
for (;;) {
Thread t = q.thread;
if (t != null) {
q.thread = null;
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
} done(); callable = null; // to reduce footprint
}

可以看到 result = c.call(); 在run方法中被调用, 实际就是 result = doInBackground(mParams); 被调用,因为该方法是在子线程里面执行,所以可以执行耗时操作。 继续读代码,call-> set(result) -> finishCompletion->done()-> postResultIfNotInvoked-> postResult

 private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
 private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
} @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.wh所以at) {
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;
}
}
}

相信上面的代码大家都能看懂,值得说明的是,因为 InternalHandler的构造函数使用 mainlooper,所以 handleMessage 当然可以进行UI 操作。

继续看源码:

private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}

总结一下,虚函数 doInBackground 在Thread(FutureTask)run方法执行,所以能进行耗时操作,而InternalHanlder 通过获得mainlooper,在 handleMessage中调用 onPostExecute 从而保证了UI 操作可以在onPostExecute执行。这个过程实际就是模板模式。

AsyncTask 进行耗时操作和UI 更新的更多相关文章

  1. AsyncTask 处理耗时操作&&显示进度条

    在Android中实现异步任务机制有两种,Handler和AsyncTask.优缺点自己百度,推荐使用AsyncTask. private ProgressDialog dialog; //新建一个对 ...

  2. RxJava2-后台执行耗时操作,实时通知 UI 更新(一)

    一.前言 接触RxJava2已经很久了,也看了网上的很多文章,发现基本都是在对RxJava的基本思想介绍之后,再去对各个操作符进行分析,但是看了之后感觉过了不久就忘了. 偶然的机会看到了开源项目 Rx ...

  3. Winform 界面执行耗时操作--UI卡顿假死问题

    UI卡顿假死问题 误区1:使用不同的线程操作UI控件和耗时操作(即,跨线程操作UI控件CheckForIllegalCrossThreadCalls = false;), 注意:此处只是为了记录... ...

  4. iOS开发创建UI的耗时操作处理

    项目中有网络请求.读写操作等一系列耗时操作时,为了避免阻塞主线程,我们会把这些耗时操作放到子线程中去处理,当处理完成后,再回到主线程更新UI,这样就不会阻塞主线程.但是创建UI的时候一般都是在主线程中 ...

  5. C#.NET使用Task,await,async,异步执行控件耗时事件(event),不阻塞UI线程和不跨线程执行UI更新,以及其他方式比较

    使用Task,await,async,异步执行事件(event),不阻塞UI线程和不跨线程执行UI更新 使用Task,await,async 的异步模式 去执行事件(event) 解决不阻塞UI线程和 ...

  6. Android AsyncTask 异步任务操作

    1:activity_main.xml <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/androi ...

  7. android异步任务处理(网络等耗时操作)

    在实际应用中经常会遇到比较耗时任务的处理,比如网络连接,数据库操作等情况时,如果这些操作都是放在主线程(UI线程)中,则会造成UI的假死现象(android4.0后也不许放在UI线程),这可以使用As ...

  8. runloop事件、UI更新、observer与coranimation

    一.触摸事件派发与视图绘制打包 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ __dispatchPreprocessedEve ...

  9. 深入理解android的UI更新机制

    深入理解android的UI更新机制 由问题开始: 如何更新android UI? 可以通过如下方法: 在主线程里直接操作UI控件. handler.post(Runnable) runOnUiThr ...

随机推荐

  1. P2170 选学霸

    传送门 思路: ① 可以把每个学生都看作点,而那些实力相同的学生就处在同一个连通块内,因为连通块内的同学要么都取,要么不取,所以可以将连通块缩成一个点.只需用并查集维护每个连通块的大小. ② 接着采取 ...

  2. Oracle创建表、修改字段类型

    1.创建表 1.创建表 create table SCM_PER( --SCM_PER表名 ID ) primary key,--主键ID USERID ),--用户ID --Permission v ...

  3. 解决JS中取URL地址中的参数中文乱码

    GET请求会将中文编码,如果取出乱码的话,应该进行解码操作, 下面的函数是获取指定参数名的参数值,参数值可是中文.英文. function getQueryString(name) { var reg ...

  4. React文档(十九)不使用ES6

    通常你会将一个React组件定义成一个普通的js类: class Greeting extends React.Component { render() { return <h1>Hell ...

  5. OO第二单元多线程电梯总结分析

    一.概述 这一部分的作业考察的关注点与上一次的作业有所不同,上一次的考察重点主要集中在输入输出的判定以及多态的考察上面,而这一次是让我们进行多线程程序的调度与开发.这次开发过程中最大的感受就是自己之前 ...

  6. Mock.js常用占位符——Basic、Date、Color

    1. Basic 在通过占位符引用方法时, Mock.mock('@boolean')  : Mock.mock('@boolean()')  都可以调用方法 方法 含义 使用举例 Random.bo ...

  7. 搭建Java开发环境及相关工具

    本文主要记录下在window环境下,搭建Java开发环境,及需要安装的工具软件说明,旨在方便快速完成这些正真开始开发前的准备工作. 1.JDK安装及环境配置 此处安装JDK1.8的版本,官方下载地址: ...

  8. SpringBoot入门最详细教程

    monkey01 关注 2017.08.08 13:36* 字数 1479 阅读 34248评论 0喜欢 15 网上有很多springboot的入门教程,自己也因为项目要使用springboot,所以 ...

  9. leetcode感想

    想想要参加秋招了,重新开始刷leetcode,记录一下自己在过程遇到的问题. 算法优化: 1.合并if分支. 2.将所有可以直接给出结果的特殊情况放在最前面直接返回.

  10. python web1(解析url)

    环境:pycharm 尝试对地址进行切片 去掉头 http 或 https a.遇到了一些问题 url = 'https://www.cnblogs.com/derezzed/articles/811 ...