4.AsyncTask使用,原理

资料

概述:

Android中,实现异步有两种方式,Handler和AsyncTask。
使用Handler+Thread时,每次执行耗时操作都需要创建新的线程进行处理,代码比较臃肿,性能开销也会较大。
使用AsyncTask,内部是封装好的Handler+Thread,并且使用了线程池,有效的优化了性能。

"AsyncTask并不适合进行特别耗时的后台任务,对特别耗时的任务,建议使用线程池。--开发艺术探索上所写,但是还并未理解"
可能是因为:多个任务时特别耗时的任务会影响后面的任务迟迟不能执行。用线程池并发的话,就没这样的问题了。

原理:

轻量级的异步任务类,在线程池中串行执行任务,通过Handler把进度和执行结果发送到主线程,更新UI。

简单使用:

 MyAsyncTask mAsyncTask = new MyAsyncTask();
mAsyncTask.execute();//下面后台任务参数为Void类型,所以不填参数。
 
/**
*AsyncTask<Void, Integer, Boolean>:泛型,可以为任意类型。
*分别为:后台任务执行的参数,更新进度的参数,任务执行完成的结果
*/
public class MyAsyncTask extends AsyncTask<String, Integer, Boolean>{
        private ProgressDialog mProgress;
        private int currentProgress;
        //任务开始前的准备操作,可以做些UI的准备或初始化。
        //多个任务时,无论是否等待,execute()的时候就在主线程调用
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            mProgress.setMax(100);
            mProgress.setMessage("下载进度");
            mProgress.show();
        }

        //线程池中执行任务
        @Override
        protected Boolean doInBackground(Void... params) {
            while (currentProgress <= 100) {
                //不能跟新UI
                //mProgress.setProgress(currentProgress);
                //想更新进度的话,主动调用该方法,会通过handler给onProgressUpdate方法。
                publishProgress(currentProgress);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                   return false;
                }
                currentProgress++;
            }
            return true;
        }

        //publishProgress通过handler调用,主线程
        @Override
        protected void onProgressUpdate(Integer... values) {
            mProgress.setMessage("下载进度:"+values[0]);
        }

        //执行完成后的结果。
        @Override
        protected void onPostExecute(Boolean aBoolean) {
            super.onPostExecute(aBoolean);
            if (aBoolean) {
                mProgress.setMessage("下载完成");
            }else {
                mProgress.setMessage("下载出错");
            }

                mProgress.cancel();
        }
        
        //中断任务时,调用该方法。但并不一定会马上中断。
        @Override
        protected void onCancelled() {
            super.onCancelled();
        }
    }

原理,源码分析:

按照调用方法,一步一步的分析。

  • 构造函数入手:
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);
                }
            }
        };
    }

好像并不能看出来什么,就两个实例对象初始化。这里记得就行,从内部逻辑上调用的方法名来看,一个mWorker跟后台任务有关;一个mFuture跟结果通知有关。这里看不懂正常,也不要每步都要求详细清楚,往下看。

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

方法2:    
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        //如果状态为正在运行或者已经结束,报错。说明,只能调用一次mAsyncTask.execute()方法。
        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;//改变状态

        //看好了哈,在此处调用。此时还是在主线程中哦,所以可以操作UI。
        onPreExecute();

        //构造方法中的一个实例,把执行后台任务的参数给了它。
        mWorker.mParams = params;
        //该exec是方法1中的sDefaultExecutor,看下面代码.
        exec.execute(mFuture);

        return this;
    }
    

1.只能执行一次mAsyncTask.execute()方法。
2.主线程调用onPreExecute()
3.更改状态,赋值。
4.调用线程池有关方法执行。sDefaultExecutor.execute(mFuture)
继续往下看,调用的方法:

    private static volatile Executor sDefaultExecutor = new SerialExecutor();
    //Executor?线程池类?
    private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;
        //mFuture参数,也即是构造方法中的实例
        public synchronized void execute(final Runnable r) {
            //封装成一个Runnable,放到队列的最后。
语句1:        mTasks.offer(new Runnable() {
                public void run() {
                    try {
                    //mFuture的run方法,只是调用方法,run并没有开新线程
语句5:              r.run();
                    } finally {
语句6:              scheduleNext();
                    }
                }
            });
            //第一次执行时
            if (mActive == null) {
语句2:             scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            //取出队列中第一个封装好的Runnable
语句3:     if ((mActive = mTasks.poll()) != null) {
                //THREAD_POOL_EXECUTOR是线程池
语句4:        THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

执行步骤,按语句顺序:将mFuture的run方法封装成Runnable对象放入队列最后--->第一次执行时,取出Runnable并到线程池中执行--->执行完以后,再取,再去执行。以此类推...
需要注意:封装好的Runnable在线程池中会开新的线程。mFuture的run只是一个方法,也就是说此时run中的代码已经在子线程了。
上面整个过程都是在放入/取出Runnable,最主要的是执行的mFuture的run方法。

//FutureTask类,mFuture的run方法
public void run() {
        if (state != NEW || !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
            return;
        try {
语句1:    Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
语句2:             result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
语句3:             set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

语句1中的callable是构造函数中初始化mFuture的用到的第一个实例mWorker啊。
语句2执行了mWorker的call方法。
语句3最后执行了mFuture的done方法。
语句2,3执行的正好是构造时两个实例复写的方法:先执行call,再执行done。
再回过头来看AsyncTask的构造方法:

    public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
语句0           mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
语句1:             result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
语句2:            postResult(result);
                }
                return result;
            }
        };

        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
语句3:           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);
                }
            }
        };
    }

语句1就是生成AsyncTask实例时复写的后台执行任务的方法:doInBackground() 在线程池中执行的。

    @WorkerThread
    protected abstract Result doInBackground(Params... params);

语句2拿到后台任务执行后的result,通过mainHandler调用onPostExecute(result);

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


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;
    }
    
    protected void onPostExecute(Result result) {
    }

另外,语句3的 postResultIfNotInvoked(get()),如果满足条件,也会调用postResult通过handler....
但是,正是因为语句0的设置,使其并不满足条件,所以不执行。(不太清楚wasTaskInvoked的作用)

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

至此,关于调用mAsyncTask.execute()方法,上面的源码分析完了,先把任务放入队列最后,然后取出队列第一个,在线程池中调用了doInBackground,后通过主线程的handler调用onPostExecute(Result result);

  • publishProgress:该方法可以再doInbackGroud中给onProgressUpdate更新进度。上面分析的过程中,程序并不会自动调用该方法,需要用到的时候,我们自己调用就行了。
    protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        }
    }
    
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;
            }
        }
        
protected void onProgressUpdate(Progress... values) {
    }

至此,整个AsyncTask执行过程就分析完了。跨度太大,总结一下:
1.onPreExecute()在mAsyncTask.execute(...params)执行的时候,马上执行,主线程。
2.doInBackgroun在线程池中调用。子线程。
3.得到结果后,通过主线程的handler发送消息,调用onPostExecute。主线程。
4.手动调用publishProgress会通过主线程的handler调用onProgressUpdate。 主线程。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容