AsyncTask异步加载的源码分析与实现实例


一 . AsyncTask

AndroidLazy Load主要体现在网络数据(图片)异步加载、数据库查询、复杂业务逻辑处理以及费时任务操作导致的异步处理等方面。在介绍Android开发过程中,异步处理这个常见的技术问题之前,我们简单回顾下Android开发过程中需要注意的几个地方。
Android应用开发过程中必须遵循单线程模型(Single Thread Model)的原则。因为AndroidUI操作并不是线程安全的,所以涉及UI的操作必须在UI线程中完成。但是并非所有的操作都能在主线程中进行,Google工程师在设计上约定,Android应用在5s内无响应的话会导致ANR(Application Not Response),这就要求开发者必须遵循两条法则:

1、不能阻塞UI线程,

2、确保只在UI线程中访问Android UI工具包。于是,开启子线程进行异步处理的技术方案应运而生

二   首先看一到AsyncTask的大纲视图 

我们可以看到关键几个步骤的方法都在其中,

2.1 doInBackground(Params... params)是一个抽象方法,我们继承AsyncTask时必须覆写此方法;

2.2 onPreExecute()、onProgressUpdate(Proess... values)、onPostExecute(Result result)、onCancelled()这几个方法体都是空的,我们需要的时候可以选择性的覆写它们;

2.3 publishProgress(Progress... values)是final修饰的,不能覆写,只能去调用,我们一般会在doInBackground(Params... params)中调用此方法;

2.4 我们可以看到有一个Status的枚举类和getStatus()方法,Status枚举类代码段如下

        //初始状态
	private volatile Status mStatus = Status.PENDING;
	public enum Status {
        /**
         * Indicates that the task has not been executed yet.
         */
        PENDING,
        /**
         * Indicates that the task is running.
         */
        RUNNING,
        /**
         * Indicates that {@link AsyncTask#onPostExecute} has finished.
         */
        FINISHED,
    }
/**
     * Returns the current status of this task.
     *
     * @return The current status.
     */
    public final Status getStatus() {
        return mStatus;
    }
2.4.1           AsyncTask的初始状态为PENDING,代表待定状态,

        RUNNING代表执行状态,

FINISHED代表结束状态,

这几种状态在AsyncTask一次生命周期内的很多地方被使用,非常重要

3 执行过程分析

3.1 一些变量声明的常识

public abstract class AsyncTask<Params, Progress, Result> {
//线程数据标签
    private static final String LOG_TAG = "AsyncTask";
//动态获取不同cpu允许开启的线程个数

    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;<span style="font-family: Helvetica, Tahoma, Arial, sans-serif; font-size: 14px; line-height: 25.2px;">// 当线程数大于核心时,终止当前多余的空闲线程等待新任务的最长时间10</span>

    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);

        public Thread newThread(Runnable r) {
            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
        }
    };

3.2 从 execute作为入口,进行分析执行过程

源码中:

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

3.3 executeOnExecutor()方法

    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        if (mStatus != Status.PENDING) {
            switch (mStatus) {
              //如果该任务正在被执行则抛出异常,值得一提的是,在调用cancel取消任务后,状态仍未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)");
            }
        }
//改变状态为RUNNING</span>
        mStatus = Status.RUNNING;

//调用onPreExecute方法
        onPreExecute();


        mWorker.mParams = params;

        exec.execute(mFuture);

        return this;
    }
代码中涉及陌生的变量:mWorker、mFuture

3.4关于exec,它是java.util.concurrent.ThreadPoolExecutor的实例,用于管理线程的执行

public interface Executor {

    /**
     * Executes the given command at some time in the future.  The command
     * may execute in a new thread, in a pooled thread, or in the calling
     * thread, at the discretion of the {@code Executor} implementation.
     *
     * @param command the runnable task
     * @throws RejectedExecutionException if this task cannot be
     * accepted for execution
     * @throws NullPointerException if command is null
     */
    void execute(Runnable command);
}

mWorker实际上是AsyncTask的一个的抽象内部类的实现对象实例,它实现了Callable<Result>接口中的call()方法,

而mFuture实际上是java.util.concurrent.FutureTask的实例,下面是它的FutureTask类的相关信息

/**
 * A cancellable asynchronous computation.
 * ...
 */
public class FutureTask<V> implements RunnableFuture<V> {

 private final WorkerRunnable<Params, Result> mWorker;
 private final FutureTask<Result> mFuture;
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
    }


 /**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     */
    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);
                }
            }
        };
    }

 private void postResultIfNotInvoked(Result result) {
        final boolean wasTaskInvoked = mTaskInvoked.get();
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }




4 概述

当我们调用execute(Params... params)方法后,execute方法会调用executeOnExecutor()方法,

然后由Executor实例执行一个execute任务,

这个过程中doInBackground(Params... params)将被调用,

如果被开发者覆写的doInBackground(Params... params)方法中调用了publishProgress(Progress... values)方法,

