Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android HandlerThread全面解析

Android HandlerThread全面解析

編輯:關於Android編程

在之前的一篇博客Android 異步消息處理機制:Looper、Handler、Message中,我們講解了Looper、Handler、Message三者的關系。實際開發中我們使用的Looper基本都是定義在UI線程中,在子線程中通過handler.post(new Runnable())可以在UI線程中更新界面,這其實是UI線程在默默的為我們服務。
其實我們完全可以借鑒UI線程Looper的思想,搞個子線程Looper,也通過Handler、Message通信,可以適用於很多場景。
一個最標准的異步消息處理線程的寫法應該是這樣:

class LooperThread extends Thread {  
      public Handler mHandler;

      public void run() {  
          Looper.prepare();   
          mHandler = new Handler() {  
              public void handleMessage(Message msg) {  
                  // process incoming messages here  
              }  
          };
          Looper.loop();  
      }  
  } 

但是Android為我們提供了一個更方便的實現類—HandlerThread。

HandlerThread實例演示

這裡的實例是用一個TextView顯示當前時間,每隔2秒更新一次。

public class MainActivity extends Activity {
    private TextView text;
    private Button button;

    private HandlerThread mThread;
    private Handler mHandler;
    private boolean isUpdateInfo;
    private static final int MSG_UPDATE_INFO = 0x110;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        text = (TextView) findViewById(R.id.text);
        button = (Button) findViewById(R.id.start_service);
        //初始化HandlerThread       
        initBackThread();
    }

    @Override
    protected void onResume() {
        super.onResume();
        //開始更新
        isUpdateInfo = true;
        mHandler.sendEmptyMessage(MSG_UPDATE_INFO);
    }

    @Override
    protected void onPause() {
        super.onPause();
        //停止更新
        isUpdateInfo = false;
        mHandler.removeMessages(MSG_UPDATE_INFO);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //釋放資源
        mThread.quit();
    }

    private void initBackThread() {
        mThread = new HandlerThread("ActivityStartArguments");
        mThread.start();
        //注意這裡Handler中傳入的是HandlerThread的Looper
        mHandler = new Handler(mThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                //UI線程中更新界面
                runOnUiThread(new Runnable(){
                    @Override
                    public void run() {
                        text.setText(getCurrentTime());
                    }
                });
                //模擬耗時操作,等待2s
                try {
                    Thread.sleep(2000);
                } catch (Exception e) {
                    Thread.currentThread().interrupt();
                }
                //發送新的任務
                if (isUpdateInfo) {
                    mHandler.sendEmptyMessage(MSG_UPDATE_INFO);
                }
            }
        };
    } 

    public String getCurrentTime(){
        Date date = new Date();          
        DateFormat df = DateFormat.getTimeInstance(DateFormat.FULL);  
        String dateStr = df.format(date);  
        return dateStr;
    }

}

可以看到我們在onCreate中,去創建和啟動了HandlerThread,並且關聯了一個mThread。然後我們分別在onResume和onPause中去開啟和暫停我們的任務,最後在onDestory中去釋放資源。
這樣就實現了每隔2s去更新我們的UI,當然我們這裡通過Thread.sleep()模擬耗時,返回了當前時間,大家可以很輕易的換成真正的數據接口。
這裡我們是在舊的Message處理完畢之後再發送新的Message給HandlerThread,其實這個過程可以是異步的,也就是說在舊的Message還沒有處理完,新的Message就可以發送了,這時新的Message會存放在Looper的MessageQueue中,待舊的Message處理完畢後,然後Looper會不斷從該MessageQueue中讀取新的Message並處理。Looper當沒有消息的時候會阻塞,有消息到來的時候就會喚醒。

運行看效果:
這裡寫圖片描述

HandlerThread源碼分析

首先看HandlerThread 啟動的地方:

mThread = new HandlerThread("ActivityStartArguments");
mThread.start();

其實我們就是初始化和啟動了一個線程,看HandlerThread內部構造:

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;

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

    protected void onLooperPrepared() {
    }

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

來看run()方法,可以看到該方法中調用了Looper.prepare(),Loop.loop(); prepare()當中會創建一個Looper對象,並且把該對象放到了該線程范圍內的變量中(sThreadLocal),在Looper對象的構造過程中,初始化了一個MessageQueue,作為該Looper對象成員變量。
loop()開啟後,會不斷的循環從MessageQueue中取消息處理,當沒有消息的時候會阻塞,有消息的到來的時候會喚醒。

接下來,我們創建了一個mHandler,是這麼創建的:

mHandler = new Handler(mThread.getLooper())

對應源碼:

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();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

mThread.getLooper()返回的就是我們在run方法中創建的mLooper。
那麼Handler的構造呢,其實就是在Handler中持有一個指向該Looper.mQueue對象,當handler調用sendMessage方法時,其實就是往該mQueue中去插入一個message,然後Looper.loop()就會取出執行。

注:如果你夠細心會發現,run方法裡面當mLooper創建完成後有個notifyAll(),getLooper()中有個wait(),這是為什麼呢?因為mLooper在一個線程中執行,而我們的handler是在UI線程初始化的,也就是說,我們必須等到mLooper創建完成,才能正確的返回getLooper();wait(),notify()就是為了解決這兩個線程的同步問題。

HandlerThread結合Service使用

