美文网首页Android技术知识Android开发Android开发经验谈
Android源码解析之android异步任务AsyncTask

Android源码解析之android异步任务AsyncTask

作者: 大虾啊啊啊 | 来源:发表于2018-08-21 11:38 被阅读9次

我们都知道通过Handler可以实现异步任务,子线程通过Handler发消息给主线程,然后主线程执行相应的操作。在Android的异步任务体系中,还有一个非常重要的组件—AsyncTask。接下来我们先介绍AsyncTask的简单使用:

public class MainActivity extends AppCompatActivity {
    private TextView tvText;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tvText = (TextView) findViewById(R.id.tv_text);
        tvText.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new MyAsynTask().execute();
            }
        });
    }
    public class MyAsynTask extends AsyncTask<Integer,Integer,Integer> {
        private static final String TAG ="MyAsynTask" ;

        @Override
        protected Integer doInBackground(Integer... integers) {
            Log.e(TAG, "doInBackground: 正在执行异步任务..." );
            return 0;
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            Log.e(TAG, "doInBackground: 执行异步任务之前..." );
        }

        @Override
        protected void onPostExecute(Integer integer) {
            super.onPostExecute(integer);
            Log.e(TAG, "doInBackground: 执行异步任务之后..." );
        }
    }

}

看上面的代码可以知道,这个组件使用起来很简单,自定义MyAsynTask 类并继承AsyncTask类,然后实现三个方法:doInBackground(正在执行异步任务),onPreExecute(执行异步任务之前),onPostExecute(执行异步任务之后)。然后创建MyAsynTask 对象,调用execute方法。我们先看下AsyncTask的构造方法。

 /**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     *
     * @hide
     */
    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。分别是Handler,WorkerRunnable,FutureTask。Handler我们比较熟悉,用来在线程之间发送消息。WorkerRunnable,FutureTask两个对象比较陌生,主要是线程池相关的两个类,因为我们在这主要关注AsyncTask源码的流程,具体就不深究这两个对象。我们知道这两个类是和线程池相关的两个类即可。创建AsyncTask对象调用AsyncTask的构造方法之后,执行execute()方法,现在我们看下execute方法的实现。

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

execute方法,使用了@MainThread注解,所以我们知道这个方法要在主线程执行。所以当我们调用execute()方法的时候,不要在子线程调用,在UI线程调用该方法。接着调用了executeOnExecutor()方法,我们看下executeOnExecutor()方法的实现。

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

这个方法首先判断当前任务的状态:
1、如果是RUNNING,则说明任务正在执行,接着抛出Cannot execute task:the task is already running.的异常
2、如果是FINISHED,则说明任务已经完成,接着抛出Cannot execute task: the task has already been executed (a task can be executed only once).的异常。任务已经完成,一个任务只能执行一次。也说明了一个AsyncTask对象只能调用一次executed 方法。
我们来试验一下:

  private TextView tvText;
    private AsyncTask task;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tvText = (TextView) findViewById(R.id.tv_text);
        task = new MyAsynTask();
        tvText.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                task.execute();
                task.execute();
            }
        });
    }

我们在一个TextView的点击事件中调用了两次execute方法,然后我们会看到接着就会抛出如下异常:

08-21 10:37:56.711 3251-3251/com.mujin.keji.myapplication E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.mujin.keji.myapplication, PID: 3251
    java.lang.IllegalStateException: Cannot execute task: the task is already running.
        at android.os.AsyncTask.executeOnExecutor(AsyncTask.java:580)
        at android.os.AsyncTask.execute(AsyncTask.java:539)
        at com.mujin.keji.myapplication.MainActivity$1.onClick(MainActivity.java:24)
        at android.view.View.performClick(View.java:4780)
        at android.view.View$PerformClick.run(View.java:19866)
        at android.os.Handler.handleCallback(Handler.java:739)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:135)
        at android.app.ActivityThread.main(ActivityThread.java:5293)
        at java.lang.reflect.Method.invoke(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:372)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)

