AsyncTask 学习

AsyncTask 类常用于处理 Android 的异步任务。

本文概括AsyncTask 的使用和简单分析内部实现原理。

本文记录基于Android API 27。

要点

  • AsyncTask 的创建可以传入 Handler对象或 Looper对象,也可以不传任何对象;
  • AsyncTask 需要重写其 doInBackground()方法,此方法会在子线程中执行;
  • AsyncTask 的onPreExecute()会在任务正式启动前被调用,此方法会在主线程中执行;
  • AsyncTask 的 onPostExecute(Result result) 方法用于执行结果的发送,此方法会在主线程中执行。返回的result是doInBackground()方法的结果,如果任务被取消,此方法不会被调用;
  • AsyncTask 内部使用线程池执行后台任务,使用Handler机制传递消息;
  • AsyncTask 的实例对象只能被执行一次;
  • AsyncTask 对象可以通过调用executeOnExecutor()方法指定其执行的Executor实现类,方便线程池的控制;

分析

构造方法

先看一下AsyncTask的构造方法,构造方法有三个,分别是:

public AsyncTask() {
    this((Looper) null);
}
public AsyncTask(@Nullable Handler handler) {
    this(handler != null ? handler.getLooper() : null);
}
public AsyncTask(@Nullable Looper callbackLooper) {
    mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
        ? getMainHandler()
        : new Handler(callbackLooper);

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

可以看到,前面两个构造方法都是调用第三个构造方法的。

构造方法中,初始化了三个成员变量mHandler、mWorker和mFuture。

初始化mHandler时,会判断构造方法是否传入null、Handler对象或Looper对象,若有,则当前AsyncTask对象中的mHandler成员是主线程中的Handler对象(参考Handler的分析)。mHandler对象只有在getHandler()方法中被调用:

private Handler getHandler() {
    return mHandler;
}

而getHandler()方法只在postResult(Result result)方法中被调用:

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

在postResult(Result result)中,获得了一个Message对象并用当前的mHandler发送出去。

mWorker和mFuture是线程池相关的两个类的实例。

AsyncTask 的构造方法中的mWorker对象的call()方法,这个方法显示设置了当前任务栈状态,然后设置当前线程优先使用后台线程,调用doInBackground(mParams)方法,这个doInBackground(mParams)方法就是需要自行实现的方法。把doInBackground(mParams)得到的结果赋值给result,最后调用postResult(result)发送出去。这个postResult(result)就是构造方法中的mHandler的发送消息方法。

mFuture对象的done()方法,尝试把FutureTask实例的执行结果拿到,然后调用postResult(result)方法发送出去。在拿result的过程会判断当前任务是否被中断、抛出异常执行异常或被任务被取消,若有,则执行记录、抛出异常或发送空的result。

启动异步任务

构造好一个AsyncTask对象后,一般会调用其execute()方法来启动任务:

@MainThread
public final AsyncTask execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}

从源码可以看到,这个方法会执行在主线程中。如果在子线程中调用此方法,则子线程也会变成主线程。例如我这么写:

mTvFirst.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        new Thread(new Runnable() {
            @Override
            public void run() {

                AsyncTask asyncTask = new AsyncTask() {
                    @Override
                    protected Object doInBackground(Object[] objects) {
                        Log.d(LOG_TAG, "doInBackground当前线程是" + Thread.currentThread().getName());
                        return null;
                    }

                    @Override
                    protected void onPreExecute() {
                        Log.d(LOG_TAG, "onPreExecute当前线程是" + Thread.currentThread().getName());
                    }
                };
                asyncTask.execute();
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.d(LOG_TAG, "run当前线程是" + Thread.currentThread().getName());
            }
        }).run();
    }
});

点击该TextView后,打印出来的是:

04-17 04:25:32.478 5805-5805/com.erkang.gradlestudy D/EKwong: onPreExecute当前线程是main
04-17 04:25:32.481 5805-5885/com.erkang.gradlestudy D/EKwong: doInBackground当前线程是AsyncTask #1
04-17 04:25:37.479 5805-5805/com.erkang.gradlestudy D/EKwong: run当前线程是main

可以看到,启动AsyncTask的线程也是主线程,尽管是在子线程中调用的。

接下来我们看一下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();

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

    return this;
}

这个方法用了final关键字,目的是不让使用者进行更改。

方法中,先判断AsyncTask当前状态是否准备状态,如果在运行或已结束,则抛出异常。这说明同一个AsyncTask对象不能被启动两次,在运行中状态或已结束状态都不可再次调用。

然后把当前状态设置为运行中,回调onPreExecute()方法。onPreExecute()方法本身为空方法,在主线程中调用,使用者可以重写来实现自己想要的功能。

然后把参数赋值给mWorker,调用Executor的实现类对象sDefaultExecutor的execute(Runnable command)方法。最后把AsyncTask本身返回。

SerialExecutor

这时候,我们看一下这个sDefaultExecutor对象是什么来的,发现在代码里是一个SerialExecutor对象:

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

在这个SerialExecutor类中,有一个Runnable的队列mTasks和一个具体的Runnable对象mActive。

在它的execute(final Runnable r)方法中,先把本次execute()方法传进来的Runnable对象和一个scheduleNext()对象组装成一个 新的Runnable对象,放到Runnable的队列mTasks尾部。判断当前mActive是否为空,若为空,则调用scheduleNext()方法。

接下来看scheduleNext()方法,先从mTasks头部取出一个Runnable对象赋值给mActive,若取到的值不为空,则让THREAD_POOL_EXECUTOR执行这个mActive对象。

mActive对象的run方法分两步,第一步是调用传进来的Runnable实现类的run()方法,第二步是继续调用scheduleNext()方法。这是一个把mTasks队列遍历完的意思。刚才我们传进来的Runnable实现类是mFuture对象,那么就是在这里调用mFuture对象的run()方法了。

FutureTask

这时候我们打开mFuture的类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);
    }
}

开头这个是当前状态是否为NEW和系统的内存判断,finally后面也是一些系统的判断,我们着重看中间try里面的内容好了。

这里会调起FutureTask对象内部的callable成员的call()方法,如果调用成功,会把布尔值ran设置为true。从构造方法我们可以看到,这个callable成员就是AsyncTask中的mWorker对象。也就是线程池THREAD_POOL_EXECUTOR所激活的时候,会调用mWorker的call()方法。

在FutureTask的run()方法中的这个try的步骤后面,会对callable成员是否运行成功做判断,如果为true的话,调用set(result)。继续点进去这个在FutureTask的run()方法中的这个try的步骤后面,会对callable成员是否运行成功做判断,如果为true的话,调用set(result)方法看,对内存状态等进行判断后,调用了finishCompletion()方法。我们看一下这个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
}

在这里,我们找到了这个done()方法,也就是在AsyncTask的构造方法中初始化mFuture对象时复写的done()方法了。

看到这里,我们就知道,在AsyncTask构造方法中初始化的两个成员mWorker和mFuture,在某个任务中,是先执行mWorker的call()方法,然后才执行mFuture的done()方法。

InternalHandler

前面将了那么,在各个步骤中调用哪些方法,调用后怎么发送消息,那么,消息被哪个Handler接收呢?接收后怎么处理呢?我们顺着AsyncTask的构造方法,可以找到这个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;
        }
    }
}

可以看到,这个类会根据消息的类型,通知result去更新进度或结果。

关于

本文为简单的学习笔记,如有错误,请多多指出。
我的GitHub: https://github.com/EKwongChum

你可能感兴趣的:(AsyncTask 学习)