AsyncTask 详解与思考

使用

先上一段熟悉的代码,AsyncTask 的使用

MyAsyncTask task = new MyAsyncTask();
task.execute("1");

private class MyAsyncTask extends AsyncTask<String, Void, String> {

    @Override
    protected void onPreExecute() {
        Log.e("TAG", "onPreExecute THREAD:" + Thread.currentThread());
        super.onPreExecute();
    }

    @Override
    protected String doInBackground(String... params) {
        Log.e("TAG", "doInBackground start " + params[0] + " THREAD:" + Thread.currentThread());
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Log.e("TAG", "doInBackground end");
        return "1";
    }

    @Override
    protected void onPostExecute(String s) {
        Log.e("TAG", "onPostExecute:" + s + " THREAD:" + Thread.currentThread());
    }
}

以上代码的输出:
onPreExecute THREAD:Thread[main,5,main]
doInBackground start 1 THREAD:Thread[AsyncTask #3,5,main]
doInBackground end
onPostExecute:1 THREAD:Thread[main,5,main]

可以看到 doInBackground 在子线程中执行,onPostExecute 在主线程执行。

源码分析

先从源码分析一下整个执行的流程(以下源码来自 api-25)

AsyncTask 中重要的列举几个成员变量:
THREAD_POOL_EXECUTOR : 内部线程池
sDefaultExecutor : 负责排队取方法执行的
sHandler : 内部 Handler, 用于切换主线程的
mWorker : 封装了 doInBackground 方法,mWorker 的 call 方法中调用 doInBackground, 执行完后调用 postResult
mFuture : 封装了 mWorker, 在 mFuture 的 run 方法中调用 mWorker.call, 执行完后再处理一些

AsyncTask 的构造函数
public AsyncTask() {
    // 初始化 mWorker, 后面执行时候会用到
    mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {
            mTaskInvoked.set(true);
            Result result = null;
            try {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                // 实际执行 doInBackground
                result = doInBackground(mParams);
                Binder.flushPendingCommands();
            } catch (Throwable tr) {
                mCancelled.set(true);
                throw tr;
            } finally {
                // 处理返回结果,切换主线程到 onPostResult
                postResult(result);
            }
            return result;
        }
    };

    mFuture = new FutureTask<Result>(mWorker) {
        @Override
        protected void done() {
            // ... 意外处理
        }
    };
}

// 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) {
        // ...
    }
    mStatus = Status.RUNNING;
    // 我们可以实现的 onPreExecute 方法,这时还没有切换线程,所以该方法是在主线程执行
    onPreExecute();
    // 开始执行 doInBackground 方法
    mWorker.mParams = params;
    exec.execute(mFuture);

    return this;
}

// 上面的 sDefaultExecutor 是下面这个类:
private static class SerialExecutor implements Executor {
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    Runnable mActive;
    // 这里的参数 r 就是 mFuture
    public synchronized void execute(final Runnable r) {
        // 挨个从队列中取出,一个执行完再取下一个
        mTasks.offer(new Runnable() {
            public void run() {
                try {
                    // 执行 mFuture.run(),方法实现看下一段代码
                    r.run();
                } finally {
                    scheduleNext();
                }
            }
        });
        if (mActive == null) {
            scheduleNext();
        }
    }
    protected synchronized void scheduleNext() {
        if ((mActive = mTasks.poll()) != null) {
            // 子线程实际开始执行,THREAD_POOL_EXECUTOR 是线程池
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

// FutureTask 的 run 方法
public void run() {
    // ...
    try {
        // callable 是 AsyncTask 的 mWorker
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                // 执行 mWorker.call(), mWorker 在 AsyncTask 的构造函数中重写了 call 方法,
                // 里面执行了 result = doInBackground(mParams); 这个就是我们必须实现的方法
                // 接下来看 postResult(result);
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                // ...
            }
            if (ran)
                set(result);
        }
    } finally {
        // ...
    }
}

// mWorker 执行完 doInBackground 后
private Result postResult(Result result) {
    // getHandler() 返回的是内部 Handler 类 InternalHandler, 
    // 这里处理了 MESSAGE_POST_RESULT : result.mTask.finish(result.mData[0]); 
    // finish 就是调用 onPostResult, 所以切主线程就是在这里
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}

思考

到这里源码部分已分析完成,有几个值得思考的问题

1. AsyncTask 不同版本的区分

AsyncTask 版本变动较大是从 api-11(3.0) 之后,上面源码分析的是来自 api-25, 是更新后的版本,多次调用 execute 对于同一个 AsyncTask 会执行完一个再执行下一个,但是 api-10(包括) 之前的版本是并行执行的,调用 exceute 内部的线程池就去执行(除非线程池满需排队等待)。下面来看一下 api-10 的源码处理

public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    //...
    onPreExecute();
    // sExecutor 执行,sExecutor 是线程池,直接执行了 call 方法,没有去排队
    mWorker.mParams = params;
    sExecutor.execute(mFuture);
    return this;
}
// sExecutor, 核心线程数量是 5, 最大线程数是 128. 
// 而新版本的 AsyncTask 核心线程数是 Math.max(2, Math.min(CPU_COUNT - 1, 4)), 最大线程数是 CPU_COUNT * 2 + 1
private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
        MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);
2. AsyncTask 是否可以取消

AsyncTask 提供了 cancel 方法来取消,需传一个 boolean 类型的参数,true 表示会 intercept 中断 doInBackground, false 不会中断 doInBackground, 它会完整地执行完。true/false 都不会再执行 onPostResult 方法。

3. AsyncTask 的缺点
  1. 内部类形式时可能持有外部类引用,导致内存泄漏或异常
    –> 解决方法:改为静态内部类
  2. Activity 已被销毁,doInBackground 还没有执行完,执行完后再执行 onPostResult, 导致产生异常
    –> 解决方法:Activity 的 onDestroy 方法中调 cancel 方法取消 AsyncTask
  3. AsyncTask 内部封装了线程池,比较适合多次执行的时候使用,当仅需要在子线程执行一些简单操作时使用 AsyncTask 会浪费消耗。
    –> 解决方法:根据场景决定是否使用 AsyncTask
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值