Android中的AsyncTask使用詳解

AsyncTask使用

AsyncTask是一個(gè)泛型類(lèi),它提供了Params,Progress,Result三個(gè)泛型參數(shù)。Params用來(lái)指定異步任務(wù)的參數(shù)類(lèi)型,Progress指定后臺(tái)任務(wù)執(zhí)行進(jìn)度的參數(shù)類(lèi)型,Result指定后臺(tái)任務(wù)返回的參數(shù)類(lèi)型,它有以下4個(gè)主要方法:

  • onPreExecute()主線(xiàn)程中執(zhí)行,開(kāi)始異步任務(wù)之前調(diào)用,用于準(zhǔn)備工作
  • doInBackground(Params...params) 執(zhí)行在線(xiàn)程池中,執(zhí)行異步任務(wù)的主要方法。它內(nèi)部可以調(diào)用publishProgress(Progress...values)來(lái)通知onProgressUpdate更新進(jìn)度,它還需要返回Result給onPostExecute(Result result)方法
  • onProgressUpdate(Progress...values) 主線(xiàn)程中執(zhí)行,主要用于更新進(jìn)度
  • onPostExecute(Result result) 主線(xiàn)程中執(zhí)行,異步任務(wù)執(zhí)行結(jié)束后調(diào)用

除此之外,它還提供了一個(gè)onCancelled()方法,當(dāng)異步任務(wù)被取消時(shí)此方法被調(diào)用,并且onPostExecute(Result result) 不再執(zhí)行。

典型的AsyncTask用法如下:

//自定義Task
class MyTask extends AsyncTask<String,Float, Bitmap> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }
        @Override
        protected Bitmap doInBackground(String... params) {
            InputStream is = null;
            ByteArrayOutputStream baos = null;
            byte[] data = null;
            try {
                HttpURLConnection connection = (HttpURLConnection)new URL(params[0]).openConnection();
                is = connection.getInputStream();
                int totalLength =0;
                totalLength = connection.getContentLength();
                baos = new ByteArrayOutputStream();
                byte[] buf = new byte[128];
                int length ;
                int count = 0;
                while ((length = is.read(buf)) != -1) {
                    baos.write(buf, 0, length);
                    count += length;
                    publishProgress(count * 1.0f / totalLength);
                }
                data = baos.toByteArray();
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                try {
                    baos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            Bitmap bitmap= BitmapFactory.decodeByteArray(data, 0, data.length);
            //Bitmap bitmap = BitmapFactory.decodeStream(is);//返回null
            Log.d("mytask", "BitMap is " + bitmap);
            return bitmap;
        }

        @Override
        protected void onProgressUpdate(Float... values) {
            Log.d("mytask", "Progress is " + values[0]*100 + "%");
        }
        @Override
        protected void onPostExecute(Bitmap bitmap) {
            MyImage.setImageBitmap(bitmap);
        }
    }
//主線(xiàn)程中調(diào)用task:
new MyTask().execute("http://192.168.1.105:8080/largeicon.png");

使用AsyncTask有幾點(diǎn)注意事項(xiàng):

  • Asynctask對(duì)象必須在主線(xiàn)程創(chuàng)建
  • execute方法必須在主線(xiàn)程調(diào)用
  • 不要在外部手動(dòng)調(diào)用AsyncTask的四個(gè)主要內(nèi)部方法
  • 一個(gè)AsyncTask對(duì)象只能執(zhí)行一次,否則會(huì)報(bào)錯(cuò)
  • AsyncTask的執(zhí)行除了execute之外,還有executeOnExecutor方法

AsyncTask源碼解析:

我們從execute方法作為入口,開(kāi)始一步一步看一下AsyncTask是怎樣工作的,execute源碼如下:

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

可以看到,調(diào)用AsyncTask的execute方法之后,除了我們傳入的參數(shù)之外,它另外加了一個(gè)sDefaultExecutor,這是一個(gè)調(diào)度任務(wù)用的串行線(xiàn)程池,稍后會(huì)詳細(xì)介紹,實(shí)際上執(zhí)行的是executeOnExecutor方法,源碼如下:

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

首先會(huì)進(jìn)入狀態(tài)判斷,如果任務(wù)已經(jīng)在執(zhí)行,或者執(zhí)行過(guò)了,會(huì)拋出異常,這也就是為什么一個(gè)AsyncTask只能執(zhí)行一次的原因了。

繼續(xù)往下,onPreExecute() 在這里執(zhí)行了,此時(shí)還沒(méi)有出現(xiàn)與線(xiàn)程相關(guān)的操作,所以onPreExecute()一定是運(yùn)行在主線(xiàn)程的。

下面的兩句就是執(zhí)行異步任務(wù)的核心了,首先將傳入的參數(shù)賦值給了mWorker.mParams,然后調(diào)用exec.execute(mFuture),這里的exec默認(rèn)情況下是sDefaultExecutor,這里調(diào)用了它的execute方法。

上面已經(jīng)提到了,sDefaultExecutor是一個(gè)執(zhí)行調(diào)度任務(wù)的線(xiàn)程池,它指向的是AsyncTask類(lèi)中的靜態(tài)變量,SERIAL_EXECUTOR,也就是說(shuō),所有的AsyncTask對(duì)象,都是通過(guò)一個(gè)線(xiàn)程池來(lái)調(diào)度的,它就是SERIAL_EXECUTOR

從名字就可以看出,SERIAL_EXECUTOR是串行的,源碼如下:

//定義SERIAL_EXECUTOR 
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

//SerialExecutor類(lèi):
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);
            }
        }
    }

逐步分析一下SerialExecutor 的內(nèi)容,首先定義了一個(gè)隊(duì)列mTasks,用來(lái)存放它接收到的任務(wù)。在execute方法中,首先在隊(duì)列mTasks的尾部添加一個(gè)任務(wù),這個(gè)任務(wù)的內(nèi)容包括兩個(gè)部分:

  1. 執(zhí)行參數(shù)Runnable對(duì)象run方法(這里的參數(shù)是mFuture,稍后會(huì)查看run方法的內(nèi)容)
  2. 執(zhí)行scheduleNext

注意,mTasks.offer(.....);這一步只是將任務(wù)添加到隊(duì)列的尾部,實(shí)際上并未執(zhí)行。

直到if (mActive == null),執(zhí)行任務(wù)之前會(huì)進(jìn)行判斷。如果這是AsyncTask對(duì)象的第一次執(zhí)行,那么條件成立,調(diào)用scheduleNext,在這里先將mActive指向下一個(gè)任務(wù),然后又調(diào)用了THREAD_POOL_EXECUTORexecute方法,在這里異步任務(wù)將得到最終的執(zhí)行。

注意synchronized 的存在,使得線(xiàn)程池只能有一個(gè)線(xiàn)程來(lái)執(zhí)行Runnable對(duì)象,從而保證了任務(wù)會(huì)按添加順序串行執(zhí)行。

異步任務(wù)開(kāi)始執(zhí)行以后,由于mTasks的任務(wù)中包含了scheduleNext,所以,只要隊(duì)列中還有任務(wù),執(zhí)行完一個(gè)之后會(huì)繼續(xù)執(zhí)行下一個(gè),直到任務(wù)全部執(zhí)行完畢。

這里我們先不分析THREAD_POOL_EXECUTOR,只需要知道,它是AsyncTask的最終執(zhí)行者。再回到mTasks,可以看到執(zhí)行任務(wù)時(shí)調(diào)用的是mFuture的run方法。

相關(guān)的關(guān)鍵代碼如下:

//AsyncTask構(gòu)造函數(shù)中對(duì)mWorker ,和mFuture賦值
//mWorker是一個(gè)Runnable對(duì)象,封裝了doInBackground和postResult方法,等待執(zhí)行。只有它內(nèi)部封裝的方法會(huì)在子線(xiàn)程執(zhí)行
mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {     ---------->###1
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
               //后臺(tái)任務(wù)執(zhí)行方法
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result);//該方法通過(guò)Handler將消息發(fā)送到主線(xiàn)程
                }
                return result;
            }
        };

        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
                 ......
        };

//mFuture 對(duì)應(yīng)FutureTask類(lèi),主要方法如下
//mFuture 賦值時(shí)調(diào)用此構(gòu)造函數(shù),將mWorker傳遞給了成員變量callable ;
 public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

//mFuture 的run方法
public void run() {
        if (state != NEW ||
            !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
            return;
        try {
//取出callable,實(shí)際上就是傳入的mWorker
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
//執(zhí)行mWorker的call方法,并返回結(jié)果,call方法在AsyncTask構(gòu)造函數(shù)中對(duì)mWorker賦值時(shí)覆寫(xiě),上文###1處
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
......
        }

到這里,整個(gè)邏輯就清楚了。 AsyncTask將doInBackground方法封裝到mWorker中 ,mFuture 又接管了mWorker 的執(zhí)行,最后將mFuture 交給THREAD_POOL_EXECUTOR的execute方法,最后doInBackground會(huì)在THREAD_POOL_EXECUTOR中運(yùn)行。

THREAD_POOL_EXECUTOR

通過(guò)名字我們也能大致猜到,這是一個(gè)線(xiàn)程池。它的創(chuàng)建代碼如下:

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;

實(shí)際上,AsyncTask中構(gòu)建了一個(gè)核心線(xiàn)程數(shù)等于CPU核心數(shù)+1,最大線(xiàn)程數(shù)為CPU核心數(shù)2倍的線(xiàn)程池,用來(lái)執(zhí)行AsyncTask任務(wù)。

通過(guò)上邊的分析,我們可以看到,雖然AsyncTask任務(wù)最終是在線(xiàn)程池中執(zhí)行的,但是由于SerialExecutor的存在,線(xiàn)程會(huì)被丟進(jìn)隊(duì)列中,從第一個(gè)開(kāi)始排隊(duì)執(zhí)行,所以仍然是串行的。

實(shí)際上Android在這里有過(guò)幾次變動(dòng),在Android1.6之前是串行的,1.6-3.0采用線(xiàn)程池并行處理,3.0以后又改為串行處理了,但是我們可以通過(guò)executeOnExecutor來(lái)指定AsyncTask使用線(xiàn)程池來(lái)并行處理:

new MyTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"http://192.168.1.105:8080/largeicon.png");
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,182評(píng)論 6 543
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,489評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 178,290評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,776評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,510評(píng)論 6 412
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,866評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,860評(píng)論 3 447
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 43,036評(píng)論 0 290
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,585評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,331評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,536評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,058評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,754評(píng)論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 35,154評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,469評(píng)論 1 295
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,273評(píng)論 3 399
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,505評(píng)論 2 379

推薦閱讀更多精彩內(nèi)容