Android AsyncTask基礎(chǔ)

AsyncTask是Android為了簡化異步操作而封裝的異步任務(wù)操作抽象類。當(dāng)我們需要在程序中執(zhí)行耗時的異步操作時腰湾,我們可以考慮使用AsyncTask來實現(xiàn)大诸。

AsyncTask的使用比較簡單钧敞,由于AsyncTask是一個抽象類糊渊,我們需要寫一個類來繼承AsyncTask,繼承AsyncTask需要指定三個泛型參數(shù),參數(shù)分別表示為:
(1)Params:在執(zhí)行AsyncTask時需要傳入的參數(shù)销凑,比如我們輸入的String類型URL
(2)Progress:后臺任務(wù)執(zhí)行中返回的進度值類型丛晌,如果需要在界面上顯示當(dāng)前進度,則就用Progress指定的泛型作為進度單位斗幼。
(3)Result:耗時操作完成后返回的結(jié)果澎蛛,如果需要對結(jié)果進行返回,那么就用這里指定的泛型作為返回的類型孟岛。

實現(xiàn)AsyncTask類還需要重寫相應(yīng)的回調(diào)方法瓶竭,一般我們用到的有如下四個方法,他們都是自動被調(diào)用的渠羞,記得不要手動的調(diào)用:
(1)doInBackground(Void... params):必須重寫斤贰,異步執(zhí)行耗時操作
(2)onPreExecute():執(zhí)行耗時操作前被調(diào)用,通常用來完成一些初始化操作
(3)onProgressUpdate(Void... values):將doInBackground方法返回的值傳遞給該方法次询,在doInBackground中調(diào)用publishProgroess()方法可以且來更新進度
(4)onPostExecute(Void result):耗時的異步任務(wù)完成后回調(diào)該方法

需要說明一點:這幾個方法中荧恍,只有doInBackground方法是在子線程中執(zhí)行的,其他的方法都是在主線程中執(zhí)行的。

啟動和取消這個異步任務(wù)的方法分別是:
(1)myAsyncTask.execute() 其中該任務(wù)
(2)myAsyncTask.cancel() 取消該任務(wù)

我們已經(jīng)大概了解了AsyncTask的基礎(chǔ)知識送巡,接下來就以如下三個點做一些實例摹菠,加深一下印象。


這里寫圖片描述

(一)觀察AsyncTask子類方法的執(zhí)行順序
首先寫一個最簡單的AsyncTask子類骗爆,傳入的參數(shù)都是空值次氨, 然后在每一個方法中都打印出一句話,主要是為了便于觀察他們的執(zhí)行順序摘投。

package com.adan.asynctaskdome;

import android.os.AsyncTask;
import android.util.Log;

/**
 * @author: xiaolijuan
 * @description:
 * @projectName: AsyncTaskDome
 * @date: 2016-03-04
 * @time: 12:47
 */
public class MyAsyncTask extends AsyncTask<Void, Void, Void> {
    @Override
    protected Void doInBackground(Void... params) {
        Log.d("TAG", "doInBackground");
        return null;
    }

    @Override
    protected void onPreExecute() {
        Log.d("TAG", "onPreExecute");
        super.onPreExecute();
    }

    @Override
    protected void onPostExecute(Void aVoid) {
        Log.d("TAG", "onPostExecute");
        super.onPostExecute(aVoid);
    }

    @Override
    protected void onProgressUpdate(Void... values) {
        Log.d("TAG", "onProgressUpdate");
        super.onProgressUpdate(values);
    }
}

MainActivity的代碼

package com.adan.asynctaskdome;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;


public class MainActivity extends Activity implements View.OnClickListener {
    private Button button1, button2, button3;

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

        new MyAsyncTask().execute();
    }

}

運行結(jié)果:


這里寫圖片描述

這里的 方法沒有執(zhí)行煮寡,是因為我們需要在doInBackground(Void... params)中提供publishProgress(Progress...)方法用于顯示進度信息,如下所示:

package com.adan.asynctaskdome;

import android.os.AsyncTask;
import android.util.Log;

/**
 * @author: xiaolijuan
 * @description:
 * @projectName: AsyncTaskDome
 * @date: 2016-03-04
 * @time: 12:47
 */
public class MyAsyncTask extends AsyncTask<Void, Void, Void> {
    @Override
    protected Void doInBackground(Void... params) {
        Log.d("TAG", "doInBackground");
        publishProgress();
        return null;
    }

    @Override
    protected void onPreExecute() {
        Log.d("TAG", "onPreExecute");
        super.onPreExecute();
    }

    @Override
    protected void onPostExecute(Void aVoid) {
        Log.d("TAG", "onPostExecute");
        super.onPostExecute(aVoid);
    }

    @Override
    protected void onProgressUpdate(Void... values) {
        Log.d("TAG", "onProgressUpdate");
        super.onProgressUpdate(values);
    }
}

運行效果:


這里寫圖片描述

從打印的結(jié)果很容易就看出方法的執(zhí)行順序了吧犀呼,如下:

onPreExecute——>onProgressUpdate(調(diào)用了publishProgress方法才會調(diào)用該方法)——>doInBackground——>onPostExecute
(二)異步加載網(wǎng)絡(luò)圖片
首先一點先別忘記了幸撕,我們既然是要訪問網(wǎng)絡(luò),千萬不能忘記了在配置文件中添加網(wǎng)絡(luò)權(quán)限哦

<uses-permission android:name="android.permission.INTERNET" />

LoadBitmapActivity外臂,我們使用一個ImageView用來加載我們的這張網(wǎng)絡(luò)圖片的坐儿,然后再在中間放置一個進度條,用來顯示進程
LoadBitmapActivity

package com.adan.asynctaskdome;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

/**
 * @author: xiaolijuan
 * @description: 加載圖片
 * @projectName: AsyncTaskDome
 * @date: 2016-03-04
 * @time: 13:12
 */
public class LoadBitmapActivity extends Activity {
    private ImageView img;
    private ProgressBar progressBar;
    private static final String url = "https://www.baidu.com/img/bd_logo1.png";

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

        img = (ImageView) findViewById(R.id.img);
        progressBar = (ProgressBar) findViewById(R.id.progressbar);
        new MyAsyncTask().execute(url);
    }

    class MyAsyncTask extends AsyncTask<String, Void, Bitmap> {
        /**
         * 將url所對應(yīng)的圖像解析成Bitmap
         *
         * @param params
         * @return
         */
        protected Bitmap doInBackground(String... params) {
            String url = params[0];// 獲取傳遞進來的參數(shù)
            Bitmap bitmap = null;
            URLConnection connection;//定義網(wǎng)絡(luò)連接對象
            InputStream is;//用于獲取數(shù)據(jù)的輸入流
            try {
                //訪問網(wǎng)絡(luò)操作宋光,耗時操作
                connection = new URL(url).openConnection();//獲取網(wǎng)絡(luò)連接對象
                is = connection.getInputStream();//獲取輸入流
                BufferedInputStream bis = new BufferedInputStream(is);
                bitmap = BitmapFactory.decodeStream(bis);//通過decodeStream()方法解析輸入流貌矿,從而轉(zhuǎn)換成一張bitmap圖片
                is.close();
                bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            //將bitmap作為返回對象
            return bitmap;
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            //顯示進度條
            progressBar.setVisibility(View.VISIBLE);
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            try {
                Thread.sleep(2000);//為了觀看效果,休眠2秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            super.onPostExecute(bitmap);
            //關(guān)閉進度條和更新UI
            progressBar.setVisibility(View.GONE);
            img.setImageBitmap(bitmap);
        }
    }
}

activity_load_bitmap.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/img"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerInParent="true" />

    <ProgressBar
        android:id="@+id/progressbar"
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:layout_centerInParent="true" />

</RelativeLayout>

效果圖如下:


這里寫圖片描述

(三)顯示進度
ShowProgressActivity跃须,用來顯示這個進度條站叼,在這個類里面我們就使用AsyncTask讓進度條動起來娃兽。

package com.adan.asynctaskdome;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.ProgressBar;

/**
 * @author: xiaolijuan
 * @description: 顯示進度
 * @projectName: AsyncTaskDome
 * @date: 2016-03-04
 * @time: 13:12
 */
public class ShowProgressActivity extends Activity {
    private ProgressBar progressBar;
    private MyAsyncTask mTask;

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

        progressBar = (ProgressBar) findViewById(R.id.progressBar);
        mTask = new MyAsyncTask();
        mTask.execute();
    }

    class MyAsyncTask extends AsyncTask<Void, Integer, Void> {

        protected Void doInBackground(Void... params) {
            for (int i = 0; i < 100; i++) {
                publishProgress(i);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }

        //根據(jù)任務(wù)執(zhí)行情況更新UI菇民,其實就是更新進度條
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            // 進度條數(shù)值顯示
            progressBar.setProgress(values[0]);
        }

    }
}

activity_show_progress,布局文件很簡單投储,里面就放置一個橫向的進度條而已

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center">

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="5dp" />

</LinearLayout>

效果圖如下:


這里寫圖片描述

不知道大家發(fā)現(xiàn)一個問題沒第练,當(dāng)我第一次點擊界面時候,進度條還沒運行完玛荞,我就返回了娇掏,當(dāng)再一次進去時候,等待了好久進度條才有開始啟動起來勋眯。這是因為我們之前的doInBackground里的線程還在進行婴梧,循環(huán)還在繼續(xù),所以AsyncTask還在執(zhí)行客蹋。因此此刻重新進入進度條界面時塞蹭,就不可能立刻啟動新的AsyncTask,而舊的呢又因為之前我們返回到按鈕界面就沒法重新繪制了讶坯。所以只有等舊的AsyncTask執(zhí)行完畢番电,才會開啟新的。就像隊列一樣,只有運行完之后才運行下一個漱办。這時候你會想这刷,那就麻煩了,如果我需要大量地處理一些耗時操作呢娩井,這樣給用戶的體驗性是不好的暇屋。我們能不能在我們點擊返回的時候就取消了這個任務(wù)呢,答案當(dāng)然是可以的洞辣,只不過我們需要將AsyncTask與Activity的生命周期相關(guān)聯(lián)率碾。
我把修改后的代碼貼一下:

package com.adan.asynctaskdome;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.ProgressBar;

/**
 * @author: xiaolijuan
 * @description: 顯示進度
 * @projectName: AsyncTaskDome
 * @date: 2016-03-04
 * @time: 13:12
 */
public class ShowProgressActivity extends Activity {
    private ProgressBar progressBar;
    private MyAsyncTask mTask;

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

        progressBar = (ProgressBar) findViewById(R.id.progressBar);
        mTask = new MyAsyncTask();
        mTask.execute();
    }

    @Override
    protected void onPause() {
        // 判斷當(dāng)前異步任務(wù)是否在進行中
        if (mTask != null && mTask.getStatus() == AsyncTask.Status.RUNNING) {
            // cancel()方法:取消該任務(wù),但只是將對應(yīng)的AsyncTask狀態(tài)標(biāo)記為cancel狀態(tài),并沒有真正取消異步操作
            mTask.cancel(true);
        }
        super.onPause();
    }

    class MyAsyncTask extends AsyncTask<Void, Integer, Void> {

        protected Void doInBackground(Void... params) {
            for (int i = 0; i < 100; i++) {
                if (isCancelled()) {//如果異步任務(wù)為取消狀態(tài)屋彪,立刻break所宰,跳出循環(huán)
                    break;
                }
                publishProgress(i);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }

        //根據(jù)任務(wù)執(zhí)行情況更新UI,其實就是更新進度條
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            // 進度條數(shù)值顯示
            progressBar.setProgress(values[0]);
        }

    }
}

接下來我們再看看效果圖:


這里寫圖片描述

最后我們來總結(jié)一下使用AsyncTask類需要注意的幾點:
(1)AsyncTask實例必須在UI Thread中創(chuàng)建畜挥;
(2)execute()方法也必須在UI Thread中調(diào)用仔粥;
(3)cancel()方法:取消該任務(wù),該方法只是將對應(yīng)的AsyncTask狀態(tài)標(biāo)記為cancel狀態(tài),并沒有真正取消異步操作蟹但;
(4)每個AsyncTask的execute()方法只能被執(zhí)行一次躯泰,否則多次調(diào)用時將會出現(xiàn)異常;
(5)重寫的四個方法是系統(tǒng)自動調(diào)用华糖,不能手動更改麦向;

如果存在理解偏差甚至錯誤的地方,請多多交流指正客叉!謝謝各位诵竭!
點擊下載源代碼:Android AsyncTask基礎(chǔ)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市兼搏,隨后出現(xiàn)的幾起案子卵慰,更是在濱河造成了極大的恐慌,老刑警劉巖佛呻,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件裳朋,死亡現(xiàn)場離奇詭異,居然都是意外死亡吓著,警方通過查閱死者的電腦和手機鲤嫡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來绑莺,“玉大人暖眼,你說我怎么就攤上這事∥伤海” “怎么了罢荡?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我区赵,道長惭缰,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任笼才,我火速辦了婚禮漱受,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘骡送。我一直安慰自己昂羡,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布摔踱。 她就那樣靜靜地躺著虐先,像睡著了一般。 火紅的嫁衣襯著肌膚如雪派敷。 梳的紋絲不亂的頭發(fā)上蛹批,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天,我揣著相機與錄音篮愉,去河邊找鬼腐芍。 笑死,一個胖子當(dāng)著我的面吹牛试躏,可吹牛的內(nèi)容都是我干的猪勇。 我是一名探鬼主播,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼颠蕴,長吁一口氣:“原來是場噩夢啊……” “哼泣刹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起裁替,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤项玛,失蹤者是張志新(化名)和其女友劉穎貌笨,沒想到半個月后弱判,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡锥惋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年昌腰,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片膀跌。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡遭商,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出捅伤,到底是詐尸還是另有隱情劫流,我是刑警寧澤,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站祠汇,受9級特大地震影響仍秤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜可很,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一诗力、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧我抠,春花似錦苇本、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至纳鼎,卻和暖如春康栈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背喷橙。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工啥么, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人贰逾。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓庶柿,卻偏偏與公主長得像箕昭,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,047評論 2 355

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