Android浅析AsyncTask

Posted Q-CODER

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android浅析AsyncTask相关的知识,希望对你有一定的参考价值。

  本篇文章,主要谈谈线程间通信方式之一AsyncTask。会从源码的角度,进行简单的分析。

  依然根据之前的学习结构来学习这个点。

  1.是什么?

  2.怎么用?

  3.为什么?(包括原理和优缺点)

  细心的你,会发现上面的结构发生了变化。按照正常的顺序,不是应该为 What, Why , How 吗?Actually,这是根据我们平时学习深度调整了一下,我们一般在技术上,都是先知道了它是什么,然后就用起来了。对于为什么?这个可能很多攻城狮和程序媛会没有精力去学习(毕竟,还要打王者上分嘛)。但是如果我们解决了【为什么】这个问题,我们的内力就会大大地提升。

  好了,叨叨这么久。开始进入正题吧。


1.什么是AsyncTask

让我们来看看官方怎么定义的吧。

打开官网

映入眼帘的第一句话居然是?

 (我刚想好好地讲讲你,上个官网一查,被抛弃了,我。。。我。。。还要不要写这篇文章呢?)

思考了良久(其实也就几秒),我决定还是把它写写,毕竟理解原理这件事,是永远不会 Deprecated 的

那么 AsyncTask 到底是什么呢?

  

从构词法我们可以看出 sync synchronize (同步) 的缩写,a-是相反。所以 AsyncTask ,翻译就是[异步任务]的意思。

比较多的说法是:一个轻量级的用于处理异步任务的类。

2.怎么用AsyncTask

那怎么用呢?

使用场景:假如你需要从远程/本地先获取数据(妹子的照片),然后将数据(妹子)展示出来。

假设,我们直接在UI线程去获取数(mei)据(zi),如果数(mei)据(zi)少的话,可能还不会出现卡顿,但是像我这种,数(mei)据(zi)特别多的时候,就会直接引起ANR(5s无响应)。

这时候,就是 AsyncTask show time 了。

    private class RequestMeiZi extends AsyncTask<Uri,Integer,Long>
        @Override
        protected Long doInBackground(Uri... uris) 
            int count = uris.length;
            long totalSize = 0;
            for (int i = 0; i < count; i++)
                totalSize = totalSize+Downloader.downloadMeiZi(uris[i]);
                publishProgress((int) ((i / (float) count) * 100));
                if (isCancelled()) break;
            
            return totalSize;
        
    

  写好了这个类,那怎么用呢?

  (大哥们,别在评论里面要网址,我怕“你号没了”)

   那我们来分析一下这个类吧。这个类继承了AsyncTask,并重写了 doInBackground() 的方法。

可以看到它一共有三个参数,分别是【入参】,【进度】,【结果】。那么聪明如我的你,肯定知道后两个参数其实就是提供数据让我们更新UI的。

那么我们来看看,怎么用上后面两个的吧。

首先,在我们RequestMeiZi 这个类中按下【Ctrl】+【O】,我们就可以看到AsyncTask其他可重写的方法了。然后我们重写一下 onPreExecute(),onPostExecute(),onProgressUpdate() 的方法。

private class RequestMeiZi extends AsyncTask<Uri,Integer,Long>
        @Override
        protected void onPreExecute() 
            super.onPreExecute();
        

        @Override
        protected Long doInBackground(Uri... uris) 
            int count = uris.length;
            long totalSize = 0;
            for (int i = 0; i < count; i++)
                totalSize = totalSize+Downloader.downloadMeiZi(uris[i]);
                publishProgress((int) ((i / (float) count) * 100));
                if (isCancelled()) break;
            
            return totalSize;
        

        @Override
        protected void onPostExecute(Long aLong) 
            super.onPostExecute(aLong);
        

        @Override
        protected void onProgressUpdate(Integer... values) 
            super.onProgressUpdate(values);
        
    

我们发现,在 onPostExecute() onProgressUpdate() 的参数类型正好是我们再 AsyncTask<Uri,Integer,Long> 传入的参数类型。

那其实,在这里我们就使用完了 AsyncTask 。根据方法名,我们可以知道只有 doInBackground() 是在子线程中运行的,其他的都是在主线程(UI线程)中执行的。

如果暂时不想学习源码的东西,那么你就可以愉快地去玩耍了。


3.AsyncTask底层是怎么操作的?及其他注意事项

异步任务通过execute被调用,点击去看看

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

发现,这个方法直接调用了 executeOnExecutor(sDefaultExecutor, params)

 mFuture 是什么,我们暂时不细究,走到底会发现就是 Runnable Future

有“强迫症”的童鞋,点击这帮你解决FutureTask的疑惑

那接下来就是 doInBackground() 的方法

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

我们可以看到 mWorker 调用了 doInBackground() 的方法,并且将改方法的返回值通过 postResult() 的方法传回到主线程的。

那它是怎么传回主线程的呢?我们接着看,

 private Result postResult(Result result) 
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    

发现通过 handler 把信息传出去。那传到哪里呢?我们搜索一下

   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;
            
        
    

发现回到了这里,那在 MESSAGE_POST_RESULT ,执行了 result.mTask.finish(result.mData[0]) ,再往下看(看啥,看finish啊)


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

哦~发下这里执行了 onPostExecute(result)

那我们整理一下,

还有最后一个方法,就是 onProgressUpdate() 这个是什么时候执行的呢? 在我们的doInBackground中,我们发现了一个方法 publishProgress() ,那我们还是点进去看看吧。

   protected final void publishProgress(Progress... values) 
        if (!isCancelled()) 
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        
    

发现也是通过 handler 把信息传回到主线程,再上上个代码中,我们可以看到 MESSAGE_POST_PROGRESS 这个信息的处理,就是调用了 onProgressUpdate().

讲了那么多,你已经是不是在疯中凌乱了呢?

别慌,我把流程图给你整理出来了。请查收


其他注意事项:

1.AsyncTask的execute是串行执行的,如果想要并行的话,要调executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");

2.AsyncTask被抛弃的原因有:容易导致内存泄漏;忘记回调;横竖屏切换会导致崩溃;不同版本的AsyncTask的

AsyncTask was intended to enable proper and easy use of the UI thread. However, 
the most common use case was for integrating into UI, 
and that would cause Context leaks, missed callbacks,
or crashes on configuration changes. 
It also has inconsistent behavior on different versions of the platform, 
swallows exceptions from doInBackground, 
and does not provide much utility over using Executors directly.

PS:秉持着联机学习 1+1>2的原则,欢迎大家指点批评,互相交流学习~

以上是关于Android浅析AsyncTask的主要内容,如果未能解决你的问题,请参考以下文章

安卓学习笔记之AsyncTask机制浅析

AsyncTask浅析

JavaScript 闭包浅析

lua迭代器和泛型for浅析

如何提取ABAQUS实体单元柱截面内力

Android面试Android异步任务AsyncTask