Android 多線程

App 啟動的時候 Android 系統自動啟動一個 Linux process 這個 process 包含一個 Thread 成為 UI Thread / Main Thread. 通常應用的所有組件都運行在這一個 process 中. 同時也可以通過修改四大組件在 AndroidManifest.xml 中的代碼塊<activity><service><provider><receiver>中的 android:process 屬性指定其運行在不同的 process 中 當一個組件在啟動的時候 如果該 process 已經存在了 那么該組件就直接通過這個 process 被啟動起來 并且運行在這個 process 的 UI Thread中

UI Thread 中運行著很多重要的邏輯: 系統事件處理(system Events) 用戶輸入事件處理(input Events) UI 繪制(Application) Service Alarm 我們編寫的代碼澤施穿插在這些邏輯的中間 比如對用戶觸摸時間的檢測和響應 對用戶輸入的處理 自定義 View 的繪制 如果我們插入的代碼比較耗時 比如網絡請求 或者數據庫讀取 那么就會阻塞 UI 線程其他的邏輯執行 從而導致界面卡頓 如果卡頓超過五秒 系統就回報 ANR 錯誤 所以耗時操作需要另起線程

獲取到數據之后 更新 UI 的時候 最好只在 UI 線程里面更新 子線程里面更新的方法(之所以子線程不能更新界面,是因為Android在線程的方法里面采用checkThread進行判斷是否是主線程,而這個方法是在ViewRootImpl中的,這個類是在onResume里面才生成的,因此,如果這個時候子線程在onCreate方法里面生成更新UI,而且沒有做阻塞,就是耗時多的操作,還是可以更新UI的。)

Android 提供了四種常用的操作多線程的方式:

Handler + Thread

AsyncTask

ThreadPoolExecutor

IntentService

Handler + Thread

Android 主線程包括一個消息隊列 (MessageQueue) 此消息隊列可以存入一系列的 message 或者 Runnable 對象 通過一個 handler 你可以往這個消息隊列中發送 Message 或者 Runnable對象 并且處理這些對象 每次創建一個新的 Handle 對象 他會綁定于創建她的線程(UI 線程)以及該線程的消息隊列 從這時起 這個 Handler 就會開始吧 Message 或者 Runnable 對象傳遞到消息隊列中 并在出隊列的時候執行他們 Handler 可以吧一個 Message 對象或者 Runnable 對象壓入消息隊列中 進而在 UI 線程中獲取 Message 或者執行 Runnable 對象 Handler 把消息壓入隊列有兩種方式 Post 和 SendMessage

post 方式:

post 允許把一個 Runnable 對象壓入到隊列消息中 它的方法有 post(Runnable) postAtTime(Runnable,long) postDelayed(Runnable,long)

對于 Handler 的 post 方式來說 他會傳遞一個 Runnable 對象到消息隊列中 在這個 Runnable 對象中 重寫run()方法 一般在這個 run() 方法中寫入需要在 UI 線程上的操作

new Handler().post(new Runnable() {

@Override

    public void run() {

         new ImageView(getContext()).setImageBitmap(new Bitmap());

    }

});
sendMessage:

sendMessage 允許吧一個包含消息數據的 Message對象壓入到消息隊列中 她的方法有

sendEmptyMessage(int) sendMessage(Message) sendMessageAtTime(Message,long) sendMessageDelayed(Message,long)

Handler 如果使用 sendMessage 的方式吧消息壓入到消息隊列中 需要傳遞一個 Message 對象 而在 Handler 中 需要重寫 HandleMessage()方法 用于獲取工作線程傳遞過來的消息 此方法運行在 UI 線程上 Message 是一個 final 類 所以不可以被繼承

Message message = new Message();
        message.what = 1;
        handler.sendMessage(message);
    }

    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == 1){
                //更新 UI 代碼
            }
        }
    };

這個時候或許有人會有疑問:更新 UI 不是只能在主線程中更新嗎 那么 Handler 是怎么回事兒呢?

更新UI只能在主線程中更新,子線程中操作是危險的. 這個時候,Handler就出現了來解決這個復雜的問題,由于Handler運行在主線程中(UI線程中),它與子線程可以通過Message對象來傳遞數據,這個時候,Handler就承擔著接受子線程傳過來的(子線程用sedMessage()方法傳弟)Message對象,(里面包含數據) , 把這些消息放入主線程隊列中,配合主線程進行更新UI。

