3.7 AnsyncTask異步任務

標注:本文為個人學習使用梗肝,僅做自己學習參考使用姐叁,請勿轉載和轉發(fā)
2018-07-10: 初稿,小冰塊加可樂真好延柠。參考博主coder-pig
2018-08-09: 二稿祸挪,在快傳的Demo中遇見的AnsyncTask的方法,重新溫習一下

0. 引言

  • 本節(jié)主要講述的為Android提供的一個輕量級的用于處理異步任務的類:AnsyncTask
  • 我們一般是繼承AsyncTask贞间,然后在類中實現(xiàn)異步操作贿条,然后將異步執(zhí)行的進度,反饋給UI主線程
參考文獻
  1. AnsyncTask異步任務
  2. Android 多線程-----AsyncTask詳解

1. 相關概念

1.1 多線程的概念
  1. 應用程序(Application):為了完成特定任務增热,用某種語言編寫的一組指令集合(一組靜態(tài)代碼)
  2. 進程(Process) :運行中的程序整以,系統(tǒng)調度與資源分配的一個獨立單位,操作系統(tǒng)會為每個進程分配 一段內存空間峻仇,程序的依次動態(tài)執(zhí)行公黑,經理代碼加載 -> 執(zhí)行 -> 執(zhí)行完畢的完整過程!
  3. 線程(Thread):比進程更小的執(zhí)行單元,每個進程可能有多條線程凡蚜,線程需要放在一個進程中才能執(zhí)行人断! 線程是由程序負責管理的!3恶迈!而進程則是由系統(tǒng)進行調度的!F状肌暇仲!
  4. 多線程概念(Multithreading):并行地執(zhí)行多條指令,將CPU的時間片按照調度算法副渴,分配給各個線程熔吗,實際上是分時執(zhí)行的,只是這個切換的時間很短佳晶,用戶感覺是同時而已!
  • 舉一個簡單的例子:
    你掛著QQ讼载,突然想聽歌轿秧,你需要關掉QQ,然后再去啟動音樂播放器么咨堤?答案是否定的菇篡,我們直接打開播放器放歌就好,QQ還在運行著一喘,是吧驱还!這個就是簡單的多線程。
  • 實際開發(fā)過程中凸克,想后臺更新议蟆,這個時候一般我們會開辟出一條后臺線程,用于下載萎战,apk咐容,但是這個時候我們會開辟出一條后臺線程,用于下載新版本的apk蚂维,但是這個時候我們還可以使用應用中的其他功能戳粒。
1.2 同步與異步
  • 同步:當我們執(zhí)行某個功能時,在沒有得到結果之前虫啥,這個調用就不能返回蔚约!簡單點就是必須等前一件事做完了才能做下一件事。
  • 異步:和同步是相對的涂籽,當我們執(zhí)行某個功能后苹祟,我們并不需要立即得到結果,噩夢可以正常的做其他工作,這個功能可以在完成后通知活著回調來告訴我們苔咪,還是上面那個后臺下載的例子锰悼,后臺下載,我們執(zhí)行下載功能之后团赏,我們就無需關心它的下載過程箕般,當下載結束之后通知我們就可以了。
1.3 Android為什么要引入異步任務
  • 因為Android程序剛剛啟動時舔清,會同時啟動一個對應的主線程(Main Thread)丝里,這個主線程主要負責處理與UI相關聯(lián)的事件,也稱作為UI線程体谒!
  • 而在Android的App時我們必須遵守這個單線程模型的規(guī)則杯聚,Android的UI操作并不是線程安全的,并且這些操作都需要在UI線程中執(zhí)行抒痒!
  • 假如我們在非UI線程中幌绍,比如主線程中new Thread()另外開辟一個線程,然后直接在里面修改UI控件的值故响,會拋出異常:android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views
  • 如果我們把耗時操作都放在UI線程中的話傀广,如果UI線程超過5秒沒有響應用于請求,那么這個時候會引發(fā)ANR(Application Not Responding)異常彩届,就是應用無響應
  • Android4.0之后禁止在UI線程中執(zhí)行網絡操作伪冰,不然會報錯:android.os.NetworkOnMainThreadException

