Talking about the working principle of AsyncTask in Android

  • 2021-12-13 09:30:37
  • OfStack

The catalog provides an overview of how AsyncTask works, the four core methods of AsyncTask using AsyncTask

Overview

In fact, Thread and Handler are encapsulated internally in AsyncTask. Although AsyncTask is very convenient to perform background tasks and update UI on the main thread, AsyncTask is not suitable for particularly time-consuming background operations. For particularly time-consuming tasks, I still recommend using thread pool. Ok, let's not say much. Let's look at the simple usage of AsyncTask first.

How to use AsyncTask

AsyncTask is an abstract generic class. Brief introduction 1. The code of its use is as follows:


package com.example.huangjialin.myapplication;


import android.os.AsyncTask;

import android.util.Log;


public class AsyncTaskTest extends AsyncTask<String, Object, Long>{


    @Override

    protected void onPreExecute() {

        super.onPreExecute();

        Log.i("AsyncTaskTest","--- Prepare to download ---");

    }



    @Override

    protected Long doInBackground(String... params) {



        Log.i("AsyncTaskTest","--- Downloading in the background ---");

        return null;

    }


    @Override

    protected void onProgressUpdate(Object... values) {

        super.onProgressUpdate(values);

        Log.i("AsyncTaskTest","--- While updating ---");

    }



    @Override

    protected void onPostExecute(Long aLong) {

        super.onPostExecute(aLong);

        Log.i("AsyncTaskTest","--- Download is complete, and the result is returned to the main thread --");

    }

}

Then call new AsyncTaskTest (). execute () in activity; It's OK... It's relatively simple to use, so we won't talk about how to use it here.

Four Core Methods of AsyncTask

1. onPreExecute (): This method is executed in the main thread and is called before asynchronous tasks are executed, which is generally used for some preparatory work.

2. doInBackground (String... params): This method is executed in a thread pool and is used to perform asynchronous tasks. In this method, the progress of the task can be updated by the publishProgress method, which calls the onProgressUpdate method, and the result of the task is returned to the onPostExecute method.

3. onProgressUpdate (Object... values): This method is executed in the main thread and is mainly used when the task progress is updated, and this method will be called.

4. onPostExecute (Long aLong): Executed in the main thread. After the asynchronous task is executed, this method will be called, and the parameters of this method will return the result for the background.

In addition to these methods, there is a less commonly used method, such as onCancelled (), which is called in the event that the asynchronous task is canceled.

Ok, the basic use of AsyncTask is introduced here. Let's get to the topic. Let's take a look at how AsyncTask works.

Working Principle of AsyncTask

Starting from execute, the source code is coming


@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) {

            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;

    }

For aspect analysis, I will take out the English annotation … source code can know from the above execute method internal call is executeOnExecutor () method. sDefaultExecutor is actually a serial thread pool. And the onPreExecute () method will be called here. Then look at this thread pool.


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

            }

        }

    }



public AsyncTask() {

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

                }

            }

        };

    }

From the above code, we can know that the execution of AsyncTask is queued, because there is a keyword synchronized, and the Params parameter of AsyncTask is encapsulated as FutureTask class, and FutureTask is a concurrent class, which acts as Runnable here. Then FutureTask will be handed over to execute method of SerialExecutor for processing, while executor method of SerialExecutor will first add FutureTask to mTasks queue. If there is no task at this time, scheduleNext () method will be called to execute the next task. If there is a task, scheduleNext () is finally called after execution; Perform the next task. Until all tasks are completed. There is one call () method in the construction method of AsyncTask, which will be executed by run method of FutureTask. So this call method will eventually be executed in the thread pool. This is where the doInBackground method is called. Let's take a good look at this call () method.


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;

            }

        };


private Result postResult(Result result) {

        @SuppressWarnings("unchecked")

        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,

                new AsyncTaskResult<Result>(this, result));

        message.sendToTarget();

        return result;

    }

mTaskInvoked. set (true); Indicates that the current task has already been executed. Next, the doInBackground method is executed, and finally the result is passed through postResult (result); Method. The message is sent in the postResult () method via sHandler, which has the following code:


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;

    }

Note: There are two thread pools in AsyncTask, one is SerialExecutor, and the other is THREAD_POOL_EXECUTOR, where the former is mainly queued for tasks, while the latter is really executing tasks.

There is also a method InternalHandler in AsyncTask, which is mainly used to switch the execution environment from the thread pool to the main thread.

The above is to talk about the details of the working principle of AsyncTask in Android. Please pay attention to other related articles on this site for more information about the working principle of AsyncTask in Android!


Related articles: