Handler相关概念

144 阅读4分钟

Message

这个是消息的载体,可以用来存放消息,可以存放的内容有以下几种。

  • what(int):用来定义Message属于何种操作。
  • obj (object):用来传递一个对象,但是要注意的是,在不同的进程中不好用,因为要用Parcelable序列化它。
  • arg1(int):int 型数据。
  • arg2(int):int型数据。
  • Bundle: 可以支持大量的数据类型。 此外,getTarget(),方法可以取得该消息的Handler对象。

Looper

个人感觉Looper有点像NIO中的Selector,它在一个单独的线程当中,可以将不同的Message发送到属于他们的Handler中去,每个线程只有一个Looper。 使用Hander处理Message需要通过Looper来完成。在ActivityThread中,系统会自动帮用户启动Looper,而在其它线程,需要用户手动调用Looper类中的方法,然后才可以正常启动Looper对象,Looper用来让一个普通线程变成Looper线程。 如下可以让一个普通线程变成一个Looper线程。

public class LooperThread extends Thread {
    @Override 
    public void run() {
        // 将当前线程初始化为Looper线程
        Looper.prepare();
         
        // ...其他处理,如实例化handler
         
        // 开始循环处理消息队列
        Looper.loop();
    }
}

prepare 方法


    public static final void prepare() {
        if (sThreadLocal.get() != null) {
            // 试图在有Looper的线程中再次创建Looper将抛出异常
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }

会发现该方法会创建一个Looper对象,但是要注意的是只会有一个Looper对象,如果第二次进入并创建的话会抛出异常。 loop 方法,该方法也很简单,就是将消息发送给对应的Handler即可,(每个Message会标明属于哪个Handler)。

public static final void loop() {
        Looper me = myLooper();  //得到当前线程Looper
        MessageQueue queue = me.mQueue;  //得到当前looper的MQ
         
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
        // 开始循环
        while (true) {
            Message msg = queue.next(); // 取出message
            if (msg != null) {
                if (msg.target == null) {
                    // message没有target为结束信号,退出循环
                    return;
                }
                // 日志
                if (me.mLogging!= null) me.mLogging.println(
                        ">>>>> Dispatching to " + msg.target + " "
                        + msg.callback + ": " + msg.what
                        );
                // 非常重要!将真正的处理工作交给message的target,即后面要讲的handler
                msg.target.dispatchMessage(msg);
                // 日志
                if (me.mLogging!= null) me.mLogging.println(
                        "<<<<< Finished to    " + msg.target + " "
                        + msg.callback);
                 
                final long newIdent = Binder.clearCallingIdentity();
                if (ident != newIdent) {
                    Log.wtf("Looper", "Thread identity changed from 0x"
                            + Long.toHexString(ident) + " to 0x"
                            + Long.toHexString(newIdent) + " while dispatching to "
                            + msg.target.getClass().getName() + " "
                            + msg.callback + " what=" + msg.what);
                }
                // 回收message资源
                msg.recycle();
            }
        }
    }

逻辑是得到当前线程的Looper→得到消息队列→循环取出消息(如果消息Target为空则退出,每个消息使用完时进行回收)。

Handler

有了Message(发送的数据),有了Looper(数据分发员),接着当然就是Handler(处理数据)了。 handler起到了处理MQ(消息队列)上的消息的作用(只处理由自己发出的消息),handler的创建会关联一个looper(默认关联当前线程的looper,不过这也可以set)。 构造方法如下:

public class handler {
    final MessageQueue mQueue;  // 关联的MQ
    final Looper mLooper;  // 关联的looper
    final Callback mCallback; 
    // 其他属性
    public Handler() {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName());
            }
        }
        // 默认将关联当前线程的looper
        mLooper = Looper.myLooper();
        // looper不能为空,即该默认的构造方法只能在looper线程中使用
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        // 重要!!!直接把关联looper的MQ作为自己的MQ,因此它的消息将发送到关联looper的MQ上
        mQueue = mLooper.mQueue;
        mCallback = null;
    }
     
    // 其他方法
}

需要注意的是:主线程会自动创建Looper,其他线程要手动创建,创建方法在Looper那里。 当然也可以通过有参构造传入一个Looper。 如下:

Handler handler= new Handler(Looper.getMainLooper());

有了Handler后我们就可以为之前的Looper线程添加Handler了

public class LooperThread extends Thread {
    private Handler handler1;
    private Handler handler2;
 
    @Override
    public void run() {
        // 将当前线程初始化为Looper线程
        Looper.prepare();
         
        // 实例化两个handler
        handler1 = new Handler();
        handler2 = new Handler();
         
        // 开始循环处理消息队列
        Looper.loop();
    }
}

线程终于有handler了,接着我们就可以通过handler来处理和发送数据了。

image.png

有了Handler后,此时的关系图如上,可以发现:

  1. 每个LooperThread(Looper线程,怎么来的?,升级来的!!!)只有一个Looper。
  2. 每个LooperThread可以有多个Handler,每个Handler都将Message发送给Looper(Looper实在是太辛苦了)。 3.Handler可以在其他线程发送消息(那肯定,要不然要Handler干嘛,但要把Handler定义在全局变量上(不一定是Static变量))。 接着就是Handler发送消息了,其API有下面那么多
  • post(Runnable)
  • postAtTime(Runnable, long)
  • postDelayed(Runnable, long)
  • sendEmptyMessage(int)
  • sendMessage(Message)
  • sendMessageAtTime(Message, long)
  • sendMessageDelayed(Message, long)

其实看起来那么多,但是也就两种,发送Runnable和发送Message,其实Runnable会包装进Message中,所以这样来看就只有一种Message而已。 包装的代码如下:

// 此方法用于向关联的MQ上发送Runnable对象,它的run方法将在handler关联的looper线程中执行
    public final boolean post(Runnable r)
    {
       // 注意getPostMessage(r)将runnable封装成message
       return  sendMessageDelayed(getPostMessage(r), 0);//从这里可以看到
    }
 
    private final Message getPostMessage(Runnable r) {
        Message m = Message.obtain();  //得到空的message
        m.callback = r;  //将runnable设为message的callback,
        return m;
    }
 
    public boolean sendMessageAtTime(Message msg, long uptimeMillis)
    {
        boolean sent = false;
        MessageQueue queue = mQueue;
        if (queue != null) {
            msg.target = this;  // message的target必须设为该handler!
            sent = queue.enqueueMessage(msg, uptimeMillis);
        }
        else {
            RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
        }
        return sent;
    }

处理数据

发送数据完了后肯定是开始处理数据了,处理数据主要有两个方法,dispatchMessage和handleMessage,其实dispatchMessage只是做过滤(过滤msg是Runnable还是Message),真正处理来时在handleMessage中。

public void dispatchMessage(Message msg) {  
        if (msg.callback != null) {  
            handleCallback(msg);  //披着Message外壳的Runnable
        } else {  
            if (mCallback != null) {  
                if (mCallback.handleMessage(msg)) {  
                    return;  
                }  
            }  
            handleMessage(msg);  
        }  
    }