Android实践今日看点Android知识

从源码解析AsyncTask注意事项

2016-11-02  本文已影响125人  皮球侧飞

AsyncTask是Android中除Handler外另一种方便处理耗时任务后的执行UI操作的工具类。其实这个说法本身有一定歧义,因为TsyncTask内部原理还是用到了Handler,可以说TsyncTask是Handler和线程使用的封装后的工具。
AsyncTask有以下几个注意事项

注意事项:

1.类的加载需要在主线程中
2.对象要在主线程中创建
3.execute在主线程中执行
4.不直接调用onPostExecute,onProgressUpdate,onPostExecute,doInBackground等方法
5.execute只能执行一次
6.Android3.0以后串行执行

源码解析

这些注意事项后续都会结合AsyncTask的源码进行解析。首先来看构造方法:

public AsyncTask() {
    mWorker = new WorkerRunnable<Params, Result>() {
        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<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);
            }
        }
    };
}

mWorker是一个WorkerRunnable,WorkerRunnable是一个实现Callable的抽象类,内部定义了Params[] mParams;变量。这个mWorker其实就是执行耗时任务的子线程。mFture就是mWorker的FutureTask。AsyncTask的执行需要调用execute()方法,然后调用了executeOnExecutor方法。

 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();

    mWorker.mParams = params;
    exec.execute(mFuture);

    return this;
}

首先会判断AsyncTask的mStatus,RUNNING和FINISHED都会报错,一旦调用该方法状态就会是RUNNING,AsyncTask执行完后mStatus就会是FINISHED,这就解释了注意事项5:execute只能执行一次。onPreExecute()方法需要执行在doInBackground之前的UI操作需要在UI线程中,所以executeOnExecutor方法必须要在UI线程中。这就解释了注意事项3:execute在主线程中执行。sDefaultExecutor的execute(mFuture)继续执行异步类的执行操作。sDefaultExecutor是SerialExecutor对象,从命名上可以猜测sDefaultExecutor的一个单执行的串行线程池。还是从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() {
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

SerialExecutor 内部定义了名为mTasks的ArrayDeque来存储线程,可以从execute方法中看出,每次执行SerialExecutor 只是从mTasks按顺序取出一个线程转交给THREAD_POOL_EXECUTOR去执行。可以看出AsyncTask有2个线程池分别是SerialExecutor和THREAD_POOL_EXECUTOR。SerialExecutor不做具体的线程执行操作而是每次取出一个线程给THREAD_POOL_EXECUTOR只做为“排队线程池“”使用,而THREAD_POOL_EXECUTOR才是执行线程操作的“执行线程池”。这就解释了注意事项:6THREAD_POOL_EXECUTOR是AsyncTask的静态变量。

public static final Executor THREAD_POOL_EXECUTOR
        = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;

从上述参数可以知道THREAD_POOL_EXECUTOR的核心线程数的CPU的量,最大线程数的2倍CPU数量+1,非核心线程的闲置超时时长为1秒。
THREAD_POOL_EXECUTOR执行的构造方法的mFuture。这就需要重新回看构造方法,主要分析下mWorker:

mWorker = new WorkerRunnable<Params, Result>() {
        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);
        }
    };

这里可以发现doInBackground()方法,所以doInBackground()本质上还是运行在子线程中去执行耗时任务。doInBackground()方法执行完之后应该需要将UI数据传给UI线程去更新UI,postResult()方法接受了result数据,所以这里应该就说执行向UI线程传递的任务。看下postResult()是如何实现该功能的。

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

Handler!AsyncTask的消息传递也是使用的Handler。既然用的Handler,如果要做UI操作,Handler必须要在主线程中创建。而sHandler是静态成员变量,在类加载的时候已经初始化了,这就解释了注意事项1:类的加载需要在主线程中

private static InternalHandler sHandler = new InternalHandler();

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.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;
        }
    }
}

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

这里Handler做了2个处理,一是更新精度,这个msg.what的通过publishProgress()方法触发;二是处理耗时计算完成或者结束的情况。执行结束的话就会调用result.mTask.onPostExecute()方法。result.mTask就是AsyncTask。onPostExecute()方法需要在主线程中调用,这解释了注意事项2:AsyncTask对象要在主线程中创建。至于注意事项4:不直接调用onPostExecute,onProgressUpdate,onPostExecute,doInBackground等方法。这个比较好理解,因为这些方法在AsyncTask内部都已经调用了,手动调用会打乱AsyncTask的工作流程。

潜在问题

AsyncTask的内存泄漏原理和Handler类似。具体处理就不详细说明了。有些读者可能说在Activity的onDestory()中将AsyncTask取消掉,这里就讲些下AsyncTask的取消AsyncTask.cancel(mayInterruptIfRunning);。调用cancel方法并不能真正立即把task取消掉,而只是把task的状态置为Cancel而已,可以通过isCancelled()方法来判断,然后在doingbackground或其他方法中判断是否被取消,然后做相应的处理,具体根据开发者需要去编写。
需要注意的是调用的cancel方法不会执行onPostExecute(result),而是onCancelled(result)。另一个问题就是在屏幕旋转等造成Activity重新创建时AsyncTask数据丢失的问题。当Activity销毁并创新创建后,还在运行的AsyncTask会持有一个Activity的非法引用即之前的Activity实例。导致onPostExecute()没有任何作用。

上一篇 下一篇

猜你喜欢

热点阅读