以上種種原因都說明了Adnroid引入異步任務的意義,AsyncTask是實現(xiàn)異步的一種方法

  1. 前面學習的Handler樟蠕,在Handler中寫好UI的更新贮聂,然后通過sendMessage()等方法通知UI更新,另外別忘了Handler寫在主線程和子線程中的區(qū)別哈
  2. 利用Activity.runOnUiThread(Runnable)把更新的ui的代碼創(chuàng)建在Runnable中寨辩,更新UI時吓懈,把Runnabel對象傳進來即可

2. AsyncTask解析

2.1 為什么要使用AsyncTask
  • 我們可以使用上數(shù)兩種方法來完成我們的異步操作,假如要我們寫的異步操作比較多靡狞,活著較為繁瑣骄瓣,難道我們new Thread()然后用上述方法通知UI更新么?
  • 主要是為了偷懶耍攘,既然官方已經給我們提供了AsyncTask這個封裝好的輕量級異步類榕栏,為什么不用呢,這個通過幾十行的代碼就可以完成我們的異步操作蕾各,而且進度可控扒磁。
  • 相比Handler,AsyncTask顯得更加簡單快捷式曲,但是只是適合簡單的異步操作妨托,同樣適合網絡操作:圖片加載缸榛、數(shù)據(jù)傳輸?shù)龋珹syncTask暫時可以滿足初學者的需求兰伤。
  • 第三方的框架内颗,比如Volley,OkHttp,android-async-http,XUtils等很多,也是異步通信
1.2 AsyncTask的基本結構
  • AsyncTask是一個抽象類敦腔,一般我們都會定義一個類繼承AsyncTask然后重寫相關方法~ 官方API:AsyncTask
1.2.1 構建AsyncTask子類的參數(shù)
  • AsyncTask和Handler一樣是用于處理異步的均澳,不過相對于前者,AsyncTask的代碼更加輕量級符衔,其實后臺是一個線程池找前,在異步任務數(shù)據(jù)比較龐大的時候,AsyncTask的線程池結構優(yōu)勢就體現(xiàn)出來了
  • AsyncTask<Params, progress, Result>
    1. Params: 啟動任務執(zhí)行的輸入參數(shù)判族,比如Http請求的URL
    2. Progress:后臺任務執(zhí)行的百分比
    3. Result:后臺執(zhí)行任務完成后返回的結果躺盛,如String、Integer等形帮,不需要指令類型的話可以寫成void
1.2.2 相關方法與執(zhí)行流程

定義一個AsyncTask類的時候一般都是繼承該類槽惫,然后將該類中的Task參數(shù)改為自己需要的參數(shù)。

class GetFileInfoListTask extends AsyncTask<String, Integer, List<FileInfo>>
  • onPreExecute(): 這個方法是在執(zhí)行異步任務之前的時候執(zhí)行辩撑,并且是在UI Thread當中執(zhí)行的界斜,通常我們在這個方法里做一些UI控件的初始化的操作,例如彈出要給ProgressDialog

  • doInBackground(Params... params): 在onPreExecute()方法執(zhí)行完之后槐臀,會馬上執(zhí)行這個方法,這個方法就是來處理異步任務的方法氓仲,Android操作系統(tǒng)會在后臺的線程池當中開啟一個worker thread來執(zhí)行我們的這個方法水慨,所以這個方法是在worker thread當中執(zhí)行的,這個方法執(zhí)行完之后就可以將我們的執(zhí)行結果發(fā)送給我們的最后一個 onPostExecute 方法敬扛,在這個方法里晰洒,我們可以從網絡當中獲取數(shù)據(jù)等一些耗時的操作

  • onProgressUpdate(Progess... values): 這個方法也是在UI Thread當中執(zhí)行的,我們在異步任務執(zhí)行的時候啥箭,有時候需要將執(zhí)行的進度返回給我們的UI界面谍珊,例如下載一張網絡圖片,我們需要時刻顯示其下載的進度急侥,就可以使用這個方法來更新我們的進度砌滞。這個方法在調用之前,我們需要在 doInBackground 方法中調用一個 publishProgress(Progress) 的方法來將我們的進度時時刻刻傳遞給 onProgressUpdate 方法來更新

  • onPostExecute(Result... result): 當我們的異步任務執(zhí)行完之后坏怪,就會將結果返回給這個方法贝润,這個方法也是在UI Thread當中調用的,我們可以將返回的結果顯示在UI控件上

  • 為什么我們的AsyncTask抽象類只有一個 doInBackground 的抽象方法呢铝宵?打掘?原因是华畏,我們如果要做一個異步任務,我們必須要為其開辟一個新的Thread尊蚁,讓其完成一些操作亡笑,而在完成這個異步任務時,我可能并不需要彈出要給ProgressDialog横朋,我并不需要隨時更新我的ProgressDialog的進度條仑乌,我也并不需要將結果更新給我們的UI界面,所以除了 doInBackground 方法之外的三個方法叶撒,都不是必須有的绝骚,因此我們必須要實現(xiàn)的方法是 doInBackground 方法。

1.2.3 注意事項
  1. Task的示例必須在UI Thread中創(chuàng)建祠够;
  2. execute方法必須在UI Thread中調用压汪;
  3. 不要手動調用onPreExecute()、onPostExecute(Result)古瓤、doInBackground(Params...)止剖、onProgressUpdate(Progress...)這幾個方法;
  4. 該task只能被執(zhí)行一次落君,否則多次調用時會初次安異常穿香;

3. AsyncTask使用示例

3.1 使用示例1

實現(xiàn)效果圖:

布局文件:activity.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    xmlns:tools="http://schemas.android.com/tools"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    android:orientation="vertical"  
    tools:context=".MyActivity">  
    <TextView  
        android:id="@+id/txttitle"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content" />  
    <!--設置一個進度條,并且設置為水平方向-->  
    <ProgressBar  
        android:layout_width="fill_parent"  
        android:layout_height="wrap_content"  
        android:id="@+id/pgbar"  
        style="?android:attr/progressBarStyleHorizontal"/>  
    <Button  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:id="@+id/btnupdate"  
        android:text="更新progressBar"/>  
</LinearLayout> 

定義一個延時操作,用于模擬下載:

public class DelayOperator {  
    //延時操作,用來模擬下載  
    public void delay()  
    {  
        try {  
            Thread.sleep(1000);  
        }catch (InterruptedException e){  
            e.printStackTrace();;  
        }  
    }  
}

自定義AsyncTask:

public class MyAsyncTask extends AsyncTask<Integer,Integer,String>  
{  
    private TextView txt;  
    private ProgressBar pgbar;  
  
    public MyAsyncTask(TextView txt,ProgressBar pgbar)  
    {  
        super();  
        this.txt = txt;  
        this.pgbar = pgbar;  
    }  
   
    //該方法不運行在UI線程中,主要用于異步操作,通過調用publishProgress()方法  
    //觸發(fā)onProgressUpdate對UI進行操作  
    @Override  
    protected String doInBackground(Integer... params) {  
        DelayOperator dop = new DelayOperator();  
        int i = 0;  
        for (i = 10;i <= 100;i+=10)  
        {  
            dop.delay();  
            publishProgress(i);  
        }  
        return  i + params[0].intValue() + "";  
    }  
  
    //該方法運行在UI線程中,可對UI控件進行設置  
    @Override  
    protected void onPreExecute() {  
        txt.setText("開始執(zhí)行異步線程~");  
    }  
  
    //在doBackground方法中,每次調用publishProgress方法都會觸發(fā)該方法  
    //運行在UI線程中,可對UI控件進行操作  
    @Override  
    protected void onProgressUpdate(Integer... values) {  
        int value = values[0];  
        pgbar.setProgress(value);  
    }  
}

