Android Handler消息機制

  Handler建立的初衷是爲了解決子線程更新UI的問題:當Android程序第一次啓動時,會建立一條(僅一條)UI線程,用於處理與UI相關的事件(就是咱們的Activity);而爲了保證線程安全性,規定只有UI線程能夠修改Activiy中的組件。這就帶來了一個問題,Andorid程序在很大程度上都依賴於UI組件(各線程的處理結果大多都以更新UI組件來呈現),Handler的消息機制就是用於將子線程的結果數據傳遞給UI線程進行更新執行,而UI線程能夠放心地把那些耗時的操做放到子線程中,本身管好本身的UI就好了。固然,Handler沒有規定只用於更新UI,你徹底能夠當他是一個異步的消息處理(在一個線程中發送消息,在另外一個線程中處理接收到的消息),在使用時,你只要遵循它所制定的規則便可。若是有人問爲何要用子線程處理耗時任務,那什麼ANR網上有不少講解。java

1、Message

  既然是消息機制,首先來了解下消息的結構,它由Handler對象進行發送和處理,包含標識信息和任意的數據對象:android

public final class Message implements Parcelable {
    //消息標識,用戶自定義,用於收發雙方肯定對方身份
    public int what;
    //用於存放簡單的整數數據,效率較高
    public int arg1;
    //同上
    public int arg2;
    //Bundle結構(封裝的ArrayMap),用於存儲數據信息
    Bundle data;
    //控制消息發送、處理,後面詳細分析
    public void setData(Bundle data) {
        this.data = data;
    }
    public Bundle getData() {
        if (data == null) {
            data = new Bundle();
        }
        return data;
    }
    
    //處理消息的對象,即Handler
    Handler target;
    //具體的消息處理,優先級高於Handler的handleMessage,後面詳細分析
    Runnable callback;
    
    //任意類型的對象數據,當使用Messenger進行進程間消息傳遞時,常常用來存放Parcelable(序列化對象)
    public Object obj;
    //基於Message的進程間通訊管理器,將Handler做爲接收者(指定發送的消息由那個Handler處理)
    public Messenger replyTo;
    
    //標識消息是否在使用,用於消息池的回收再用控制
    static final int FLAG_IN_USE = 1 << 0;
    //指向消息池中可用的消息
    private static Message sPool;
    //替代構造函數,從消息池中獲取可用的消息(避免直接構造時的空間申請),當消息池中沒有可用時才調用構造
    //一些重載函數(帶Message、Handler、what等參數)再也不復述
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
}
Message

  能夠簡單地進行歸納下:what標識消息;arg一、arg二、data和obj存儲數據;target和callback控制消息的處理;obtain建立消息(固然經常使用的還有Handler.obtainMessage(),後面會提到);replyTo用於進程間通訊(不在本文探討範圍)。安全

 

2、Looper

  要講解Handler,就不能不說他的死黨Looper。Looper能夠說是Handler處理消息的控制者,而且擁有很是重要的屬性——存儲待處理消息的消息隊列MessageQueue。每個Looper對象在構造之初便會初始化一個消息隊列(一一對應),用於存放它所控制的那些Handler在其餘線程所發送過來的消息。首先看一下Looper的屬性和構造:多線程

public final class Looper {
    final MessageQueue mQueue;  //消息隊列
    final Thread mThread;     //當前線程標識
    //構造函數,將當前線程構建爲Looper線程(初始化線程對應的消息隊列,固然還有初始化線程私有屬性,後面爲講到)
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
}

 

 不對啊,這是要搞事情啊,構造函數怎麼是私有的呢?由於它不想讓你用這個構造,這裏涉及到多線程,Java對多線程的處理大多都給出了重載函數,一方面保證單線程使用的性能,另外一方面保證多線程使用時數據的準確性(同步與異步),因此這裏也給出了相應的封裝函數,看一下具體處理:app

public final class Looper {
    //ThreadLocal:線程私有屬性,不受其餘線程的影響,用於存儲與同進程中其餘線程不一樣的那些私有數據(帶有存儲私有數據的hash map),這裏主要用於構造控制(見prepare)
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    //Looper構造與初始化函數
    public static void prepare() {
        prepare(true);
    }
    private static void prepare(boolean quitAllowed) {
        //構造Looper對象時初始化其私有屬性,經過私有屬性保證線程只建立一次(一個線程中prepare只被調用一次)
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
}

  

