美文网首页
AsyncTask 学习

AsyncTask 学习

作者: E_Kwong | 来源:发表于2018-04-23 00:03 被阅读0次

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

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

    构造方法中,初始化了三个成员变量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<Result>(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<Params, Progress, Result> 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<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;
    }
    

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

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

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

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

    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类中,有一个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<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);
        }
    }
    

    开头这个是当前状态是否为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 学习

          本文链接:https://www.haomeiwen.com/subject/xlohlftx.html