手把手帶你實現(xiàn)Handler

在開始實現(xiàn)之前我們需要一個Handler模型進行推導

handler.png

ThreadLocal:

呵呵呵,沒有我你們能干啥?

synchronized:笑而不語

Lock:樓上sb?還沒被開除?

Condition:2樓sb

Handler:氣氛Hi起來!動次打次!

Message:朋友圈幫我集個贊!

Looper:.......

MessageQueue:退群了

旁白:我們通過上圖的模型可以推導出,要想實現(xiàn)Handler機制少不了以下的一些隊友的配合

1.Message

TA就是一個對象,封裝了一些信息,使用者可以將自己想要的信息傳到里面去!里面只有幾個成員變量,沒別的!

private int what;//用過Handler的肯定經(jīng)常用吧

private Object;//跟上面的成員變量一個意思

private Hander target;//這是個重點,為什么要封裝一個Handler對象呢?因為最終處理消息的是Handler啊.如果沒有這個對象,我們?nèi)绾?調(diào)用)把消息傳遞給Handler的handleMessage方法呢?

2.MessageQueue

a.MessageQueue 是一個鏈表結(jié)構(gòu)的消息隊列(數(shù)組實現(xiàn))

b.主要提供存,取,對存取的線程進行鎖控制,避免多線程并發(fā)訪問的問題.

c.MessageQueue可提供的功能列表:

void enqueueMessage(Message message);//將消息對象添加到鏈表中

Message next();//從鏈表中取出消息對象

boolean hasNext();//消息隊列中的數(shù)據(jù)是否為空

3.Looper

死循環(huán)!死循環(huán)!死循環(huán)!死循環(huán)!死循環(huán)!死循環(huán)!死循環(huán)!死循環(huán)!

Looper中可提空的功能列表:

void prepare();//初始化Looper,將Looper綁定到ThreadLocal

void loop();//開始死循環(huán)的從MessageQueue中取出消息對象.

Looper myLooper();//獲取ThreadLocal中存儲的Looper對象

4.Handler

負責發(fā)送消息,處理消息

可提供的功能列表:

void handleMessage(Message message);

void sendMessage(Message message);//發(fā)送消息就是將你的Message對象放到MessageQueue中去

void dispatchMessage(Message message);//沒別的,就是調(diào)用了一下handleMessage,之所以會有這個方法是因為我們更合理的進行封裝.當Looper.loop方法循環(huán)取消息時會調(diào)用這個方法.

5.ThreadLocal

在線程中扮演了很重要的角色

引:ThreadLocal是解決線程安全問題一個很好的思路,它通過為每個線程提供一個獨立的變量副本解決了變量并發(fā)訪問的沖突問題。在很多情況下,ThreadLocal比直接使用synchronized同步機制解決線程安全問題更簡單,更方便,且結(jié)果程序擁有更高的并發(fā)性.

在Handler中使用ThreadLocal來進行數(shù)據(jù)隔離,目的就是更好的隔離Looper對象,畢竟我們的Looper是存儲在了ThreadLocal 的map中.

6.Lock,Condition

為了解決線程鎖,隊列為空,或者滿員的問題,當隊列為空或者隊列滿員的情況我們要通過Lock去阻塞(消息不能被添加到隊列中,取出消息的時候也同理),直到隊列中有消息被消費了(處理了).畢竟我們得隊列長度是有限的嘛,要不然就內(nèi)存溢出了嘛,老天爺會懲罰你的嘛.

Condition:與Lock配對使用,我們這里之所以用Condition而不用synchronized的原因就是Condition 與Lock配合,可以指定"誰"來解鎖.顯而易見的用synchronized來實現(xiàn)是非常麻煩的,Lock和Condition 更適合復雜的并發(fā)場景.

OK!我們經(jīng)過了上面的小圖片的推導,大體的結(jié)構(gòu)就已經(jīng)出來了

首先Handler中的代碼:

public class Handler {

    private MessageQueue mMessageQueue;
    private Looper myLooper;
    public void handleMessage(Message message){

    }
    //handler 在主線程中初始化
    public Handler(){
        //拿到Looper
        myLooper = Looper.myLooper();
        //拿到Looper中的消息隊列

        mMessageQueue = myLooper.messageQueue;

    }
    public void dispathMessage(Message message){
        handleMessage(message);
    }
    public void sendMessage(Message message){
        //發(fā)送消息就是將消息實體入隊到消息隊列中
        message.target = this;
        mMessageQueue.enqueueMessage(message);

    }

}

Looper中的代碼:

public class Looper {


    MessageQueue messageQueue;
    public static void prepare(){
        if (mThreadLocal.get() != null){
            throw new RuntimeException("Only on Looper may be created per thread");
        }
        mThreadLocal.set(new Looper());
    }
    public Looper(){
        messageQueue = new MessageQueue();
    }
    private static final ThreadLocal<Looper> mThreadLocal = new ThreadLocal<>();

    public static void loop(){
       Looper me =  myLooper();
       if (me ==null){
           throw  new RuntimeException("thread looper is null.call Looper.prepare()");
       }
       //獲取到loop中的消息隊列
        MessageQueue messageQ = me.messageQueue;
        for (;;){

            if (!messageQ.hasNext()){
                continue;
            }else {
                Message message = messageQ.next();
                //如果消息為null,繼續(xù)處理下一個
                if (message ==null){
                    continue;
                }
                //拿到消息,將消息發(fā)送給Handler
                message.target.dispathMessage(message);
            }
        }
    }
    public static Looper myLooper(){
        if (mThreadLocal.get() ==null){

            throw new RuntimeException("not call Looper.prepare()");
        }
        return mThreadLocal.get();
    }
}

Message中的代碼:

public class Message {
     Handler target;//消息的處理對象
    public int what;
    public Object object;

    @Override
    public String toString() {
        return object.toString()+":what:"+what;
    }
}

MessageQueue中的代碼

public class MessageQueue {

    /**
     * 到底能不能再子線程中更新UI?
     * Android 通過判斷當前線程是否是ViewRootImpl的創(chuàng)建線程來檢查是否可以更新UI
     * 調(diào)用View.addView的時候會調(diào)用初始化ViewRootImpl,也就是說如果我在子線程中addView,
     * 那么我就可以在子線程中更新View.
     */
    private int MAX_QUEUE = 50;
    /**
     *  消息隊列應該有上限,否則會造成內(nèi)存溢出
        當隊列沒有消息的時候需要阻塞,直到有消息進來
        當隊列消息滿的時候需要阻塞,直到有消息被處理
     */
    Message[] messageQueue;

    private Lock mLock;//鎖對象,為了可以控制消息隊列什么時候可以繼續(xù)出隊或者入隊
    private Condition popCondition;//可以選擇性的解鎖
    private Condition pushCondition;

    private int messageQueueCout;//隊列中的消息的當前是數(shù)量
    private int pushIndex;//入隊時的索引
    private int popIndex;//出隊時的索引
    //出隊消息:運行在子線程中

    public void enqueueMessage(Message message){
        try {
            //加鎖類似于synchronized (),因為Condition可以選擇性解鎖
            mLock.lock();
            //當消息隊列滿了,子線程停止發(fā)送消息,阻塞
            //為了避免多個子線程同級喚醒,導致的單次判斷問題.使用while 不斷進行檢查.
            while (messageQueueCout == MAX_QUEUE){
                pushCondition.await();
            }
            //將消息推進數(shù)組中
            messageQueue[pushIndex] = message;
            pushIndex++;
            if (pushIndex == MAX_QUEUE){
                pushIndex = 0;
            }
            messageQueueCout++;
            //如果有產(chǎn)品被生產(chǎn),通知消費者可以繼續(xù)消費Message
            popCondition.signalAll();//??
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            mLock.unlock();
        }

    }
    //出隊消息:運行在主線程中(消費隊列中的消息)
    public Message next(){
        Message message = null;
        try {
            //當消息隊列為空的時候阻塞消費者,讓消費者不能繼續(xù)消費消息
            mLock.lock();
            while (messageQueueCout ==0){
                popCondition.await();
            }

            message = messageQueue[popIndex];
            messageQueue[popIndex] = null;//當一個數(shù)據(jù)被出隊時,釋放內(nèi)存
            popIndex++;
            if (popIndex == MAX_QUEUE){
                popIndex=0;
            }
            //當隊列數(shù)量不滿時通知子線程可以繼續(xù)入隊消息
            messageQueueCout--;

            //通知生產(chǎn)者可以繼續(xù)生產(chǎn)Message
            pushCondition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            mLock.unlock();
        }

        return message;
    }
    public boolean hasNext(){
        return messageQueue.length!=0;
    }
    public MessageQueue(){
        //初始化消息隊列
        messageQueue = new Message[MAX_QUEUE];
        mLock = new ReentrantLock();//初始化鎖對象
        this.pushCondition = mLock.newCondition();
        this.popCondition = mLock.newCondition();

    }

}

好的!我們來寫個Main方法調(diào)用一下!

public class Main {

    public static void main(String[] args) {

        Looper.prepare();
        Handler mHandler = new Handler(){
            @Override
            public void handleMessage(Message message) {
                System.out.println(message);
            }
        };
        new Thread(new Runnable() {
            @Override
            public void run() {
                Message m = new Message();
                m.object="hello";
                mHandler.sendMessage(m);
            }
        }).start();
        Looper.loop();
    }
}

這就是為什么在Activity main方法中會調(diào)用 Looper.prepare()...Looper.loop();
因為你要將自己持久化到內(nèi)存中去嘛!要不然就死掉了嘛!
通過一個死循環(huán)+Handler
當我們在子線程中創(chuàng)建Handler的時候,也要模仿Activity的Handler創(chuàng)建方式才對嘛!
在哪個線程中創(chuàng)建了Handler 對于Handler本身來說誰就是主線程.因為對于Handler來說線程是相對關(guān)系的!

  public class Activity{
    public static main(String[] args){
      ......
      Looper.prepareMainLoop();
      ............
      Looper.loop();
    }
  }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容