Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android多線程消息處理機制 HandlerThread案例分析

Android多線程消息處理機制 HandlerThread案例分析

編輯:關於Android編程

Main線程已經有了looper且已啟動,意味著包含了消息隊列。
所有沒有指定looper對象而創建的handler,使用的都是main的looper。
這樣的handler發送的message都放到了main線程的looper管理的消息隊列中了。

發現有HandlerThread這個類,以前沒用過,總以為是在Thread中內置了一個Handler。
在面試中也總是被問到,很心煩,於是索性來研究一下,正好前面也整體分析了looper,handler等技術。

先來看下源碼吧:

/**
 * 創建一個新的線程類,同時包含有一個Looper. 然後可以使用這個Looper創建一個Handler.
 * Note:包含有一個Looper意味著什麼?
 * 啟動線程時啟動looper消息循環,looper內置有messageQueue。這個線程專用來處理消息的了
 * 看文章:Android多線程消息處理機制,http://blog.csdn.net/fesdgasdgasdg/article/details/52081773
 */
public class HandlerThread extends Thread {
    int mPriority;//線程優先級
    int mTid = -1;//進程id
    Looper mLooper;//核心對象


    public HandlerThread(String name) {
        super(name);
		//獲取線程默認的優先級,0
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    
    /**
     * 構造函數
     * @param name 線程名稱
     * @param priority 線程優先級. 必須從android.os.Process獲取,而不能從java.lang.Thread獲取.
     */
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
    
    /**
     * 如果需要在looper循環前執行一些操作時,可以顯式的重寫此方法
     */
    protected void onLooperPrepared() {
    }


	/**
	 * 線程的run方法,在線程start()後,就開始執行run方法,
	 * 在run裡面Looper.prepare()創建looper...
	 * 此內容參考:Android多線程消息處理機制,http://blog.csdn.net/fesdgasdgasdg/article/details/52081773
	 */
    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();//循環前的回調
        Looper.loop();
        mTid = -1;
    }
    
    /**
     * 此方法返回當前線程關聯的looper,
     * 如果當前線程沒有啟動或者已停止等原因,此方法返回null
     * 如果此方法已經啟動,此方法將阻塞知道looper被初始化 
     * @return The looper.
     */
    public Looper getLooper() {
	    //如果當前線程不處在運行狀態,則返回null
        if (!isAlive()) {
            return null;
        }
        
        // 如果此方法已經啟動,此方法將阻塞知道looper被初始化
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }


    /**
     * 退出線程的looper循環.
     * 使looper線程終止消息循環,不再處理消息隊列中的任何消息
	 * 當looper調用quit之後,任何嘗試post message到messageQueue中的操作都會失敗
     * 比喻:Handler的sendMessage(Message)方法會返回false
     * 使用這個方法可能不安全,因為在looper停止之前可能有message沒有被執行。
     * 考慮使用quitSafely()方法代替quit()方法,以確保所有待完成的工作有序完成。
     * @return 如果looper調用了quit,則返回true。如果線程還沒有開始執行則返回false
     *
     * @see #安全退出
     */
    public boolean quit() {
        Looper looper = getLooper();//獲取當前線程的looper
        if (looper != null) {
            looper.quit();
            return true;//成功的調用了quit,返回true
        }
        return false;
    }


    /**
     * 安全的退出線程looper循環。
     * 當messageQueue中所有已到期的message都處理完後,終止looper線程的消息循環。
     * 但是由於延時的消息需要等待,故這些消息將不會被處理。
     * Pending delayed messages with due times in the future will not be delivered.
     * 當looper調用quit之後,任何嘗試post message到messageQueue中的操作都會失敗
     * 比喻:Handler的sendMessage(Message)方法會返回false
	 * 如果線程沒有被啟動,或者已經結束則返回false。否則在調用quitSafely()之後返回true。
     */
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }


    /**
     * 返回此線程的標識符. 參考Process.myTid().
     */
    public int getThreadId() {
        return mTid;
    }
}

 

這麼一分析,是否都清楚了?無非就是個Thread,在裡面內置了一個Looper,looper會自帶一個MessageQueue。

在Thread的run方法中使用looper.prepqre()創建了looper。然後賦給當前線程的looper成員變量,供外面的handler使用。

接著調用looper.loop()方法啟動消息循環。

前面也講過可以自己創建LooperThread線程,HandlerThread就是google提供的經典實例,只不過HandlerThread裡面提供了線程安全訪問,退出消息循環等方法。

具體的用法簡單的1C, 下面就用自己創建的LooperThread為例,如果你不喜歡的話,直接把LooperThread替換成現有的HandlerThread類即可。

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private Handler mHandler;
    private LooperThread thread;
    private TextView show;
    private Button start;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        show = (TextView) findViewById(R.id.show);
        start = (Button) findViewById(R.id.start);
        start.setOnClickListener(this);
        init();
    }

    private void init() {
        thread = new LooperThread();
        thread.start();
        while (thread.getLooper() == null) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        mHandler = new Handler(thread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                //處理消息
                Log.v("looper的handler", Thread.currentThread().getName() + " - " + msg.what);
                //修改textview會報錯的。
                //show.setText("");
            }
        };
    }

    @Override
    public void onClick(View v) {
        mHandler.sendEmptyMessage(200);
    }

    /**
     * Looper線程
     */
    class LooperThread extends Thread {
        private Looper looper;

        public Looper getLooper() {
            return looper;
        }

        @Override
        public void run() {
            super.run();
            Looper.prepare();
            looper = Looper.myLooper();
            Looper.loop();
        }
    }
}


 

初始化的時候先創建LooperThread線程,且啟動。

然後裡面就會有looper了。

在創建handler的時候傳入上面的looper。那麼此時的handler已和LooperThread的消息隊列綁定了。這個handler所發送和處理的消息只經過LooperThread的消息循環,

跟UI線程已有的消息循環沒關系了。

 

最後有一點需要注意:

 

public void handleMessage(Message msg) {
                //處理消息
                Log.v("looper的handler", Thread.currentThread().getName() + " - " + msg.what);
                //修改textview會報錯的。
                //show.setText("");
            }

這個處理消息的方法,不能再處理UI線程創建的UI控件了。如果你真心想修改某一個ui,那的保證這個ui必須在LooperThread裡面創建和添加到window中去。

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