——————————這一段來自 windroid之父 的CSDN 博客 ,全文地址請點擊:https://blog.csdn.net/u013252711/article/details/37820719?utm_source=copy

優缺點:Handler 用法簡單明了 可以將多個異步任務更新 UI 的代碼放在一起 但是處理單個異步任務代碼顯得比較多

使用范圍:多個異步任務的更新 UI

AsyncTask

AsyncTask是Android 提供的輕量級異步類 可以直接繼承AsyncTask 在類中實現異步操作 并提供接口返回當前異步執行的成都(可以通過接口實現 UI 進度更新) 最后反饋執行的結果給 UI 主線程

AsyncTask通過阻塞 BlockingQuery(Runnable) 儲存待執行的任務 利用靜態線程池 THREAD_POOL_EXECUTOR 提供一定數量的線程 默認是128個 在 Android 3.0以前默認采取數據的是并行任務執行器 3.0以后改成了默認采用串行任務執行器 通過靜態串行任務執行器SERIAL_EXECUTOR 控制任務串行執行 循環取出任務給THREAD_POOL_EXECUTOR 中的線程執行 執行完一個 再執行下一個

class DownloadTask extends AsyncTask<Integer, Integer, String>{  
    // AsyncTask<Params, Progress, Result>
    //后面尖括號內分別是參數(例子里是線程休息時間),進度(publishProgress用到),返回值類型
    @Override
    protected void onPreExecute() {
        //第一個執行方法
        super.onPreExecute();
    }
    @Override
    protected String doInBackground(Integer... params) {
        //第二個執行方法,onPreExecute()執行完后執行
        for(int i=0;i<=100;i++){
            publishProgress(i);
            try {
                Thread.sleep(params[0]);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return "執行完畢";
    }
    @Override
    protected void onProgressUpdate(Integer... progress) {
        //這個函數在doInBackground調用publishProgress時觸發,雖然調用時只有一個參數
        //但是這里取到的是一個數組,所以要用progesss[0]來取值
        //第n個參數就用progress[n]來取值
        tv.setText(progress[0]+"%");
        super.onProgressUpdate(progress);
    }
    @Override
    protected void onPostExecute(String result) {
        //doInBackground返回時觸發,換句話說,就是doInBackground執行完后觸發
        //這里的result就是上面doInBackground執行后的返回值,所以這里是"執行完畢"
        setTitle(result);
        super.onPostExecute(result);
    }
}

優缺點:處理單個異步任務簡單 可以獲取到異步任務的進度 可以通過 cancel 方法取消還沒有執行完的AsyncTask 處理多個異步任務閑的代碼較多

適用范圍:單個異步任務的處理

ThreadPoolExecutor

ThreadPoolExecutor提供了一組線程池 可以管理多個線程并執行 這樣一方面減少了每個并行任務獨自建立線程的開銷 另一方面可以管理多個并發線程的公共資源 從而提高了多線程的小鹿 所以ThreadPoolExecutor比較適合一組任務的執行 Excutors 利用工廠模式對ThreadPoolExecutor進行了封裝 使用起來更加方便

Executors 提供了四種創建ExecutorService 的方法 她們的場景使用如下

Executors.newFixedThreadPool()
創建一個定長的線程池 提交一個任務就創建一個縣城 直到到達線程池的最大長度 這是線程會保持長度不再變化

Executors.newCachedThreadPool()
創建一個可緩存的線程池 如果當前線程池的長度超過了處理時所需要的 他可以靈活的收回空閑的線程 當需要增加時 他可以靈活的添加心的線程 而不會對線程池的長度做任何的限制

Executors.newScheduledThreadPool()
創建一個定長的線程池,而且支持定時的以及周期性的任務執行,類似于Timer

Executors.newSingleThreadExecutor()
創建一個單線程化的executor,它只創建唯一的worker線程來執行任務

使用范圍:批量處理任務

IntentService

ntentService繼承自Service,是一個經過包裝的輕量級的Service,用來接收并處理通過Intent傳遞的異步請求。客戶端通過調用startService(Intent)啟動一個IntentService,利用一個work線程依次處理順序過來的請求,處理完成后自動結束Service。 一個可以處理異步任務的簡單Service

此文章摘抄與 http://www.lxweimin.com/p/2b634a7c49ec

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,802評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,762評論 2 372

推薦閱讀更多精彩內容