Android Handler消息机制

  Handler创建的初衷是为了解决子线程更新UI的问题:当Android程序第一次启动时,会创建一条(仅一条)UI线程,用于处理与UI相关的事件(就是我们的Activity);而为了保证线程安全性,规定只有UI线程可以修改Activiy中的组件。这就带来了一个问题,Andorid程序在很大程度上都依赖于UI组件(各线程的处理结果大多都以更新UI组件来呈现),Handler的消息机制就是用于将子线程的结果数据传递给UI线程进行更新执行,而UI线程可以放心地把那些耗时的操作放到子线程中,自己管好自己的UI就行了。当然,Handler没有规定只用于更新UI,你完全可以当他是一个异步的消息处理(在一个线程中发送消息,在另一个线程中处理接收到的消息),在使用时,你只要遵循它所制定的规则即可。如果有人问为什么要用子线程处理耗时任务,那什么ANR网上有很多讲解。

一、Message

  既然是消息机制,首先来了解下消息的结构,它由Handler对象进行发送和处理,包含标识信息和任意的数据对象:

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标识消息;arg1、arg2、data和obj存储数据;target和callback控制消息的处理;obtain创建消息(当然常用的还有Handler.obtainMessage(),后面会提到);replyTo用于进程间通信(不在本文探讨范围)。

 

二、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对多线程的处理大多都给出了重载函数,一方面保证单线程使用的性能,另一方面保证多线程使用时数据的准确性(同步与异步),所以这里也给出了相应的封装函数,看一下具体处理:

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进行处理。

  消息的构造方法有很多,如Message的obtain,Handler的obtainMessage,当然也有人就喜欢直接使用Message的构造函数。Handler的obtainMessage和Message的包含Handler参数的obtain直接在构造时就确定了他的target,有些则是等到最终的统一确认阶段——发送消息时进行target赋值(Handler的sendMessage),下面给出在构造时确定target的情况,发送消息时的统一确认在讲述Handler时会提到:

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的功能:

 

  不能在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);
    }
}

 

三、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

 

四、

在调试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

好吧,就是它了(红色)

posted @ 2017-10-25 10:18  九鼎煮鸡蛋  阅读(624)  评论(0编辑  收藏  举报