Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android多線程開發之HandlerThread的使用

Android多線程開發之HandlerThread的使用

編輯:關於Android編程

初次看到HandlerThread的名字,我們可能會聯想到Handler和Thread這兩個類,沒錯,它其實就是跟Handler和Thread有莫大的關系。HandlerThread繼承自Thread,它本質上就是一個Thread,而且專門用來處理Handler的消息。

一、HandlerThread簡介

看看官方對它的解釋:

Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called.

大致就是說HandlerThread可以創建一個帶有looper的線程,looper對象可以用於創建Handler類來進行來進行調度,而且start()方法必須被調用。

在Android開發中,不熟悉多線程開發的人一想到要使用線程,可能就用new Thread(){…}.start()這樣的方式。實質上在只有單個耗時任務時用這種方式是可以的,但若是有多個耗時任務要串行執行呢?那不得要多次創建多次銷毀線程,這樣導致的代價是很耗系統資源,容易存在性能問題。那麼,怎麼解決呢?

我們可以只創建一個工作線程,然後在裡面循環處理耗時任務,創建過程如下:

Handler mHandler;
private void createWorkerThread() {
  new Thread() {
      @Override
        public void run() {
            super.run();
            Looper.prepare();
             mHandler = new Handler(Looper.myLooper()) {
                @Override
                public void handleMessage(Message msg) {
                    ......
                }
            };
            Looper.loop();
        }
    }.start();
}

在該工作線程中,
- 調用Looper.prepare()創建與當前線程綁定的Looper實例;
- 使用上面創建的Looper生成Handler實例;
- 調用Looper.loop()實現消息循環;

然後透過Looper的循環,在Handler的handlerMessage()中進行異步任務的循環處理。而這也正好是HandlerThread的實現。

@Override
public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}

那麼我們看下HandlerThread有哪些特點:
- HandlerThread本質上是一個線程類,它繼承了Thread
- HandlerThread有自己的內部Looper對象,通過Looper.loop()進行looper循環
- 通過獲取HandlerThread的looper對象傳遞給Handler對象,然後在handleMessage()方法中執行異步任務
- 創建HandlerThread後必須調用HandlerThread.start()方法來啟動線程

二、HandlerThread使用步驟

1、創建HandlerThread實例對象

HandlerThread handlerThread = new HandlerThread("Handler Thread");
//HandlerThread handlerThread = new HandlerThread("Handler Thread",Process.THREAD_PRIORITY_DEFAULT);

HandlerThread默認有兩個構造函數,提供了線程名參數和線程優先級參數的設置。

2、啟動HandlerThread線程

handlerThread.start();

通過start()方法就可以啟動一個HandlerThread了,該線程會不斷地循環運行。

3、通過Handler構建循環消息處理機制

Handler workderHandler = new Handler(handlerThread.getLooper(), new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                //運行在工作線程(子線程)中,用於實現自己的消息處理
                return true;
            }
        });

通過將HandlerThread綁定的Looper對象傳遞給Handler作為參數,構建一個異步的Handler對象,為了能實現耗時任務的異步執行,我們重寫了Handler的Callback接口的handleMessage()方法,當然也可以不重寫該方法,而通過post()方法進行耗時任務操作。

Handler workderHandler = new Handler(handlerThread.getLooper());
workderHandler.post(new Runnable() {
    @Override
    public void run() {
        //運行在工作線程(子線程)中,用於實現自己的消息處理
    }
});

最後,我們就可以通過調用workerHandler以發送消息的形式發送耗時任務到工作線程HandlerThread中去執行,實際上就是在Handler.Callback裡的handleMessage()中執行。

這裡要注意,在創建Handler作為HandlerThread線程消息執行者的時候必須先調用start()方法,因為創建Handler所需要的Looper參數是從HandlerThread中獲得的,而Looper對象的賦值又是在HandlerThread的run()方法中創建。

三、HandlerThread使用實例

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

/**
 * Created by Administrator on 2016/9/18.
 */
public class HandlerThreadActivity extends Activity implements Handler.Callback {
    private DBHandlerThread mDBHandlerThread;
    private Handler mUIHandler; //與UI線程相關聯的Handler

    Button mBtnQuery;
    TextView mTextResult;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mBtnQuery = (Button) findViewById(R.id.buttonQuery);
        mTextResult = (TextView) findViewById(R.id.result);
        mBtnQuery.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //將異步耗時任務發送到HandlerThread中
                //Message msg = Message.obtain(null, DBHandlerThread.MSG_QUERY_FRIENDS);
                //mDBHandlerThread.getWorkerHandler().sendMessage(msg);
                mDBHandlerThread.queryFriends();
            }
        });
        mUIHandler = new Handler(this);
        initWorkerThread();
    }

    protected void initWorkerThread() {
        mDBHandlerThread = new DBHandlerThread("Handler Thread");
        mDBHandlerThread.setUIHandlerCallBack(mUIHandler);
        mDBHandlerThread.start(); //start()後會執行Thread的run()方法
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mDBHandlerThread.setUIHandlerCallBack(null);
        mDBHandlerThread.quit();
        mDBHandlerThread = null;
    }

    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case DBHandlerThread.MSG_QUERY_FRIENDS:
                // update UI
                break;
        }
        return false;
    }
}
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;