MainActivity.java:

public class MyActivity extends ActionBarActivity {  
  
    private TextView txttitle;  
    private ProgressBar pgbar;  
    private Button btnupdate;  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        txttitle = (TextView)findViewById(R.id.txttitle);  
        pgbar = (ProgressBar)findViewById(R.id.pgbar);  
        btnupdate = (Button)findViewById(R.id.btnupdate);  
        btnupdate.setOnClickListener(new View.OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                MyAsyncTask myTask = new MyAsyncTask(txttitle,pgbar);  
                myTask.execute(1000);  
            }  
        });  
    }  
} 
3.2 使用示例2
  • 下載一張網絡圖片绎速,帶有進度條的更新
public class MainActivity extends Activity
{
    private Button button;
    private ImageView imageView;
    private ProgressDialog progressDialog;
    private final String IMAGE_PATH = "http://developer.android.com/images/home/kk-hero.jpg";
//    private final String IMAGE_PATH2 = "http://ww2.sinaimg.cn/mw690/69c7e018jw1e6hd0vm3pej20fa0a674c.jpg";
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        button = (Button)findViewById(R.id.button);
        imageView = (ImageView)findViewById(R.id.imageView);
        //    彈出要給ProgressDialog
        progressDialog = new ProgressDialog(MainActivity.this);
        progressDialog.setTitle("提示信息");
        progressDialog.setMessage("正在下載中皮获,請稍后......");
        //    設置setCancelable(false); 表示我們不能取消這個彈出框,等下載完成之后再讓彈出框消失
        progressDialog.setCancelable(false);
        //    設置ProgressDialog樣式為水平的樣式
        progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        
        button.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                new MyAsyncTask().execute(IMAGE_PATH);
            }
        });
    }
    
 
    public class MyAsyncTask extends AsyncTask<String, Integer, byte[]>
    {
        @Override
        protected void onPreExecute()
        {
            super.onPreExecute();
            //    在onPreExecute()中我們讓ProgressDialog顯示出來
            progressDialog.show();
        }
        @Override
        protected byte[] doInBackground(String... params)
        {
            //    通過Apache的HttpClient來訪問請求網絡中的一張圖片
            HttpClient httpClient = new DefaultHttpClient();
            HttpGet httpGet = new HttpGet(params[0]);
            byte[] image = new byte[]{};
            try
            {
                HttpResponse httpResponse = httpClient.execute(httpGet);
                HttpEntity httpEntity = httpResponse.getEntity();
                InputStream inputStream = null;
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                if(httpEntity != null && httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK)
                {
                    //    得到文件的總長度
                    long file_length = httpEntity.getContentLength();
                    //    每次讀取后累加的長度
                    long total_length = 0;
                    int length = 0;
                    //    每次讀取1024個字節(jié)
                    byte[] data = new byte[1024];
                    inputStream = httpEntity.getContent();
                    while(-1 != (length = inputStream.read(data)))
                    {
                        //    每讀一次纹冤,就將total_length累加起來
                        total_length += length;
                        //    邊讀邊寫到ByteArrayOutputStream當中
                        byteArrayOutputStream.write(data, 0, length);
                        //    得到當前圖片下載的進度
                        int progress = ((int)(total_length/(float)file_length) * 100);
                        //    時刻將當前進度更新給onProgressUpdate方法
                        publishProgress(progress);
                    }
                }
                image = byteArrayOutputStream.toByteArray();
                inputStream.close();
                byteArrayOutputStream.close();
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
            finally
            {
                httpClient.getConnectionManager().shutdown();
            }
            return image;
        }
        @Override
        protected void onProgressUpdate(Integer... values)
        {
            super.onProgressUpdate(values);
            //    更新ProgressDialog的進度條
            progressDialog.setProgress(values[0]);
        }
        @Override
        protected void onPostExecute(byte[] result)
        {
            super.onPostExecute(result);
            //    將doInBackground方法返回的byte[]解碼成要給Bitmap
            Bitmap bitmap = BitmapFactory.decodeByteArray(result, 0, result.length);
            //    更新我們的ImageView控件
            imageView.setImageBitmap(bitmap);
            //    使ProgressDialog框消失
            progressDialog.dismiss();
        }
    }
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu)
    {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末洒宝,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子萌京,更是在濱河造成了極大的恐慌雁歌,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,464評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件知残,死亡現(xiàn)場離奇詭異靠瞎,居然都是意外死亡,警方通過查閱死者的電腦和手機求妹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評論 3 399
  • 文/潘曉璐 我一進店門乏盐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人制恍,你說我怎么就攤上這事丑勤。” “怎么了吧趣?”我有些...
    開封第一講書人閱讀 169,078評論 0 362
  • 文/不壞的土叔 我叫張陵法竞,是天一觀的道長耙厚。 經常有香客問我,道長岔霸,這世上最難降的妖魔是什么薛躬? 我笑而不...
    開封第一講書人閱讀 59,979評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮呆细,結果婚禮上型宝,老公的妹妹穿的比我還像新娘。我一直安慰自己絮爷,他們只是感情好趴酣,可當我...
    茶點故事閱讀 69,001評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著坑夯,像睡著了一般岖寞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上柜蜈,一...
    開封第一講書人閱讀 52,584評論 1 312
  • 那天仗谆,我揣著相機與錄音,去河邊找鬼淑履。 笑死隶垮,一個胖子當著我的面吹牛,可吹牛的內容都是我干的秘噪。 我是一名探鬼主播狸吞,決...
    沈念sama閱讀 41,085評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼指煎!你這毒婦竟也來了蹋偏?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 40,023評論 0 277
  • 序言:老撾萬榮一對情侶失蹤贯要,失蹤者是張志新(化名)和其女友劉穎暖侨,沒想到半個月后椭住,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體崇渗,經...
    沈念sama閱讀 46,555評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,626評論 3 342
  • 正文 我和宋清朗相戀三年京郑,在試婚紗的時候發(fā)現(xiàn)自己被綠了宅广。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,769評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡些举,死狀恐怖跟狱,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情户魏,我是刑警寧澤驶臊,帶...
    沈念sama閱讀 36,439評論 5 351
  • 正文 年R本政府宣布版仔,位于F島的核電站癣朗,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜令哟,卻給世界環(huán)境...
    茶點故事閱讀 42,115評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望掩浙。 院中可真熱鬧赖临,春花似錦、人聲如沸爽茴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,601評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽室奏。三九已至火焰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間窍奋,已是汗流浹背荐健。 一陣腳步聲響...
    開封第一講書人閱讀 33,702評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留琳袄,地道東北人江场。 一個月前我還...
    沈念sama閱讀 49,191評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像窖逗,于是被迫代替她去往敵國和親址否。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,781評論 2 361

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,321評論 25 707
  • 第5章 多線程編程 5.1 線程基礎 5.1.1 如何創(chuàng)建線程 在java要創(chuàng)建線程碎紊,一般有==兩種方式==:1)...
    AndroidMaster閱讀 1,796評論 0 11
  • 周末收到的會議通知佑附,沒有筆記本在手邊,就沒有及時記錄仗考。 覺得是很重要的會議音同,不可能忘記。事實證明隨便好重要都不要挑...
    冰小寒閱讀 312評論 0 1
  • 春節(jié)假期基本在家秃嗜,在家陪老爸喝喝茶权均,陪老媽媽看看電視就是回家最重要的事。一年365天锅锨,相聚的也就這么短短的10天叽赊,...
    林今夕閱讀 174評論 0 0
  • 今年的春節(jié)兒子只在家呆了三天,今天晚上七點十分的高鐵必搞,到北京已是深夜了必指。我只能在候車大廳外面,透過玻璃看他漸走漸遠...
    閆大姐oo閱讀 182評論 0 0