  喲西,這樣咱們在一個線程中調用Looper.prepare(),便可將當前線程構建成Looper線程,同時初始化了Looper線程的消息隊列和私有屬性。而後看一下Looper具體作了哪些工做(爲了便於閱讀,只列出重要的代碼部分):異步

public static void loop() {
    //獲取當前Looper線程的Looper對象,loop函數在prepare函數以後調用,二者工做在同一線程中
    final Looper me = myLooper();
    final MessageQueue queue = me.mQueue;

    for (;;) {
        //從消息隊列中取出一條未處理的消息
        Message msg = queue.next();
        if (msg == null) {
            return;
        }
        try {
            //具體的消息處理過程,而dispatchMessage這個函數是Handler的函數,下面分析Handler時會詳細講述
            //這裏的處理也說明了一點:loop函數是在處理線程中調用的
 msg.target.dispatchMessage(msg);
        } 
        //回收處理完的Message資源(將Message的各項值還原成初始化值,而後丟到消息池中)
        msg.recycleUnchecked();
    }
}

  

  上面還有一個問題沒有解決,那就是msg.target這個對象是什麼。毫無疑問,他是Handler的對象,由於dispatchMessage是Handler類的函數,而target在這裏的做用就是將發送和處理進行關聯(在發送線程中,將Handler做爲Message的target屬性與Message進行綁定,在處理線程中,經過Message的target對Message進行處理)。這樣就經過屬性target將發送消息和處理消息聯繫起來了,即哪一個Handler對象發送消息,就由哪一個Handler進行處理。async

  消息的構造方法有不少,如Message的obtain,Handler的obtainMessage,固然也有人就喜歡直接使用Message的構造函數。Handler的obtainMessage和Message的包含Handler參數的obtain直接在構造時就肯定了他的target,有些則是等到最終的統一確認階段——發送消息時進行target賦值(Handler的sendMessage),下面給出在構造時肯定target的狀況,發送消息時的統一確認在講述Handler時會提到:ide

public class Handler {
    public final Message obtainMessage()
    {
        return Message.obtain(this);
    }
}

public final class Message implements Parcelable {
    public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;
        return m;
    }
}

  

  講到這,下面看一下Looper如何使用,提供一個最簡單的例子:函數

public class TestLooper extends Thread {
    //聲明Handler對象,供發送線程使用
    public Handler mHandler;
    public Handler mHandlernew;
    @Override
    //必須在線程中,Looper是將尋常線程初始化爲Looper線程,且不能在UI線程中
    public void run() {
        //初始化,將TestLooper轉化成Looper線程
        Looper.prepare(); //定義Handler對象,即將Handler對象與Looper綁定,使得消息隊列中的Message經過其target能夠找到正確的Handler進行處理,能夠定義多個不一樣Handler類的對象。固然,若是你在一個Looper下定義同一個Handler類的多個對象,我也布吉島結果是啥
        Handler mHandler = new MyHandler(){
            @Override
            public void handleMessage(Message msg) {...}
            };
        Handler mHandlernew = new MyHandlernew(){......}
        //開始循環處理消息
        Looper.loop();
    }
}
TestLooper

  上面的TestLooper類中,咱們首先構造了一個Looper線程TestLooper;隨後定義了兩個Handler對象,並重寫了他們的handleMessage函數(必須);最後調用loop開始等待、處理消息。在使用時,只需定義TestLooper的對象,調用其start方法便可啓動線程,以後能夠手動調用quit或發送信號量結束該Looper線程,到這裏Looper的工做就完成了(UI線程中直接定義Handler對象便可)。在發送線程中,經過Handler對象的引用(Handler對象做爲參數傳遞給發送線程)或將Handler做爲Looper對象的屬性(在發送線程中定義處理線程的對象,經過對象調用其屬性Handler)等方法發送消息,在結束Handler講解後會有使用的代碼,這裏給出簡易的圖示說明Looper的功能:oop

 

  不能在UI線程中的緣由:UI線程在建立時就已經設定爲是一個Looper線程,若是在UI線程中調用Looper.prepare(),就至關於屢次建立會拋出異常,下面簡單的看下對UI線程在Looper方面的控制

//控制應用程序的主線程執行和調度
public final class ActivityThread {
    public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();
        ........

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
}
public final class Looper {
    //用於將當前線程建立爲Looper線程,且標識爲應用的main Looper,這個函數由Android環境所調用
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
}

  

  Looper還有一些獲取當前Looper屬性的函數,這裏一併給出:

