Android初識多線程

Android多線程

  • 一個Android的應用程序運行在一個獨立的進程中,運行在一個獨立的虛擬機(dvk)上。 (進程名為包名)

  • Android應用程序開啟后,默認開啟一個主線程(UI線程)
    Activity,Service,BroadcastReceiver組件運行在主線程中
    Android應用程序退出后,保留空UI線程,可以加快應用程序啟動速度

  • 用戶不能在UI主線程中做耗時的操作,一旦該操作超過5s,應用程序拋一個ANR異常(Application not respond)。
    如何避免ANR錯誤?
    將耗時的操作放入子線程中。(耗時的操作包括:長時間的休眠,計數(shù),聯(lián)網(wǎng),復雜的運算。)

  • 只有主線程才能操作Widget控件。
    如果在子線程中操作Widget控件,系統(tǒng)拋出CalledFromWrongThreadException異常。

  • 系統(tǒng)為什么要這么做?
    避免出現(xiàn)同步問題。

Handler機制。

  • Goolge為什么設計這套機制?
    主要是為了解決非UI線程中不能更新Widget控件的問題

  • Handler機制剖析
    子線程發(fā)送消息給底層的消息隊列。
    handler.sendMessage(msg)
    主線程查詢消息隊列,處理消息對象。
    handlerMessage(msg)
    MessageQueue 消息隊列
    負責存儲消息對象

  • Looper
    給UI線程安排代碼,一個UI線程只能有一個Looper對象,否則多個Looper對象都在UI線程上安排代碼,解決沖突就是個大問題。 Looper對象會線性安排在UI線程上執(zhí)行的代碼,它通過一個隊列管理各個Handler對象提交的代碼。

  • Message消息對象

//從消息池中獲取消息對象
Message msg = handler.obtainMessage();
//在消息對象上綁定int類型數(shù)據(jù)
msg.arg1 = count;
//在消息對象上綁定其它類型數(shù)據(jù)
msg.setData(Bundle);        //Bundler為數(shù)據(jù)集(類似于HashMap容器)
  • 向消息隊列發(fā)送消息,1000毫秒后執(zhí)行Runnable對象中的代碼。
myHandler.postDelayed(new Runnable() { 
           @Override 
          public void run() { 
                 // TODO Auto-generated method stub 
                 Log.e("Test", "thread name = "+Thread.currentThread().getName());
           }
}, 1000);

異步任務(AsyncTask)

  • 概念
    封裝多線程和Handler機制。給用戶提供重寫接口的方式,不需要用戶手動創(chuàng)建子線程和Handler對象。

  • 異步任務的優(yōu)點
    Handler模式需要為每一個任務創(chuàng)建一個新的線程,任務完成后通過Handler實例向UI主線程發(fā)送消息,完成界面的更新,這種方式對于整個過程的控制比較精細,但是也有缺點,代碼臃腫,在多個任務同時執(zhí)行時,不易對線程進行精確的控制。為了簡化操作,Android1.5提供了一個工具類AsyncTask,它是創(chuàng)建異步任務變的更加簡單,不再需要編寫任務線程和Handler實例就可完成任務。

  • 異步任務的局限性
    多個異步任務不能同時執(zhí)行,在某個時間內,只能執(zhí)行一個異步任務。

  • 執(zhí)行異步任務的步驟:

  1. execute(Params... params),執(zhí)行一個異步任務,需要我們在代碼中調用此方法,觸發(fā)異步任務的執(zhí)行。
  2. onPreExecute(),在execute(Params... params)被調用后立即執(zhí)行,一般用來在執(zhí)行后臺任務前對UI做一些標記。
  3. doInBackground(Params... params),在onPreExecute()完成后立即執(zhí)行,用于執(zhí)行較為費時的操作,此方法將接收輸入?yún)?shù)和返回計算結果。在執(zhí)行過程中可以調publishProgress(Progress... values)來更新進度信息。
  4. onProgressUpdate(Progress... values),在調用publishProgress(Progress... values)時,此方法被執(zhí)行,直接將進度信息更新到UI組件上。
  5. onPostExecute(Result result),當后臺操作結束時,此方法將會被調用,計算結果將做為參數(shù)傳遞到此方法中,直接將結果顯示到UI組件上。
  • 取消異步任務
/** 
*cancel(true) 取消當前的異步任務,傳入的true,表示當中斷異步任務時繼續(xù)已經(jīng)運行的線程的操作, 
*但是為了線程的安全一般為讓它繼續(xù)設為true 
**/ 
mTask.cancel(true); 
/** 
* 但是重新運行后會發(fā)現(xiàn)還是不能起到效果, 
* 注意:這是因為cancel方法只是發(fā)出一個請求取消異步任務的信號, 
* 將對應當前的異步任務標記為CANCEL狀態(tài),而并不是真正取消線程的執(zhí)行,
 * 而此時異步任務中的線程仍然在執(zhí)行并沒有結束 
* 所以效果依然是這樣的,并且在java中我們是無法直接暴力將一個線程給停止掉
 * 既然我們知道無法去取消一個已經(jīng)正在運行的線程,但是我們如何去解決這個BUG呢? 
* 在異步任務中還給我們提供一個isCanceled的回調方法,也就是當我已經(jīng)給當前的異步任務 
* 調用了cancel(true)方法,發(fā)出一個請求取消異步任務的信號,那么此時的isCanceled的回調方法 
* 會直接返回一個true,那么我們就可以通過判斷當前異步任務isCanceled是否為true,來終止
* 線程中的操作而不是去終止線程,從而達到了界面顯示好像線程中的操作被終止了,而實際上 
* 該線程依然在運行
* */
  • 注意
  1. 異步任務對象只能執(zhí)行一次。
  2. 異步任務對象必須在UI主線程中創(chuàng)建。execute(Params... params)方法必須在UI線程中調用。
  3. 不要手動調用onPreExecute(),doInBackground(Params... params),onProgressUpdate(Progress... values),onPostExecute(Result result)這幾個方法。
  4. 不能在doInBackground(Params... params)中更改UI組件的信息。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容