则通过InternalHandler实例sHandler发送一条MESSAGE_POST_PROGRESS消息,更新进度,sHandler处理消息时onProgressUpdate(Progress... values)方法将被调用;如果遇到异常,则发送一条MESSAGE_POST_CANCEL的消息,取消任务,sHandler处理消息时onCancelled()方法将被调用;如果执行成功,则发送一条MESSAGE_POST_RESULT的消息,显示结果,sHandler处理消息时onPostExecute(Result result)方法被调用。

经过上面的介绍,相信朋友们都已经认识到AsyncTask的本质了,它对Thread+Handler的良好封装,减少了开发者处理问题的复杂度,提高了开发效率

5 实例





import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;

/**
 * AsyncTask是对Thread+Handler良好的封装,在android.os.AsyncTask代码里仍然可以看到Thread和Handler的踪迹
 */

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /**
         * 初始化所有的控件
         */
        assignViews();

        /**
         * 为按钮设置点击事件
         */
        setButtonOnClick();

    }

    private TextView mTextView01;
    private ProgressBar mProgressBar02;
    private Button mButton03;

    private void assignViews() {
        mTextView01 = (TextView) findViewById(R.id.textView01);
        mProgressBar02 = (ProgressBar) findViewById(R.id.progressBar02);
        mButton03 = (Button) findViewById(R.id.button03);
    }

    private void setButtonOnClick() {
        mButton03.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                /**
                 * 调用
                 */
                ProgressBarAsyncTask asyncTask = new ProgressBarAsyncTask(mTextView01, mProgressBar02);
                asyncTask.execute(1000);
            }
        });
    }

    /**
     * AsyncTask直接继承于Object类,位置为android.os.AsyncTask
     * AsyncTask定义了三种泛型类型 Params,Progress和Result
     *          Params 启动任务执行的输入参数,比如HTTP请求的URL
     *          Progress 后台任务执行的百分比
     *          Result 后台执行任务最终返回的结果,比如String
     *onProgressUpdate(Progress…)   可以使用进度条增加用户体验度。 此方法在主线程执行,用于显示任务执行的进度。
     *onPreExecute()        这里是最终用户调用Excute时的接口,当任务执行之前开始调用此方法,可以在这里显示进度对话框。
     *onCancelled()             用户调用取消时,要做的操作
     *Task的实例必须在UI thread中创建;
     *execute方法必须在UI thread中调用
     */
    class ProgressBarAsyncTask extends AsyncTask<Integer, Integer, String> {

        private TextView textView;
        private ProgressBar progressBar;

        /**
         * 构造方法
         * @param textView
         * @param progressBar
         */
        public ProgressBarAsyncTask(TextView textView, ProgressBar progressBar) {
            super();
            this.textView = textView;
            this.progressBar = progressBar;
        }

        //该方法运行在UI线程当中,并且运行在UI线程当中 可以对UI空间进行设置
        @Override
        protected void onPreExecute() {
            textView.setText("开始执行异步线程");
        }
        /**
         * 这里的Integer参数对应AsyncTask中的第一个参数
         * 这里的String返回值对应AsyncTask的第三个参数
         * 该方法并不运行在UI线程当中,主要用于异步操作,所有在该方法中不能对UI当中的空间进行设置和修改
         * 但是可以调用publishProgress方法触发onProgressUpdate对UI进行操作
         */
        @Override
        protected String doInBackground(Integer... params) {

            int i = 0;
            for (i = 10; i <= 100; i += 10) {
                SystemClock.sleep(800);
                publishProgress(i);
            }
            return i + params[0].intValue() + "";
        }


        /**
         * 这里的String参数对应AsyncTask中的第三个参数(也就是接收doInBackground的返回值)
         * 在doInBackground方法执行结束之后在运行,并且运行在UI线程当中 可以对UI空间进行设置
         */
        @Override
        protected void onPostExecute(String result) {
            textView.setText("异步操作执行结束" + result);
        }


        /**
         * 这里的Intege参数对应AsyncTask中的第二个参数
         * 在doInBackground方法当中,,每次调用publishProgress方法都会触发onProgressUpdate执行
         * onProgressUpdate是在UI线程中执行,所有可以对UI空间进行操作
         */
        @Override
        protected void onProgressUpdate(Integer... values) {
            if (BuildConfig.DEBUG) Log.d("ProgressBarAsyncTask", "onprogressupdate");
            int vlaue = values[0];
            progressBar.setProgress(vlaue);
        }

        /**
         * 用户调用取消时,要做的操
         */
        @Override
        protected void onCancelled() {
            super.onCancelled();
        }
    }
}










早起的年轻人 CSDN认证博客专家 移动开发 项目管理 Java
只要用心去做,每一件事情还是有可能成功的,当然成功是没有界限的,只不过是达到自己心里的那个目标,公众号:我的大前端生涯,一个爱喝茶的程序员,通常会搞搞SpringBoot 、Herbinate、Mybatiys、Android、iOS、Flutter、Vue、小程序等.
©️2020 CSDN 皮肤主题: 代码科技 设计师:Amelia_0503 返回首页