/**
 * Created by Administrator on 2016/9/18.
 */
public class DBHandlerThread extends HandlerThread implements Handler.Callback {
    public static final int MSG_QUERY_FRIENDS = 100;

    private Handler mWorkerHandler; //與工作線程相關聯的Handler
    private Handler mUIHandler; //與UI線程相關聯的Handler

    public DBHandlerThread(String name) {
        super(name);
    }

    public DBHandlerThread(String name, int priority) {
        super(name, priority);
    }

    public void setUIHandlerCallBack(Handler handler) {
        this.mUIHandler = handler;
    }

    public Handler getWorkerHandler() {
        return mWorkerHandler;
    }

    @Override
    protected void onLooperPrepared() {
        mWorkerHandler = new Handler(getLooper(), this);
    }

    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case MSG_QUERY_FRIENDS:
                //...查詢數據庫操作...
                Message message = Message.obtain(null, MSG_QUERY_FRIENDS);
                mUIHandler.sendMessage(message); //通知UI更新
                break;
        }
        return true;
    }

    public void queryFriends() {
        Message msg = Message.obtain(null, MSG_QUERY_FRIENDS);
        mWorkerHandler.sendMessage(msg);
    }
}

四、HandlerThread源碼解析

HandlerThread的源碼不多,先看下它的構造函數:

/**
 * Handy class for starting a new thread that has a looper. The looper can then be 
 * used to create handler classes. Note that start() must still be called.
 */
public class HandlerThread extends Thread {
    int mPriority; //線程優先級
    int mTid = -1; //當前線程id
    //當前線程持有的Looper對象
    Looper mLooper;

    public HandlerThread(String name) {
        //調用父類默認的方法創建線程
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
   //帶優先級參數的構造方法
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
    ...............
}

從代碼中得知,HandlerThread帶有兩個構造函數,可傳遞兩個參數,一個參數是name,指的是線程的名稱,另一個參數是priority,指的是線程優先級。線程的優先級的取值范圍為-20到19。優先級高的獲得的CPU資源更多,反之則越少。-20代表優先級最高,19最低。我們可以根據自己的需要去設置線程的優先級,也可以采用默認的優先級,HandlerThread的默認優先級是Process.THREAD_PRIORITY_DEFAULT,具體值為0。該優先級是再run()方法中設置的,我們看它的run()方法:

public class HandlerThread extends Thread {
  /**
     * Call back method that can be explicitly overridden if needed to execute some
     * setup before Looper loops.
     */
    protected void onLooperPrepared() {
    }

    @Override
    public void run() {
        mTid = Process.myTid(); //獲得當前線程的id
        Looper.prepare(); //准備循環條件
        //通過鎖機制來獲得當前線程的Looper對象
        synchronized (this) {
            mLooper = Looper.myLooper();
            //喚醒等待線程
            notifyAll();
        }
        //設置當前線程的優先級
        Process.setThreadPriority(mPriority);
        //在線程循環之前做一些准備工作(子類可實現也可不實現)
        onLooperPrepared();
        //啟動loop
        Looper.loop();
        mTid = -1;
    }
}

run()方法主要是通過Looper.prepare()和Looper.loop()構造了一個循環線程。這裡要注意,在創建HandlerThread對象後必須調用其start()方法才能進行run()方法體的執行。

在Looper.prepare()執行後,Looper對象會被創建,然後通過同步鎖機制,將Looper對象賦值給HandlerThread的內部變量mLooper,並通過notifyAll()方法去喚醒等待線程。接著為線程賦予優先級,然後執行onLooperPrepared()方法,該方法是一個空實現,留給我們必要的時候去重寫的,主要用來做一些初始化工作。最後通過執行Looper.loop()在線程中啟動消息隊列。

我們看到在run()方法中進行了喚醒等待線程,為什麼要這麼做呢?答案就在getLooper()方法中:

/**
 * This method returns the Looper associated with this thread. If this thread not been started
 * or for any reason is isAlive() returns false, this method will return null. If this thread 
 * has been started, this method will block until the looper has been initialized.  
 * @return The looper.
 */
public Looper getLooper() {
   if (!isAlive()) { //判斷當前線程是否啟動
       return null;
   }
   // If the thread has been started, wait until the looper has been created.
   synchronized (this) {
       while (isAlive() && mLooper == null) {
           try {
               wait(); //等待,直到另一個線程調用notify()或notifyAll()來喚醒它
           } catch (InterruptedException e) {
           }
       }
   }
   return mLooper;
}

該方法用來獲取當前子線程HandlerThread所關聯的Looper對象實例。首先判斷HandlerThread線程是否存活,如果沒有存活就直接返回null,否則繼續執行,進入同步塊並判斷Looper對象是否為空以及線程是否啟動,若都滿足,則調用wait()方法進入阻塞階段,直到Looper對象被成功創建並且通過notifyAll()方法喚醒該等待線程,最後才返回該Looper對象。

Looper對象的創建是在run()方法進行的,也即在子線程中執行的,而getLooper()方法是在UI線程中調用的,若不使用等待喚醒機制,我們就無法保證在UI線程中調用getLooper()方法時Looper對象已經被創建,會面臨一個同步的問題,所以HandlerThread就通過等待喚醒機制來解決該同步問題

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved