美文网首页
AsyncTask的使用及原理解析

AsyncTask的使用及原理解析

作者: liuzhimi | 来源:发表于2019-03-22 20:51 被阅读0次

AsyncTask的使用及原理解析

Android应用在主线程中执行耗时任务时会导致ANR(Application Not Responing),因此使用子线程异步执行耗时任务是必要的。Android SDK中给我们提供了AsyncTask这一封装好的组件。
让我们来了解一下它。

AsyncTask的使用

以下代码是使用AsyncTask从网络异步加载一张图片的流程。

public class NetCacheUtil {

    private static NetCacheUtil instance;

    public static NetCacheUtil getInstance(){
        if (instance == null){
            synchronized (NetCacheUtil.class){
                if (instance == null)
                    instance = new NetCacheUtil();
            }
        }
        return instance;
    }

    public void getBitmapFromNet(String url, ImageView iv){
        MyAsyncTask asyncTask = new MyAsyncTask();
        asyncTask.execute(iv, url);
    }

    private class MyAsyncTask extends AsyncTask<Object, Void, Bitmap> {

        private ImageView ivPic;
        private String url;

        // 耗时任务执行之前 --主线程
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        // 后台执行的任务
        @Override
        protected Bitmap doInBackground(Object... params) {
            // 执行异步任务的时候,将URL传过来
            ivPic = (ImageView) params[0];
            url = (String) params[1];
            Bitmap bitmap = downloadBitmap(url);
            // 为了保证ImageView控件和URL一一对应,给ImageView设定一个标记
            ivPic.setTag(url);// 关联ivPic和URL

            return bitmap;
        }

        // 更新进度 --主线程
        @Override
        protected void onProgressUpdate(Void... values) {
            super.onProgressUpdate(values);
        }

        // 耗时任务执行之后--主线程
        @Override
        protected void onPostExecute(Bitmap result) {
            String mCurrentUrl = (String) ivPic.getTag();
            if (url.equals(mCurrentUrl)) {
                ivPic.setImageBitmap(result);
                System.out.println("从网络获取图片");
                // 从网络加载完之后,将图片保存到本地SD卡一份,保存到内存中一份
                localCacheUtil.getInstance().saveBitmapToLocal(url, result);
                // 从网络加载完之后,将图片保存到本地SD卡一份,保存到内存中一份
                memoryCacheUtil.getInstance().saveToMemory(url, result);
            }
        }
    }

    private Bitmap downloadBitmap(String url) {
        HttpURLConnection conn = null;
        try {
            URL mURL = new URL(url);
            // 打开HttpURLConnection连接
            conn = (HttpURLConnection) mURL.openConnection();
            // 设置参数
            conn.setConnectTimeout(5000);
            conn.setReadTimeout(5000);
            conn.setRequestMethod("GET");
            // 开启连接
            conn.connect();

            // 获得响应码
            int code = conn.getResponseCode();
            if (code == 200) {
                // 相应成功,获得网络返回来的输入流
                InputStream is = conn.getInputStream();

                // 图片的输入流获取成功之后,设置图片的压缩参数,将图片进行压缩
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inSampleSize = 2;// 将图片的宽高都压缩为原来的一半,在开发中此参数需要根据图片展示的大小来确定,否则可能展示的不正常
                options.inPreferredConfig = Bitmap.Config.RGB_565;// 这个压缩的最小

                // Bitmap bitmap = BitmapFactory.decodeStream(is);
                Bitmap bitmap = BitmapFactory.decodeStream(is, null, options);// 经过压缩的图片

                return bitmap;
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 断开连接
            conn.disconnect();
        }

        return null;
    }

}

要使用AsyncTask我们需要继承它,实现内部的抽象方法。
三个泛型,分别对应着传入参数类型,执行进度的类型,返回结果的类型。
-onPreExecute():耗时任务执行之前执行此方法(主线程)。
-doInBackground():耗时任务在此方法中执行(子线程)。
-onProgressUpdate():更新进度(主线程)。
-onPostExecute():耗时任务执行完成之后执行此方法(主线程)。

源码分析

先看一下成员变量:

