Android進階6:ThreadHandler詳解和源碼分析

線程與線程之間的通訊

源碼解讀 Android 消息機制( Message MessageQueue Handler Looper)中詳細介紹了Looper,handle,Message的使用關係。java

  • 主線程和子線程
    在UI線程中,自動建立了Looper,和消息隊列MessageQueue,並開啓了循環。只須要在UI線程中實現Handler 的mUIHnadler,而後在某個子線程的run方法裏把使用mUIHnadler.sendMessage(),便可把消息發送到UI線程的MessageQueue中,在UI線程處理消息
 //主線程
 mUIHandler = new Handler() {
              public void handleMessage(Message msg) {
                  // 這裏處理消息
              }
          };
 //子線程
 class SubThread extends Thread {
      public void run() {

         //其餘耗時代碼
          mHandler.sendMessage(new Message());//發送到主線程處理
      }
複製代碼
  • 子線程和子線程
    通常在子線程中須要顯示的聲明建立Looper:
 public Handler mHandler1;
 //子線程1 建立looper並循環
class LooperThread1 extends Thread {

      public void run() {
          Looper.prepare();

          mHandler = new Handler() {
              public void handleMessage(Message msg) {
                  // 這裏處理消息
              }
          };

          Looper.loop();
      }
//子線程2 
class LooperThread2 extends Thread {

      public void run() {

          //線程2的其餘代碼

          mHandler1.sendMessage(Message.obtain());//發送到線程1的MessageQueue中,在線程1中去處理

      }
複製代碼

很明顯的一點就是,咱們要在子線程中調用Looper.prepare() 爲一個線程開啓一個消息循環,默認狀況下Android中新誕生的線程是沒有開啓消息循環的。(主線程除外,主線程系統會自動爲其建立Looper對象,開啓消息循環。) Looper對象經過MessageQueue來存放消息和事件。一個線程只能有一個Looper,對應一個MessageQueue。 而後經過Looper.loop() 讓Looper開始工做,從消息隊列裏取消息,處理消息。android

注意:寫在Looper.loop()以後的代碼不會被執行,這個函數內部應該是一個循環,當調用mHandler.getLooper().quit()後,loop纔會停止,其後的代碼才能得以運行。web

能夠看到,很是繁瑣,一層套一層看着也不美觀。安全

HandlerThread就是爲了幫咱們免去寫上面那樣的代碼而生的網絡

官方文檔對它的介紹:app

HandlerThread 是一個包含 Looper 的 Thread,咱們能夠直接使用這個 Looper 建立 Handler異步

HandlerThread,繼承自Thread,本質是Thread,它與普通Thread的差異就在於,它有個Looper成員變量。其內部就是經過Thread+Looper來實現的,說白了HandlerThread就是Android已經封裝好的一個擁有本身looper的線程,咱們能夠利用它執行一些耗時任務。咱們先來看看HandlerThread的使用步驟並提供給你們一個使用案例:ide

HandlerThread的使用步驟:

  • 建立實例對象
HandlerThread handlerThread = new HandlerThread("downloadImage");  
複製代碼

參數的做用主要是標記當前線程的名字,能夠任意字符串。函數

  • 啓動HandlerThread線程
//必須先開啓線程  
  handlerThread.start();  
複製代碼

到此,咱們就構建完一個循環線程。那麼咱們怎麼將一個耗時的異步任務投放到HandlerThread線程中去執行呢?接下來看下面步驟:oop

  • 構建循環消息處理機制
  /** 
   * 該callback運行於子線程 
   */
  
  class ChildCallback implements Handler.Callback {  
      @Override  
      public boolean handleMessage(Message msg) {  
          //在子線程中進行相應的網絡請求  

          //通知主線程去更新UI  
          mUIHandler.sendMessage(msg1);  
          return false;  
      }  
  }  
複製代碼

構建異步handler

//子線程Handler  
Handler childHandler = new Handler(handlerThread.getLooper(),new ChildCallback());  
複製代碼

第3步是構建一個能夠用於異步操做的handler,並將前面建立的HandlerThread的Looper對象和Callback接口類做爲參數傳遞給當前的handler,這樣當前的異步handler就擁有了HandlerThread的Looper對象,而其中的handlerMessage方法來處理耗時任務,Looper+Handler+MessageQueue+Thread異步循環機制構建完成。下面咱們來看一個使用案例

HandlerThread的使用案例:每一個1秒去更新圖片

public class HandlerThreadActivity extends Activity {
    /**
     * 圖片地址集合,圖片來自網絡.
     */

    private String url[]={
            "http://img.my.csdn.net/uploads/201407/26/1406383299_1976.jpg",
            "http://img.my.csdn.net/uploads/201407/26/1406383291_6518.jpg",
            "http://img.my.csdn.net/uploads/201407/26/1406383291_8239.jpg",
            "http://img.my.csdn.net/uploads/201407/26/1406383290_9329.jpg",
            "http://img.my.csdn.net/uploads/201407/26/1406383290_1042.jpg",
            "http://img.my.csdn.net/uploads/201407/26/1406383275_3977.jpg",
            "http://img.my.csdn.net/uploads/201407/26/1406383265_8550.jpg",
            "http://img.my.csdn.net/uploads/201407/26/1406383264_3954.jpg",
            "http://img.my.csdn.net/uploads/201407/26/1406383264_4787.jpg",
            "http://img.my.csdn.net/uploads/201407/26/1406383264_8243.jpg",
            "http://img.my.csdn.net/uploads/201407/26/1406383248_3693.jpg",
            "http://img.my.csdn.net/uploads/201407/26/1406383243_5120.jpg",
            "http://img.my.csdn.net/uploads/201407/26/1406383242_3127.jpg",
            "http://img.my.csdn.net/uploads/201407/26/1406383242_9576.jpg",
            "http://img.my.csdn.net/uploads/201407/26/1406383242_1721.jpg",
            "http://img.my.csdn.net/uploads/201407/26/1406383219_5806.jpg",
            "http://img.my.csdn.net/uploads/201407/26/1406383214_7794.jpg",
            "http://img.my.csdn.net/uploads/201407/26/1406383213_4418.jpg",
            "http://img.my.csdn.net/uploads/201407/26/1406383213_3557.jpg",
            "http://img.my.csdn.net/uploads/201407/26/1406383210_8779.jpg",
            "http://img.my.csdn.net/uploads/201407/26/1406383172_4577.jpg"
    };
    private ImageView imageView;
    private Handler mUIHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            LogUtils.e("次數:"+msg.what);
            ImageModel model = (ImageModel) msg.obj;
            imageView.setImageBitmap(model.bitmap);
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_thread);
        imageView= (ImageView) findViewById(R.id.image);
        //建立異步HandlerThread
        HandlerThread handlerThread = new HandlerThread("downloadImage");
        //必須先開啓線程
        handlerThread.start();
        //子線程Handler
        Handler childHandler = new Handler(handlerThread.getLooper(),new ChildCallback());
        for(int i=0;i<10;i++){
            //每一個1秒去更新圖片
            childHandler.sendEmptyMessageDelayed(i,1000*i);
        }
    }
    /**
     * 該callback運行於子線程
     */

    class ChildCallback implements Handler.Callback {
        @Override
        public boolean handleMessage(Message msg) {
            //在子線程中進行網絡請求
            Bitmap bitmap=downloadUrlBitmap(url[msg.what]);
            ImageModel imageModel=new ImageModel();
            imageModel.bitmap=bitmap;
            imageModel.url=url[msg.what];
            Message msg1 = new Message();
            msg1.what = msg.what;
            msg1.obj =imageModel;
            //通知主線程去更新UI
            mUIHandler.sendMessage(msg1);
            return false;
        }
    }
    private Bitmap downloadUrlBitmap(String urlString) {
        HttpURLConnection urlConnection = null;
        BufferedInputStream in = null;
        Bitmap bitmap=null;
        try {
            final URL url = new URL(urlString);
            urlConnection = (HttpURLConnection) url.openConnection();
            in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);
            bitmap=BitmapFactory.decodeStream(in);
        } catch (final IOException e) {
            e.printStackTrace();
        } finally {
            if (urlConnection != null) {
                urlConnection.disconnect();
            }
            try {
                if (in != null) {
                    in.close();
                }
            } catch (final IOException e) {
                e.printStackTrace();
            }
        }
        return bitmap;
    }
}
複製代碼

