C&C++ 實(shí)現(xiàn)Android Handler

本文根據(jù)眾多互聯(lián)網(wǎng)博客內(nèi)容整理后形成,引用內(nèi)容的版權(quán)歸原始作者所有,僅限于學(xué)習(xí)研究使用,不得用于任何商業(yè)用途。

使用Android的Handler之后,回去寫c++代碼時(shí),時(shí)刻懷念A(yù)ndroid里面的Handler,希望有一個(gè)c++版本的Handler。為了能自己實(shí)現(xiàn)c++版本的Handler,今天準(zhǔn)備去梳理一下這Android中的Handler。

理解Android中的Handler

Handler的適用場(chǎng)景

只要寫多線程程序,必不可少要碰到線程通信的問(wèn)題。其中一個(gè)辦法就是在工作線程中開(kāi)辟一個(gè)消息隊(duì)列,別的線程往消息隊(duì)列發(fā)消息,即可完成與本線程的通信。工作線程順序的從消息隊(duì)列中讀取消息并逐一處理。這種模型的好處是:

  1. 消息可以緩存
  2. 工作線程中消息是順序處理的,所以不需要加鎖。

Handler適用的就是這種場(chǎng)景:多個(gè)線程往一個(gè)工作線程發(fā)消息,工作線程順序挨個(gè)處理。

google設(shè)計(jì)Handler最初的原因

在安卓應(yīng)用中,多個(gè)線程去處理UI的刷新,而UI操作不是線程安全的,如果要實(shí)現(xiàn)多線程對(duì)UI的操作,就需要在每一個(gè)要進(jìn)行UI操作的地方進(jìn)行加鎖。加鎖操作引入的問(wèn)題

鎖的管理復(fù)雜,開(kāi)發(fā)者容易忘記加鎖,解鎖,或者亂加鎖造成死鎖
加鎖是有性能消耗的
Android選用java作為開(kāi)發(fā)語(yǔ)言就是看中其開(kāi)發(fā)效率,引入鎖加大開(kāi)發(fā)難度了!

因此UI操作就該由一個(gè)單一線程進(jìn)行處理,在Android中,UI線程就是應(yīng)用進(jìn)程的第一個(gè)線程–主線程。所以每一個(gè)android應(yīng)用的開(kāi)發(fā)者都會(huì)被告誡:不要在主線程里搞事情阻塞UI處理。
為了簡(jiǎn)化操作,google設(shè)計(jì)了Handler機(jī)制,開(kāi)發(fā)者只需要?jiǎng)?chuàng)建一個(gè)Handler,然后在別的線程利用主線程的handler發(fā)送消息即可完成與主線程的通信。

Handler, Looper, Thread ,Message之間的關(guān)系

名稱 含義
Looper 用來(lái)管理消息隊(duì)列的,它提供一個(gè)loop()接口,不停的從消息隊(duì)列中取消息來(lái)處理。
Thread 是Looper運(yùn)行的載體,一個(gè)Thread有且只有一個(gè)Looper。
Handler 是用來(lái)往運(yùn)行與Thread里的Looper的消息隊(duì)列發(fā)消息用的,同時(shí)Handler里有一個(gè)消息處理函數(shù),Thread會(huì)用Looper取出消息,然后在Thread里執(zhí)行這個(gè)函數(shù)來(lái)處理消息。
Message 消息內(nèi)容的封裝,里邊包含消息的處理函數(shù),用戶可以在發(fā)消息的時(shí)候動(dòng)態(tài)指定其執(zhí)行的處理函數(shù)
image.png

Android中Handler的使用

下面以實(shí)際代碼簡(jiǎn)單看下如何用Handler與主線程的通信的:

class MainActivity extends Activity {
    Handler mHandler = new Handler(){
        //主線程Looper收到消息后,會(huì)當(dāng)沒(méi)有指定具體回調(diào)函數(shù),會(huì)回調(diào)這個(gè)函數(shù)處理消息。
        //也就說(shuō)下面的函數(shù)將會(huì)在主線程內(nèi)執(zhí)行
        @Override
        public void handleMessage(Message msg) {
            switch(msg.what){
                case 1:
                    break;
                default:
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        //onCreate這里是主線程中調(diào)用的,
        //使用Handler重要的一點(diǎn)就是要明白代碼在哪個(gè)線程里執(zhí)行
        //下面,我們?cè)谧泳€程里邊給主線程發(fā)消息

        // 方式1:
        new Thread(){
            @Override
            public void run(){
                //往主線程消息隊(duì)列發(fā)消息
                //這種方式最終會(huì)在主線程中執(zhí)行 mHandler.handleMessage(msg)
                mHandler.sendEmptyMessage(1);
            }
        }.start();

        // 方式2:
        new Thread(){
            @Override
            public void run(){
                //我覺(jué)得這種方式才是Handler的精髓,用戶在代碼里隨處都可以寫處理UI操作的代碼
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        //這里的代碼將會(huì)被在主線程中執(zhí)行!真是太方便了
                    }
                });
            }
        }.start();
    }
}