这也正证明了上面的结论。
我们继续回到上面的executeOnExecutor()方法,判断完任务状态之后,如果任务是第一次执行,那么就继续往下走

 mStatus = Status.RUNNING;

修改任务的状态变成正在执行,当如果再次调用的时候,此时的任务状态是正在执行,那么就会抛上面的异常。继续往下走调用了onPreExecute方法,当我们点进去该方法

  @MainThread
    protected void onPreExecute() {
    }

该方法是一个空方法,因为该方法由我们来实现。到了这里就完成了第一个回调方法onPreExecute,异步任务执行之前。接着我们继续往下走。

   exec.execute(mFuture);

传入和线程池相关的对象mFuture,exec调用execute方法。从方法名我们大概可以猜到,该方法就是去执行异步任务的意思。接着我们进入execute方法。exec是一个SerialExecutor对象,我们看下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类中,只有一个execute方法和一个Runnable集合(即任务列表)。execute方法中首先调用offer方法。我们进入offer方法

    /**
     * Inserts the specified element at the end of this deque.
     *
     * <p>This method is equivalent to {@link #offerLast}.
     *
     * @param e the element to add
     * @return {@code true} (as specified by {@link Queue#offer})
     * @throws NullPointerException if the specified element is null
     */
    public boolean offer(E e) {
        return offerLast(e);
    }

从注释我们可以看到Inserts the specified element at the end of this deque.,该方法的作用就是讲特定的原素插入到队尾。当然在这里的意思就是将异步任务插入到队尾。调用完offer方法之后,接着判断Runnable mActive对象是不是空的,如果是第一次执行,该对象为空,然后调用scheduleNext方法。在scheduleNext方法中

  protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }

首先从任务列表头部取出任务赋值给mActive,并判断任务是不是空的,如果不为空,调用THREAD_POOL_EXECUTOR.execute(mActive);方法,执行异步任务。THREAD_POOL_EXECUTOR在这里就是一个线程池对象。其实就是从线程池中取出一个线程,然后执行该任务。在执行任务的过程中会回调WorkerRunnable mWorker 对象中的Result 方法(在AsyncTask构造方法中初始化了mWorker对象),然后在方法体内我们看到执行了doInBackground方法,也就是我们重写的doInBackground方法(正在执行异步任务)

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

我们现在看下在AsyncTask构造方法中的第二个对象FutureTask mFuture 。

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

执行完异步任务之后回调done()方法,在这方法体又调用了postResultIfNotInvoked方法,接着我们继续看postResultIfNotInvoked方法。

   private void postResultIfNotInvoked(Result result) {
        final boolean wasTaskInvoked = mTaskInvoked.get();
        if (!wasTaskInvoked) {
            postResult(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方法。在postResult方法中我们看到很熟悉的对象Message message 和getHandler()方法。取出一条消息,然后调用sendToTarget方法发送消息。我们看下getHandler()方法。通过一步步往下走发现

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

在AsyncTask内部自定义了InternalHandler 对象,当执行完异步任务之后发送消息,然后会在InternalHandler 类的handleMessage方法体内处理消息。我们重点看下finish方法

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

首先判断任务是否已经取消,如果取消了,取消当前操作。如果没有取消,则调用onPostExecute方法,也就是我们实现onPostExecute方法(异步任务执行完成)。然后修改异步任务的状态。AsyncTask三个回调方法执行完毕,整个流程也执行完了。在这里我们知道AsyncTask是通过线程池+Handler来实现异步任务的。接下来我们做个简单的小结:
1.初始化线程池相关的对象和Handler对象。
2.通过线程池执行异步任务。
3.通过Handler发送消息通知异步任务执行完毕。

相关文章

网友评论

    本文标题:Android源码解析之android异步任务AsyncTask

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