我們都知道在Service裡面不能直接進行耗時操作,一般都需要去開啟子線程去做一些事情,自己去管理Service的生命周期以及子線程並非是個優雅的做法;其實我們可以結合HandlerThread完成對Service生命周期的控制,非常完美。

public class MyService extends Service {
    private HandlerThread mThread;
    private Handler mHandler;

    @Override  
    public void onCreate() {  
        super.onCreate();  
        initBackThread();
    }  

    private void initBackThread() {
        mThread = new HandlerThread("ServiceStartArguments");
        mThread.start();
        mHandler = new Handler(mThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                //線程操作
                MainActivity.showlog("processing...msg.arg1="+msg.arg1);
                try {
                    Thread.sleep(2000);
                } catch (Exception e) {
                    Thread.currentThread().interrupt();
                }
                //和當前startId一致時,才銷毀service
                stopSelf(msg.arg1);
            }
        };
    }

    @Override  
    public int onStartCommand(Intent intent, int flags, int startId) {          
        //執行新的任務,傳入startId
        Message msg = mHandler.obtainMessage();
        msg.arg1 = startId;
        mHandler.sendMessage(msg);      
        return START_STICKY;
    }  

    @Override  
    public void onDestroy() {  
        super.onDestroy();
        MainActivity.showlog("onDestroy");
        mThread.quit();
    }  

    @Override  
    public IBinder onBind(Intent intent) {
        return null;
    }  

}  

啟動Service:

    @Override  
    public void onClick(View v) {  
        switch (v.getId()) {  
        case R.id.start_service:
            showlog("click Start Service button");
            Intent it1 = new Intent(this, MyService.class);  
            startService(it1);  
            break;
        default:  
            break;  
        }  
    }  

當我們多次點擊Start Service按鈕,多次啟動Service,onStartCommand執行多次,會發送多個Message到HandlerThread,然後Looper會不斷從該MessageQueue中讀取新的Message並處理。
關鍵點來了,執行完任務後有這麼一句話,stopSelf(msg.arg1);,注意這個msg.arg1是個int值,相當於一個請求的唯一標識,其實它就是啟動Service的startId,我們每次啟動Service會把這個startId傳到onStartCommand的第三個參數,並將它置為當前的啟動ID。當調用stopSelf(msg.arg1)方法時,若該標識與當前的啟動ID一致,則銷毀service,如果在調用stopSelf(msg.arg1)之前,Service收到一個新的Start請求,ID將無法匹配,Service並不會停止。

我們看一下效果,啟動程序,連續點擊三次Start Service:
這裡寫圖片描述

可以看到,處理了三次任務,每次間隔時間2s,最後一次處理完畢後,Service自動銷毀。這樣就自動完成了Service生命周期的控制,Service完全不需要我們自己手動去銷毀。

其實所有這些過程,Android已經幫我們封裝好了,Android已經給我們提供了一個現成類,叫做IntentService,下面看具體用法。

IntentService使用

使用IntentService的要點如下:

默認在子線程中處理回傳到onStartCommand()方法中的Intent; 在重寫的onHandleIntent()方法中處理按時間排序的Intent隊列,所以不用擔心多線程(multi-threading)帶來的問題。 當所有請求處理完成後,自動停止service,無需手動調用stopSelf()方法; 默認實現了onBind()方法,並返回null; 默認實現了onStartCommand()方法,並將回傳的Intent以序列的形式發送給onHandleIntent(),您只需重寫該方法並處理Intent即可。

綜上所述,您只需重寫onHandleIntent()方法即可,當然,還需要創建一個構造方法,示例如下:

public class MyIntentService extends IntentService {        

    public MyIntentService() {
        super("MyIntentService");
    }

    @Override  
    public void onHandleIntent(Intent intent) {
        //線程操作
        MainActivity.showlog("processing...");
        try {
            Thread.sleep(2000);
        } catch (Exception e) {
            Thread.currentThread().interrupt();
        } 
    }

    @Override  
    public void onDestroy() {  
        super.onDestroy();
        MainActivity.showlog("onDestroy");
    }  

}  

是不是簡單多了呢?其實效果和上面是一樣的。如果你還希望在IntentService的繼承類中重寫其他生命周期方法,如onCreate()、onStartCommand() 或 onDestroy(),那麼請先調用各自的父類方法以保證子線程能夠正常啟動。 比如,這裡我重寫了onDestroy()方法,添加了Log信息。
注:除onHandleIntent()外,onBind()方法也無需調用其父類方法。

連續點擊三次Start Intent Service按鈕,Log如下:
這裡寫圖片描述

下面我們再稍微看下IntentService的源碼:

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;

    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }


    public IntentService(String name) {
        super();
        mName = name;
    }


    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public void onStart(Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }


    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }


    protected abstract void onHandleIntent(Intent intent);
}

可以看到它在onCreate裡面初始化了一個HandlerThread,然後每次調用onStart的時候,通過mServiceHandler發送一個消息,消息中包含我們的intent。然後在該mServiceHandler的handleMessage中去回調onHandleIntent(intent);就可以了。果然是這樣,onStartCommand中回調了onStart,onStart中通過mServiceHandler發送消息到該handler的handleMessage中去。最後handleMessage中回調onHandleIntent(intent)。回調完成後回調用 stopSelf(msg.arg1),當任務完成銷毀Service回調onDestory,可以看到在onDestroy中釋放了我們的Looper:mServiceLooper.quit()。

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