public final class Looper {
    //獲取Looper對象對應的線程標識
    public @NonNull Thread getThread() {
        return mThread;
    }
    //獲取當前Looper線程的Looper對象
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
    //獲取Looper對象對應的消息隊列
    public @NonNull MessageQueue getQueue() {
        return mQueue;
    }
    //結束此Looper
    public void quit() {
        mQueue.quit(false);
    }
}

 

3、Handler

  終於輪到Handler了,Handler提供了兩個功能:1.將打有本身標識的消息(包括可運行過程)發送給另外一個線程 2.根據打有本身標識的消息在對方線程中執行操做。

  能夠打個不是很恰當的比喻,Handler比如是一個外賣餐點,他提供兩個功能:1.點餐,不論是美團的仍是大衆點評的,只要客戶是經過我家點餐頁面點的外賣(特定Handler對象發送消息),那都把這些消息發送給我(Handler綁定的MessageQueue);2.送餐(具體操做),這是餐點在線下(相對於美團這種發送線程,線下就至關因而處理線程,而餐點就是在該線程定義的Handler)執行的,固然有可能會有一些矯情的(我要身高一米八,皮膚白皙笑起來還很陽光的帥哥送,這就是Runnable,由發送的Message決定具體操做,固然執行者仍是餐點)

  首先來看一下Handler的屬性和構造函數:

public class Handler {
    final Looper mLooper;
    final MessageQueue ;
    final Callback mCallback;
    //不帶Looper參數的構造最終走的函數,沒有的參數用null、false代替
    public Handler(Callback callback, boolean async) {
        //獲取當前Looper線程的Looper對象。咱們在使用的時候,Handler的構造函數都是在消息處理線程中執行(定義Handler對象,將Handler對象與Looper線程、消息隊列進行綁定),因此這裏獲取的是消息處理線程的Looper對象,而消息發送線程通常是經過參數傳遞得到Handler對象的引用,經過此引用來發送消息
        mLooper = Looper.myLooper();
        //當前線程不是Looper線程,拋出異常,即Handler必定要在Looper線程中定義
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        //關聯消息隊列
        mQueue = mLooper.mQueue;
        //相似回調機制,在Activity中實現interface Callback的boolean handleMessage(Message msg)函數,利用此callback建立Handler,能夠代替定義Handler對象時重寫void handleMessage(Message msg),功能相同
        mCallback = callback;
    }
    //帶Looper參數的構造最終走的函數,沒有的參數用null、false代替
    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
}

   能夠看到,Handler的構造作了兩件事:1.綁定Looper(和定義Handler對象所在的Looper(處理消息的Looper)進行綁定,同時綁定Looper對應的消息隊列),目的:綁定消息隊列,這樣無論Handler在其餘哪一個線程中發送消息,都能保證正確的送達其對應的消息隊列  2.若是須要,設置回調函數(代替handleMessage)

 

  下面來看一下他的發送消息,有不少重載函數,sendMessage(Message msg)、sendEmptyMessage(int what)、sendMessageDelayed(Message msg, long delayMillis)、sendEmptyMessageAtTime(int what, long uptimeMillis)等等,他們最終都會調用sendMessageAtTime(Message msg, long uptimeMillis):

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    //將綁定的消息隊列設置成參數傳入
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    //這就是target的統一賦值,將Handler做爲Message的target屬性,具體解析見Looper分析
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    //將消息插入到綁定的消息隊列中,這樣處理線程就能正確的接收他所綁定的Handler發送的消息
    return queue.enqueueMessage(msg, uptimeMillis);
}

boolean enqueueMessage(Message msg, long when) {
    synchronized (this) {
        //標識消息正在使用,防止此時消息被再次入隊或被回收
        msg.markInUse();
        msg.when = when;
        //消息隊列的頭結點
        Message p = mMessages;
        boolean needWake;
        //隊列爲空,當前消息做爲頭結點
        if (p == null || when == 0 || when < p.when) {
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } 
        //隊列不爲空,當前消息插入到隊尾
        else {
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p;
            prev.next = msg;
        }

        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

  

  固然還有那個誰——Runnable,他也是以消息的形式進行發送的,固然,加了一層封裝,並將他的需求設置到Message的callback(優先級大於handleMessage),這樣就能控制Handler執行他指定的需求,具體執行稍後講述,先看下他的發送,只列舉post看一下他的封裝,postAtTime(Runnable r, long uptimeMillis)、postDelayed(Runnable r, long delayMillis)等等同理:

public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    //將Runnable對象設置爲Message的callback屬性,封裝到一個空Message中
    m.callback = r;
    return m;
}

  

  這樣,不論是Message仍是Runnable,都只要走Message的統一發送接口,就能把咱們的需求發送處處理線程的消息隊列中。至此,發送線程中的操做就完成了,那要怎麼使用呢,舉個最簡單的例子(接着上面的TestLooper):

TestLooper mLooper = new TestLooper();
//啓動TestLooper線程(啓動Looper、監聽MessageQueue)
mLooper.start();
//經過Looper對象綁定的Handler屬性發送消息
Message msg = mLooper.mHandler.obtainMessage();
msg.what = 1;
mLooper.mHandler.sendMessage(msg);
Runnable rmsg = new MyRunnable();
mLooper.mHandlernew.post(rmsg);

  最後來看一下Handler的消息處理,也就是上面loop函數中的msg.target.dispatchMessage:

public void dispatchMessage(Message msg) {
    //對比getPostMessage可知,這個callback就是Runnable,handleCallback其實就是調用Runnable的run方法,可見Runnable的優先級是最高的,當有特殊要求須要處理線程執行時,可用這個
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            //回調函數,執行interface Callback的boolean handleMessage(Message msg),優先級次之
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //定義Handler對象時重寫的handleMessage函數,優先級最低,能夠把他當成時最大衆的處理方式,若是沒有特殊需求,處理他,有特殊需求,就用Runnable代替
        handleMessage(msg);
    }
}

   到這裏,Handler的發送、處理消息也就分析完了,下面看一下如何使用。

  上面已經列舉了一個TestLooper的例子,首先將一個線程類定義成一個Looper類(Looper類中定義了Handler對象),調用線程類的start函數使Looper線程開始工做,經過Looper線程對象的Handler屬性發送消息。下面列舉一個經過 消息機制更新UI的例子:

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView text =  (TextView) findViewById(R.id.editText);
        //在UI線程中,不用再建立Looper,直接定義Handler
        final Handler myHandler = new Handler(){
            @Override
            public void handleMessage(Message msg){
                if(msg.what == 0x01){
                    Bundle paramBuldle = msg.getData();
                    text.setText(paramBuldle.getString("data"));
                }            
            }
        };
        //啓動發送線程
        MyThread mthread = new MyThread(myHandler);
        mthread.start();
    }
}

public  class MyThread extends Thread{
    private Handler pHandler;
    public MyThread(Handler inputHandler){
        //處理線程中Handler對象的引用
        pHandler = inputHandler;
    }
    @Override
    public void run() {
        Message msg = myHandler.obtainMessage();
        msg.what = 0x01;
        for(int i = 0; i < input.length; i++){
            Bundle paramBuldle = new Bundle();
            paramBuldle.putString("data",input[i]);
            msg.setData(paramBuldle);
            //經過Handler對象的引用發送消息,由參數傳遞
            pHandler.sendMessage(msg);
        }
    }
}
UI Handler

 

4、

在調試smali時,常常會遇到消息機制,而消息機制的異步特性致使調試時沒法關聯(sendMessage和handleMessage),這時就要藉助消息機制的特性。

1.調試時遇到sendMessage,沒法肯定消息處理函數的狀況,首先看看找對應的handleMessage:

 invoke-virtual {v1, v0}, Landroid/os/Handler;->sendMessage(Landroid/os/Message;)Z 

這時根據消息機制的特性——發送消息的Handler也即處理消息的Handler,而這裏時Handler通用類型,那就要回溯去尋找v1的類型

 iget-object v1, p0, Lyi/decoder_yi/Helper/ThreadLooper;->pHandler:Landroid/os/Handler; 

而後查找Lyi/decoder_yi/Helper/ThreadLooper;->pHandler 這個對象在何處賦值,若是賦值的地方較多,就須要藉助調試幫忙肯定,這裏發現是在初始化中賦值,很顯然是一個參數傳遞引用   

.method public constructor <init>(Landroid/os/Handler;)V

    invoke-direct {p0}, Ljava/lang/Thread;-><init>()V

    iput-object p1, p0, Lyi/decoder_yi/Helper/ThreadLooper;->pHandler:Landroid/os/Handler;

接着就要查找這個初始化函數是在哪裏被調用,又是用什麼參數進行賦值的(肯定Handler對象),這時若是又有多個,那仍是須要藉助調試,對仍是他(由於在一些接口、虛函數處,咱們沒法用靜態的方法肯定它具體會執行哪個,只有經過動態調試去肯定)

iget-object v3, p0, Lyi/decoder_yi/Activity/AESdecode;->mhandler:Landroid/os/Handler;