    private static final String LOG_TAG = "AsyncTask";

    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    /**
     * 线程池核心线程数,最少2个最多4个。
     */
    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
    /**
     * 线程池最大线程数
     */
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    /**
     * 线程最大闲置时间
     */    
    private static final int KEEP_ALIVE_SECONDS = 30;
    /**
     * 线程工厂,用于创建新线程并计数。
     */
    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());
        }
    };
    /**
     * 容量为128的有序阻塞队列
     */
    private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);

    /**
     * An {@link Executor} that can be used to execute tasks in parallel.
     * 用来执行任务的线程池
     */
    public static final Executor THREAD_POOL_EXECUTOR;
    /**
     * 实例化执行任务的线程池
     */
    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }

    /**
     * 排队任务的线程池
     */
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    /**
     * 发送给Handler的数据
     */
    private static final int MESSAGE_POST_RESULT = 0x1;
    private static final int MESSAGE_POST_PROGRESS = 0x2;
    /**
     * 默认线程池设为排队任务线程池
     */
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    /**
     * InternalHandler为内部类,sHandler为主线程的Handler。
     */
    private static InternalHandler sHandler;
    /**
     * 一个抽象内部类,用来执行任务
     */
    private final WorkerRunnable<Params, Result> mWorker;
    /**
     * 处理结果任务
     */
    private final FutureTask<Result> mFuture;
    /**
     * AsyncTask状态
     */
    private volatile Status mStatus = Status.PENDING;
    /**
     * 当前任务是否被取消
     */    
    private final AtomicBoolean mCancelled = new AtomicBoolean();
    /**
     * 当前任务是否被调用
     */
    private final AtomicBoolean mTaskInvoked = new AtomicBoolean();

    private final Handler mHandler;


了解它们各自的作用后,我们再看看各个内部类,以及它们如何实现:

    /*
     * 排队任务线程池
     */
    private static class SerialExecutor implements Executor {
        /*
         * 双端队列,用来存放任务
         */
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        /*
         * 当前正在执行的任务
         */
        Runnable mActive;
        /*
         * 调用此任务时,将任务添加到mTask中
         */
        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);
            }
        }
    }

    /**
     * 枚举类型,指示AsyncTask状态
     */
    public enum Status {
        /**
         * Indicates that the task has not been executed yet.
         * 表示task还未被执行
         */
        PENDING,
        /**
         * Indicates that the task is running.
         * 表示task正在执行
         */
        RUNNING,
        /**
         * Indicates that {@link AsyncTask#onPostExecute} has finished.
         * task运行完成
         */
        FINISHED,
    }

    private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;

        AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task;
            mData = data;
        }
    }

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

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

在了解了AsyncTask的成员变量及内部类后我们就来看看一个AsyncTask实例化时都做了些什么:

    public AsyncTask(@Nullable Looper callbackLooper) {
        /*
         * 构造函数不传参数的话默认用 getMainHandler()方法获取Handler
         * 该方法的内部实现见之后第一个代码块
         */
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);
        
        //实例化
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);//该任务被调用,设置成true
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //执行耗时任务,并返回结果
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    //出现异常将该任务被取消,设置成true
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    //调用postResult方法,该方法内部实现见之后第二个代码块
                    postResult(result);
                }
                return result;
            }
        };

        //实例化
        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
                    //调用postResultIfNotInvoked方法,传入get方法返回值-mWorker的call方法返回值
                    //postResultIfNotInvoked方法内部实现见之后第三个代码块
                    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);
                }
            }
        };
    }

在调用postResultIfNotInvoked方法之后,最终会调用postResult方法,将result发送给handler进行处理。

    private static Handler getMainHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                //利用主线程的Looper创建Handler(也是主线程)
                sHandler = new InternalHandler(Looper.getMainLooper());
            }
            return sHandler;
        }
    }

这部分代码不懂的话可以看看Handler内部的实现,建议看看其它博客,后续我自己也会补上对Handler机制的分析。

    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();//将message发送给mHandler
        return result;
    }

postResult方法的作用就是获取一个msg.what为MESSAGE_POST_RESULT,msg.obj为AsyncTaskResult实例的message,然后将该message传给mHandler处理,至于怎么处理可以网上翻阅到内部类InternalHandler的实现。

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

获取到当前任务是否被调用,只有当前任务没有被调用时,才会调用postResult方法。

构造函数执行完成。我们再来看看调用execute方法会发生什么:

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

调用了executeOnExecutor方法,参数为等待任务线程池,以及传入参数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;
    }

1.首先判断当前task的状态,如果状态是 运行中 或者 完成 都会报错。这也是execute只能调用一次的主要原因。
2.判断通过后,将状态设置为运行中。
3.调用onPreExecute方法
4.将传入参数params传给mWorker
5.调用线程池的execute方法并传入mFuture。
6.执行完成

我们看看该线程池的execute方法都干了什么:

 /*
     * 排队任务线程池
     */
    private static class SerialExecutor implements Executor {
        /*
         * 双端队列,用来存放任务
         */
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        /*
         * 当前正在执行的任务
         */
        Runnable mActive;
        /*
         * 调用此任务时,将任务添加到mTask中
         */
        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);
            }
        }
    }

执行任务,将执行流程可见mWorker和mFuture的实例化(构造函数内)。执行完成后会发送给mHandler调用finish方法或者onProgressUpdate方法。
finish方法:

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

有参onCancelled方法会调用无参的onCancelled方法,该方法可以重写。
如果没有被取消的话则调用onPostExecute方法
最后将状态设置为完成。

第一次认真写博客,不够好的地方望包涵,同时也欢迎提出您的意见、问题。

相关文章

网友评论

      本文标题:AsyncTask的使用及原理解析

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