主線程里邊的Looper是自動(dòng)創(chuàng)建的嗎?

初次使用Handler,都會(huì)有一個(gè)疑問(wèn):以上的代碼,沒(méi)有創(chuàng)建Looper,Looper是自動(dòng)創(chuàng)建的嗎?
實(shí)際上,不會(huì)自動(dòng)創(chuàng)建,只是在執(zhí)行到Activity的onCreate之前,系統(tǒng)已經(jīng)幫忙創(chuàng)建Looper了。
framework/base/core/java/android/app/ActivityThread.java

public static void main(String[] args) {
    //這里創(chuàng)建main線程的Looper
    Looper.prepareMainLooper();
    //...

    //死循環(huán)處理消息,知道為什么不能阻塞主線程了吧!
    //主線程就是取消息然后以阻塞的方式一個(gè)個(gè)順序執(zhí)行
    //你在主線程中搞耗時(shí)的東西,你的UI就要卡在?。?!
    Looper.loop();
}

Handler怎么關(guān)聯(lián)上Looper的?

前面說(shuō)的每一個(gè)線程里面有且僅有一個(gè)Looper,在創(chuàng)建Handler時(shí),會(huì)去獲取當(dāng)前線程的Looper,并賦值給Handler對(duì)象。
請(qǐng)看Handler類的構(gòu)造函數(shù)
frameworks/base/core/java/android/os/Handler.java

public Handler() {
    //就是這個(gè)Looper.myLooper();
    mLooper = Looper.myLooper();

    //這里就是如果工作線程沒(méi)有創(chuàng)建looper前,創(chuàng)建Handler會(huì)失敗的原因
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
}

public Handler(Looper looper) {
    ...
    //最終執(zhí)行mLooper = looper;
}

//myLooper利用的就是linux提供的線程局部存儲(chǔ)實(shí)現(xiàn)的,每個(gè)線程都有獨(dú)立的一個(gè)。
public static final Looper myLooper() {
    return (Looper)sThreadLocal.get();
}

可見(jiàn):Handler從創(chuàng)建開(kāi)始就要指定其Looper, 默認(rèn)構(gòu)造函數(shù)就是使用當(dāng)前線程的Looper。

自定義的工作線程怎么創(chuàng)建Looper和使用Handler?

如果我們要在自己的線程中使用Handler機(jī)制,則需要執(zhí)行創(chuàng)建Looper對(duì)象。
創(chuàng)建Looper的方法很簡(jiǎn)單,就是執(zhí)行Looper.prepair()即可。

class MyThread extends Thread {
    private Handler myHandler = null;
    private boolean mPrepaired = false;
    public Handler getMyHandler(){
        //沒(méi)有創(chuàng)建Looper,并loop前,handler發(fā)消息沒(méi)人去處理
        if (mPrepaired)
        return null;
        return myHandler;
    }

    @Override
    public void run() {
        //第一步:創(chuàng)建當(dāng)前線程對(duì)應(yīng)的Looper
        Looper.prepare();
        //第二步:創(chuàng)建handler,會(huì)自己匹配到當(dāng)前線程的Looper
        //如果沒(méi)有執(zhí)行第一步,這里就會(huì)報(bào)異常
        myHandler = new Handler();
        mPrepaired = true;
        //第三步:讓Looper跑起來(lái),開(kāi)始消息的讀取和消息處理
        //沒(méi)有執(zhí)行l(wèi)oop操作,handler發(fā)的消息是沒(méi)人處理的
        Looper.loop();
    }
}

HandlerThread介紹

google封裝了一個(gè)HandlerThread類,進(jìn)一步簡(jiǎn)化自定義線程消息處理,就不用那么麻煩的處理Looper的創(chuàng)建和啟動(dòng)了。HandlerThread的使用類似下面這樣:

class MyHandler extends Handler {
    //這種方式,使用的創(chuàng)建Handler所在線程的Looper
    public MyHandler(){
    
    }
    //為了與自定義的線程關(guān)聯(lián),需要指定對(duì)應(yīng)線程的looper。
    public MyHandler(Looper looper){
        super(looper);
    }
    