思路分析:在這個案例中,咱們建立了兩個Handler,一個用於更新UI線程的mUIHandler和一個用於異步下載圖片的childHandler。最終的結果是childHandler會每一個隔1秒鐘經過sendEmptyMessageDelayed方法去通知ChildCallback的回調函數handleMessage方法去下載圖片並告訴mUIHandler去更新UI界面。

HandlerThread源碼解析

HandlerThread 源碼很是簡單,看起來 so easy:

public class HandlerThread extends Thread {
     //優先級
    int mPriority;
    //線程id
    int mTid = -1;
    //線程的looper
    Looper mLooper;

    public HandlerThread(String name) {
        super(name);
        //默認
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }

    //也能夠指定線程的優先級,注意使用的是 android.os.Process 而不是 java.lang.Thread 的優先級!
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }

    // 子類須要重寫的方法,在這裏作一些執行前的初始化工做
    protected void onLooperPrepared() {
    }

    //獲取當前線程的 Looper
    //若是線程不是正常運行的就返回 null
    //若是線程啓動後,Looper 還沒建立,就 wait() 等待 建立 Looper 後 notify
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }

        synchronized (this) {
            while (isAlive() && mLooper == null) {    //循環等待
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

    //調用 start() 後就會執行的 run()
    @Override
    public void run() {
        mTid = Process.myTid();
        //幫咱們建立了 Looepr
        Looper.prepare();  

        synchronized (this) {
           //Looper 已經建立,喚醒阻塞在獲取 Looper 的線程
            mLooper = Looper.myLooper();

            notifyAll();    
        }
        Process.setThreadPriority(mPriority);

        //子類可重寫的方法
        onLooperPrepared();

        //開始循環
        Looper.loop();  

        //調用quit後纔會執行
        mTid = -1;
    }
    //退出
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }
    //安全退出
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }

    public int getThreadId() {
        return mTid;
    }
}
複製代碼

能夠看到,
①HandlerThread 本質仍是個 Thread,建立後別忘了調用 start()。
②在 run() 方法中建立了 Looper,調用 onLooperPrepared 後開啓了循環
③咱們要作的就是在子類中重寫 onLooperPrepared,作一些初始化工做
④在建立 HandlerThread 時能夠指定優先級,注意這裏的參數是 Process.XXX 而不是 Thread.XXX

HandlerThread 的使用場景

咱們知道,HandlerThread 所作的就是在新開的子線程中建立了 Looper,那它的使用場景就是 Thread + Looper 使用場景的結合,即:在子線程中執行耗時的、可能有多個任務的操做

好比說多個網絡請求操做,或者多文件 I/O 等等

參考連接:

  • https://blog.csdn.net/javazejian/article/details/50813444
  • https://blog.csdn.net/u011240877/article/details/72905631
相關文章
相關標籤/搜索