1. 程式人生 > >Android 多執行緒處理之多執行緒用法大集合

Android 多執行緒處理之多執行緒用法大集合

handler.post(r)其實這樣並不會新起執行緒,只是執行的runnable裡的run()方法,卻沒有執行start()方法,所以runnable走的還是UI執行緒。

1.如果像這樣,是可以操作ui,但是run還是走在主執行緒,見打印出來的Log執行緒名字是main,說明是主執行緒。

這就是為什麼可以直接在run方法裡操作ui,因為它本質還是ui執行緒

handler.post(new Runnable(){

  public void run(){

  Log.e("當前執行緒:",Thread.currrentThread.getName());//這裡列印de結果會是main

  setTitle("哈哈");

      }

});

2.通過HandlerThread獲取到looper卻是可以新起執行緒,但是在這裡的run方法裡操作ui是不可能的,但是這顯然有個缺點,如果你執行多次post(r)方法其實走的還是HandlerThread執行緒。假如你執行5次,n次,其實還是一次並且它們是序列的。假如下載5張圖片,你會看到圖片是下完第一張,才會去下第二張。

實踐證明,只有是擁有主執行緒looper的handler才可以操作ui,而在主執行緒操作ui可以在handler的handlerMessage()方法中操作Ui,也可以在handler的post(r)的run方法裡操作Ui.

HandlerThread ht = new HandlerThread("handler thread");

ht.start();

handler = new Handler(ht.getLooper());

handler.post(new Runnable(){//這裡run()方法其實還是在等ht.start()呼叫

  public void run(){

  Log.e("當前執行緒:",Thread.currrentThread.getName());//這裡列印的會是handler thread

  setTitle("哈哈");//這樣必定報錯

  //android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

      }

});

這樣該怎麼辦呢,呵呵,可以無參構建一個handler。用這個handler來發送訊息和處理訊息,用上面的handler來開啟新執行緒。

mainHandler = new Handler(){

  protecket void handlerMessage(Message msg){

    setTitle("哈哈");//這樣就不會報錯啦

  }

}

handler.post(new Runnable(){//這裡run()方法其實還是在等ht.start()呼叫

  public void run(){

  Log.e("當前執行緒:",Thread.currrentThread.getName());//這裡列印的會是handler thread

  mainHandler.sendEmpetMessage();//用mainHandler來發送訊息

  //setTitle("哈哈");//這樣必定報錯

  //android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

      }

});

列印Log:

3.其實第2個方法顯得麻煩而且低效,用了2個handler,一個用來發起執行緒,一個用於處理訊息。發起執行緒的handler必須擁有looper,所以還要例項化一個HanderThread;而處理訊息的handler則不需要looper,因為它預設擁有主執行緒的looper,所以可以在這個handler處理ui。

其實可以只需要例項化一個handler,在主執行緒裡構建一個無參的handler,然後由它傳送和處理訊息。而建立執行緒的任務就不用handler了,直接用new Thread(r).start();然後在r的run()方法裡面處理邏輯事務。

用這樣的模式下載5張圖片,你就可能不會看到圖片一張挨著一張展示出來,可能第2張先出來,也可能同時出來3張,5條執行緒很隨機的。

  1. privatevoid loadImagesByThread(final String url,finalint id){//通過Thread來new 出多個執行緒
  2.         new Thread(new Runnable(){  
  3.             @Override
  4.             publicvoid run() {  
  5.                 // TODO Auto-generated method stub
  6.                 Log.e("當前執行緒:"""+Thread.currentThread().getName());  
  7.                 Drawable drawable = null;  
  8.                 try {  
  9.                     drawable = Drawable.createFromStream(new URL(url).openStream(), "image.gif");  
  10.                 } catch (MalformedURLException e) {  
  11.                     // TODO Auto-generated catch block
  12.                     e.printStackTrace();  
  13.                 } catch (IOException e) {  
  14.                     // TODO Auto-generated catch block
  15.                     e.printStackTrace();  
  16.                 }  
  17.                 Message msg = mainHandler.obtainMessage();  
  18.                 msg.what = 2012;  
  19.                 msg.arg1 = id;  
  20.                 msg.obj = drawable;  
  21.                 msg.sendToTarget();  
  22.             }  
  23.         }).start();  
  24.     }  