invoke-direct {v2, v3}, Lyi/decoder_yi/Helper/ThreadLooper;-><init>(Landroid/os/Handler;)V

到這一步,咱們的Handler對象就能夠肯定了,就是這個mhandler,接着咱們就要找到mhandler的具體類型,而handleMessage就在這個具體類型的smali文件中(這個具體類型其實就是Handler的一個子類)

invoke-direct {v2, p0}, Lyi/decoder_yi/Activity/AESdecode$1;-><init>(Lyi/decoder_yi/Activity/AESdecode;)V

iput-object v2, p0, Lyi/decoder_yi/Activity/AESdecode;->mhandler:Landroid/os/Handler;

到這就有了,這個類就是 Lyi/decoder_yi/Activity/AESdecode$1,找到它對應的smali文件中的handleMessage,就是所要找的處理函數

 

2.若是是Runnable,要找處理函數就簡單多了,由於post的參數就是Runnable對象,找到該對象的具體類型(紅色),直接能夠到對應smali文件中查找run方法,這就是處理函數

invoke-direct {v1, p0}, Lyi/decoder_yi/Activity/ongdecode$2$1;-><init>(Lyi/decoder_yi/Activity/ongdecode$2;)V

invoke-virtual {v0, v1}, Landroid/os/Handler;->post(Ljava/lang/Runnable;)Z

 

3.回調函數handleMessage與第一種狀況相似,它尋找的是回調函數所在的類類型(紅色)

invoke-direct {v3, p0}, Lyi/decoder_yi/Activity/activity_desdecode$1;-><init>(Lyi/decoder_yi/Activity/activity_desdecode;)V

invoke-direct {v2, v3}, Landroid/os/Handler;-><init>(Landroid/os/Handler$Callback;)V

iput-object v2, p0, Lyi/decoder_yi/Activity/activity_desdecode;->mhandler:Landroid/os/Handler;

 

4.而若是是調試時找到了處理函數,而沒法肯定消息是從哪發送過來的(消息的處理函數由looper控制,調試時是一個死循環,這樣就沒法跳出這個線程去查找發送信息的函數),這種你推比較難理解一點,還好咱們有上面的思路,能夠逆着推導

找到handleMessage後,查看它所在的類,而後查找類的初始化函數調用(這個類就是Handler的子類,咱們的Handler對象的具體類型,而要執行處理函數,就要定義Handler對象,因此確定會有這步初始化工做(這個初始化<init>其實就是類的構造)),發現handleMessage在類 Lyi/decoder_yi/Activity/mappingdecode$1中,緊接着找 Lyi/decoder_yi/Activity/mappingdecode$1;-><init>的調用

invoke-direct {v2, p0}, Lyi/decoder_yi/Activity/mappingdecode$1;-><init>(Lyi/decoder_yi/Activity/mappingdecode;)V

iput-object v2, p0, Lyi/decoder_yi/Activity/mappingdecode;->mhandler:Landroid/os/Handler;

這裏v2就是構建的Handler對象,緊接着它被賦值給mappingdecode的屬性mhandler,能夠猜想,發送線程要麼是經過mappingdecode的對象直接操做屬性mhandler發送信息,要麼是將mhandler做爲參數傳遞給發送線程進行信息的發送,不管如何,他會經過mhandler來發送,因此緊跟mhandler的使用

iget-object v3, p0, Lyi/decoder_yi/Activity/mappingdecode;->mhandler:Landroid/os/Handler;

invoke-direct {v2, v3}, Lyi/decoder_yi/Helper/ThreadLooper;-><init>(Landroid/os/Handler;)V

顯然這裏是經過傳遞引用的方式來使用這個Handler對象,一樣能夠猜想,在類ThreadLooper中有一個Handler類型的屬性,它做爲 Lyi/decoder_yi/Activity/mappingdecode;->mhandler這個對象的引用,在以後會用這個引用來發送信息

.method public constructor <init>(Landroid/os/Handler;)V

    invoke-direct {p0}, Ljava/lang/Thread;-><init>()V

    iput-object p1, p0, Lyi/decoder_yi/Helper/ThreadLooper;->pHandler:Landroid/os/Handler;

 pHandler就是這個 引用,接着就跟蹤這個pHandler,看它在哪裏調用了消息發送函數

iget-object v1, p0, Lyi/decoder_yi/Helper/ThreadLooper;->pHandler:Landroid/os/Handler;

invoke-virtual {v1, v0}, Landroid/os/Handler;->sendMessage(Landroid/os/Message;)Z

好吧,就是它了(紅色)

相關文章
相關標籤/搜索