    @Override
    public void handleMessage(Message msg){
    
    }
}

HandlerThread t = new HandlerThread("mythread");
t.start();

// 注意那個(gè)t.getLooper(), 其實(shí)內(nèi)部是阻塞等到線程執(zhí)行完Looper.prepair()才返回的
Handler h = new MyHandler(t.getLooper());

frameworks/base/core/java/android/os/HandlerThread.java

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 {
                //這里等looper創(chuàng)建完畢
                wait();
            } catch (InterruptedException e) {
            }
        }
    }
    return mLooper;
}

如何實(shí)現(xiàn)c++版本的Handler呢?

用了Handler后,回去寫c++代碼,沒(méi)有這種東西,但是我們基本了解Handler的實(shí)現(xiàn)原理了,實(shí)現(xiàn)一個(gè)就行。

如何確保線程里有且僅有一個(gè)消息隊(duì)列?

linux的pthread庫(kù)提供在線程內(nèi)創(chuàng)建私有變量的接口:

#include <pthread.h>
int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
int pthread_setspecific(pthread_key_t key,const void *pointer));
void *pthread_getspecific(pthread_key_t key);

如何實(shí)現(xiàn)android中java的handler.post(Runnable)類型的接口?

java里有匿名函數(shù),使用很方便,其實(shí)在c++11里也支持這種寫法了。

//這里搞一個(gè)匿名函數(shù),輸入?yún)?shù)為空
handler->post([](){
    
});

偽代碼實(shí)現(xiàn)

typedef void (*MessageCallback)();

class Message {
public:
    int what;
    int arg1;
    //void* obj;
    Message* obtain();
    
    MessageCallback callback;
private:
    vector<Message> mMsgPool;
};

class Handler {
public:
    virtual ~Handler();
    void sendMessage(Message* msg) {
        mLooper->enQueue(msg);
    }
    
    void setLooper(Looper* Looper);
    //可以像java,post(new Runagle())那樣用
    void post(MessageCallback callback);
    virtual void handleMessage(Message* msg){}
    
private:
     Looper* mLooper;
};

class Looper{
public:
    static void prepair();
    static void loop();
    
    //提供一個(gè)接口用來(lái)獲取當(dāng)前線程的Looper
    static Looper* getThreadLocalLooper();
    void enQueue(Message* msg){
        mMsgQ.enQueue(msg);
    }
    Message* deQueue(){
        return mMsgQ.deQueue();
    }
private:
    MessageQueue mMsgQ;
};

class MessageQueue {
public:
    void enQueue(Message* msg);
    Message* deQueue();
};

void threadfunc(Handler* handler) {
    Message* msg = Message::obtain();
    handler->sendMessage(msg );
}

int main(int argc, char** argv) {
    Looper::prepair();
    Handler *handler = new Handler();

    //開(kāi)個(gè)線程來(lái)往主線程發(fā)消息
    std::thread t(threadfun,handler);
    t.detach();
    Looper::loop();
    
    return 0;
}

class MyThread {
public:
    MyThread() {
        mThread = NULL;
        mPrepaired = false;
    }
    Looper* getLooper(){
        while(!mPrepaired ){
            sleep(1);
        }
        return Looper::getThreadLocalLooper();
    }
    
    void start() {
        if (NULL == mThread) {
            mThread = new thread(threadfunc, this);
        }
    }
private:
    static void threadfunc(MyThread* t) {
        Looper::prepair();
        
        Looper::loop();
    }
    bool mPrepaired;
    thread* mThread;
};

//非主線程測(cè)試
int main2(int argc, char** argv) {

    //創(chuàng)建自定義線程
    MyThread t;
    
    //在線程內(nèi)構(gòu)造自己的Looper
    t.start();
    
    //Handler *handler = new XXXHandler();
    Handler *handler = new Handler();
    handler->setLooper(t.getLooper());
    
    //主線程向自定義線程的消息隊(duì)列發(fā)消息
    while ( true ) {
        Message* msg = Message::obtain();
        t.getHandler()->sendMessage(msg);
        
        /*也可以像java那樣,直接指定消息的回調(diào)處理
        t.getHandler()->post([](){
            cout << "haha" << endl;
        });*/
        sleep(1);
    }
    return 0;
}

參考文檔
自己寫個(gè)C++版本Handler來(lái)理解Android的Handler機(jī)制

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

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