列印Log:

4.AsyncTask

用非同步任務架構多工模型其實也不是很健壯,得建立多個AsyncTask例項。一個AsyncTask僅執行一次,不能重複執行,快餐類的執行緒,一次用完。

實現AsyncTask子類,最重要的兩個方法,一個是doInBackground(params);一個是onPostExecute(result)。在doInBackground()方法裡處理耗時事務,並把結果返回,返回的值將在onPostExecute方法作為引數,然後就可以在onPostExecute()把結果展示在ui上面了。

步驟:

①例項化AsyncTask:

例項化AsyncTask然後通過task.exec(pamas);傳進去引數,這個引數列表是動態的,可以是一個也可以使多個,長度可變。

  AsyncTask<params,values,reslut>,第一個引數會傳進去這個方法doInBackground(params),第二個引數是資料更新的值,第三個是處理事務返回的結果。

②onPreExecute方法:

這個方法沒有引數,也沒有返回值,可以在這個方法裡,做一些提醒。比如show一個Dialog,或者彈個Toast告訴使用者開始下載啦。

③doInBackground(params)方法:

進入AsyncTask內部結構,首先將執行reslut doInBackground(params)方法,這個方法將處理耗時事務,exec()的引數將會傳進這個方法做引數,而返回值將會作為onPostExecute()的引數。如果要更新進度的話,需執行publicProgress()方法。

④onProgressUpdate(values)方法:

這個方法的引數必須在doInBackground()方法裡執行publicProgress()方法,這個方法將會把引數傳遞進onProgressUpdate()方法裡,然後可以在這個方法做一些ui上的更新展示,比如進度條的值就可以通過這個values值動態改變。

⑤onPostExecute(result)方法:

這裡就是事務處理完畢的走的方法,doInBackground方法執行的結果將傳到這裡,如果這個方法返回了資料。在這個方法裡可以處理Ui,可以把處理完的資料展示在ui上。比如圖片啊,文字啊,一切你想要的結果。

  1. privatevoid loadImageByAsyncTask(final String url,finalint id){//構建非同步任務,這樣就不用handler來處理訊息了
  2.         DownloadTask task = new DownloadTask();  
  3.         task.execute(""+id,url);//AsyncTask不可重複執行
  4.     }  
  5.     class DownloadTask extends AsyncTask<String,Integer,Drawable>{  
  6.         int id;  
  7.         @Override
  8.         protected Drawable doInBackground(String... params) {//params儲存url和控制元件id兩個資料
  9.             // TODO Auto-generated method stub
  10.             Log.e("當前執行緒:"""+Thread.currentThread().getName());  
  11.             Drawable drawable = null;  
  12.             this.id = Integer.parseInt(params[0]);  
  13.             try {  
  14.                 drawable = Drawable.createFromStream(new URL(params[1]).openStream(), "image.gif");  
  15.             } catch (MalformedURLException e) {  
  16.                 // TODO Auto-generated catch block
  17.                 e.printStackTrace();  
  18.             } catch (IOException e) {  
  19.                 // TODO Auto-generated catch block
  20.                 e.printStackTrace();  
  21.             }  
  22.             return drawable;  
  23.         }  
  24.         @Override
  25.         protectedvoid onPostExecute(Drawable result) {  
  26.             // TODO Auto-generated method stub
  27.             super.onPostExecute(result);  
  28.             ((ImageView)MainActivity.this.findViewById(id)).setImageDrawable(result);  
  29.         }  
  30.         @Override
  31.         protectedvoid onPreExecute() {  
  32.             // TODO Auto-generated method stub
  33.             super.onPreExecute();  
  34.         }  
  35.         @Override
  36.         protectedvoid onProgressUpdate(Integer... values) {  
  37.             // TODO Auto-generated method stub
  38.             super.onProgressUpdate(values);  
  39.         }  
  40.     }  

這裡列印的log

5.ExecutorServie執行緒池

通過Executors的靜態方法來建立,一般有三種:

這裡我們用固定5個執行緒來應用,使用方法是建立ExecutorService物件,然後執行submit(r)可以發起一個Runnable物件。用執行緒池來管理的好處是,可以保證系統穩定執行,適用與有大量執行緒,高工作量的情景下使用,假如要展示1000張圖片如果建立1000個執行緒去載入,保證系統會死掉。用執行緒池就可以避免這個問題,可以用5個執行緒輪流執行,5個一組,執行完的執行緒不直接回收而是等待下次執行,這樣對系統的開銷就可以減小不少。

  1. privatevoid loadImagesByExecutors(final String url,finalint id){  
  2.         service.submit(new Runnable(){  
  3.             @Override
  4.             publicvoid run() {  
  5.                 // TODO Auto-generated method stub
  6.                 Log.e("當前執行緒:"""+Thread.currentThread().getName());  
  7.                 try {  
  8.                     final Drawable drawable  = Drawable.createFromStream(new URL(url).openStream(), "image.gif");  
  9.                     mainHandler.post(new Runnable(){  
  10.                         @Override
  11.                         publicvoid run() {//這將在主執行緒執行
  12.                             // TODO Auto-generated method stub
  13.                             ((ImageView)MainActivity.this.findViewById(id)).setImageDrawable(drawable);  
  14.                         }  
  15.                     });  
  16.                 } catch (MalformedURLException e) {  
  17.                     // TODO Auto-generated catch block
  18.                     e.printStackTrace();  
  19.                 } catch (IOException e) {  
  20.                     // TODO Auto-generated catch block
  21.                     e.printStackTrace();  
  22.                 }  
  23.             }  
  24.         });  
  25.     }  

Log:

其實可能沒有說清楚,第一種不算多執行緒。

1.loadImagesByHandler()是通過Handler.post()方法,構建兩個Handler進行通訊。

2.loadImagesByThread(),這個是直接new Thread()發起執行緒,在主執行緒的handler處理訊息

3.loadImageByAsyncTask(),這個用的是非同步任務,所有實現在它的內部結構裡,可以在裡頭操作Ui.

4.loadImagesByExecutors()用的是執行緒池,使得執行緒可控,保證穩定執行。

其實常用的就是後三種,第二個用法靈活,簡單,但不適宜大數量任務;第三個一般適用於單個任務,一次性任務;第四個一般用於大數量,高密度執行的使用情景,比如批量載入圖片,批量下載檔案等。

看一眼圖吧:

全部原始碼:

  1. package com.bvin.exec;  
  2. import java.io.IOException;  
  3. import java.net.MalformedURLException;  
  4. import java.net.URL;  
  5. import java.util.concurrent.ExecutorService;  
  6. import java.util.concurrent.Executors;  
  7. import android.app.Activity;  
  8. import android.graphics.drawable.Drawable;  
  9. import android.os.AsyncTask;  
  10. import android.os.Bundle;  
  11. import android.os.Handler;  
  12. import android.os.HandlerThread;  
  13. import android.os.Message;  
  14. import android.util.Log;  
  15. import android.view.View;  
  16. import android.widget.Button;  
  17. import android.widget.ImageView;  
  18. publicclass MainActivity extends Activity {  
  19.     /** Called when the activity is first created. */
  20.     private Handler handler ;  
  21.     private Button bt;  
  22.     private Handler mainHandler = new Handler(){  
  23.         @Override
  24.         publicvoid handleMessage(Message msg) {  
  25.             // TODO Auto-generated method stub
  26.             super.handleMessage(msg);  
  27.             if(msg.what == 2012){  
  28.                 //只要在主執行緒就可以處理ui 
  29.                 ((ImageView)MainActivity.this.findViewById(msg.arg1)).setImageDrawable((Drawable)msg.obj);  
  30.             }  
  31.         }  
  32.     };  
  33.     private ExecutorService service = Executors.newFixedThreadPool(5);  
  34.     @Override
  35.     publicvoid onCreate(Bundle savedInstanceState) {  
  36.         super.onCreate(savedInstanceState);  
  37.         setContentView(R.layout.main);  
  38.         initViews();  
  39.         HandlerThread ht = new HandlerThread("down image thread");  
  40.         ht.start();  
  41.         handler = new Handler(ht.getLooper()){//如果有了looper那麼這個handler就不可以處理ui了
  42.             @Override
  43.             publicvoid handleMessage(Message msg) {  
  44.                 // TODO Auto-generated method stub
  45.                 super.handleMessage(msg);  
  46.             }  
  47.         };  
  48.     }  
  49.     privatevoid initViews(){  
  50.         bt = (Button)findViewById(R.id.bt);  
  51.         bt.setOnClickListener(new View.OnClickListener() {  
  52.             @Override
  53.             publicvoid onClick(View v) {  
  54.                 // TODO Auto-generated method stub
  55.                 loadImagesByExecutors("http://news.baidu.com/z/resource/r/image/2012-11-23/23c1625aca99f02c50d8e510383a34e7.jpg",R.id.iv1);  
  56.                 loadImagesByExecutors("http://news.baidu.com/z/resource/r/image/2012-11-23/c4698d97ef6d10722c8e917733c7beb3.jpg",R.id.iv2);  
  57.                 loadImagesByExecutors("http://news.baidu.com/z/resource/r/image/2012-11-23/f332ffe433be2a3112be15f78bff5a40.jpg",R.id.iv3);  
  58.                 loadImagesByExecutors("http://news.baidu.com/z/resource/r/image/2012-11-23/6ff8a9c647a1e80bc602eeda48865d4c.jpg",R.id.iv4);  
  59.                 loadImagesByExecutors("http://news.baidu.com/z/resource/r/image/2012-11-23/f104d069f7443dca52a878d779392874.jpg",R.id.iv5);  
  60.             }  
  61.         });  
  62.     }  
  63.     privatevoid loadImagesByHandler(final String url,finalint id){//通過擁有looper的handler.post(runnable),新建執行緒
  64.         handler.post(new Runnable(){//如果handler沒有Looper那麼它就不能構建新執行緒了
  65.             @Override
  66.             publicvoid run() {  
  67.                 // TODO Auto-generated method stub
  68.                 Log.e("當前執行緒:"""+Thread.currentThread().getName());  
  69.                 Drawable drawable = null;  
  70.                 try {  
  71.                     drawable = Drawable.createFromStream(new URL(url).openStream(), "image.gif");  
  72.                 } catch (MalformedURLException e) {  
  73.                     // TODO Auto-generated catch block
  74.                     e.printStackTrace();  
  75.                 } catch (IOException e) {  
  76.                     // TODO Auto-generated catch block
  77.                     e.printStackTrace();  
  78.                 }  
  79.                 //SystemClock.sleep(2000);
  80.                 //((ImageView)MainActivity.this.findViewById(id)).setImageDrawable(drawable);
  81.                 Message msg = mainHandler.obtainMessage();  
  82.                 msg.what = 2012;  
  83.                 msg.arg1 = id;  
  84.                 msg.obj = drawable;  
  85.                 msg.sendToTarget();  
  86.             }  
  87.         });  
  88.     }  
  89.     privatevoid loadImagesByThread(final String url,finalint id){//通過Thread來new 出多個執行緒
  90.         new Thread(new Runnable(){  
  91.             @Override
  92.             publicvoid run() {  
  93.                 // TODO Auto-generated method stub
  94.                 Log.e("當前執行緒:"""+Thread.currentThread().getName());  
  95.                 Drawable drawable = null;  
  96.                 try {  
  97.                     drawable = Drawable.createFromStream(new URL(url).openStream(), "image.gif");  
  98.                 } catch (MalformedURLException e) {  
  99.                     // TODO Auto-generated catch block
  100.                     e.printStackTrace();  
  101.                 } catch (IOException e) {  
  102.                     // TODO Auto-generated catch block
  103.                     e.printStackTrace();  
  104.                 }  
  105.                 Message msg = mainHandler.obtainMessage();  
  106.                 msg.what = 2012;  
  107.                 msg.arg1 = id;  
  108.                 msg.obj = drawable;  
  109.                 msg.sendToTarget();  
  110.             }  
  111.         }).start();  
  112.     }   
  113.     privatevoid loadImageByAsyncTask(final String url,finalint id){//構建非同步任務,這樣就不用handler來處理訊息了
  114.         DownloadTask task = new DownloadTask();  
  115.         task.execute(""+id,url);//AsyncTask不可重複執行
  116.     }  
  117.     privatevoid loadImagesByExecutors(final String url,finalint id){  
  118.         service.submit(new Runnable(){  
  119.             @Override
  120.             publicvoid run() {  
  121.                 // TODO Auto-generated method stub
  122.                 Log.e("當前執行緒:"""+Thread.currentThread().getName());  
  123.                 try {  
  124.                     final Drawable drawable  = Drawable.createFromStream(new URL(url).openStream(), "image.gif");  
  125.                     mainHandler.post(new Runnable(){  
  126.                         @Override
  127.                         publicvoid run() {//這將在主執行緒執行
  128.                             // TODO Auto-generated method stub
  129.                             ((ImageView)MainActivity.this.findViewById(id)).setImageDrawable(drawable);  
  130.                         }  
  131.                     });  
  132.                 } catch (MalformedURLException e) {  
  133.                     // TODO Auto-generated catch block
  134.                     e.printStackTrace();  
  135.                 } catch (IOException e) {  
  136.                     // TODO Auto-generated catch block
  137.                     e.printStackTrace();  
  138.                 }  
  139.             }  
  140.         });  
  141.     }  
  142.     class DownloadTask extends AsyncTask<String,Integer,Drawable>{  
  143.         int id;  
  144.         @Override
  145.         protected Drawable doInBackground(String... params) {//params儲存url和控制元件id兩個資料
  146.             // TODO Auto-generated method stub
  147.             Log.e("當前執行緒:"""+Thread.currentThread().getName());  
  148.             Drawable drawable = null;  
  149.             this.id = Integer.parseInt(params[0]);  
  150.             try {  
  151.                 drawable = Drawable.createFromStream(new URL(params[1]).openStream(), "image.gif");  
  152.             } catch (MalformedURLException e) {  
  153.                 // TODO Auto-generated catch block
  154.                 e.printStackTrace();  
  155.             } catch (IOException e) {  
  156.                 // TODO Auto-generated catch block
  157.                 e.printStackTrace();  
  158.             }  
  159.             return drawable;  
  160.         }  
  161.         @Override
  162.         protectedvoid onPostExecute(Drawable result) {  
  163.             // TODO Auto-generated method stub
  164.             super.onPostExecute(result);  
  165.             ((ImageView)MainActivity.this.findViewById(id)).setImageDrawable(result);  
  166.         }  
  167.         @Override
  168.         protectedvoid onPreExecute() {  
  169.             // TODO Auto-generated method stub
  170.             super.onPreExecute();  
  171.         }  
  172.         @Override
  173.         protectedvoid onProgressUpdate(Integer... values) {  
  174.             // TODO Auto-generated method stub
  175.             super.onProgressUpdate(values);  
  176.         }  
  177.     }