Handler机制

Handler机制

  1. Handler机制的简单介绍
    1. 1 Handler

Handler对象允许你发送消息和Runnable对象加入到MessageQueue队列,同时也可以处理从MessageQueue中取出来的消息和Runnable对象。每一个Handler实例都跟一个线程和MessageQueue绑定在一起。当新的Handler创建时,Handler对象将会跟创建时所在的线程的线程实例和MessageQueue绑定。所以,Handler将会把消息和Runnable实例发送到和它绑定的MessageQueue,并且执行或处理从该队列分发出来的消息和Runnable对象。

  1. 2Looper

每一个线程最多只有一个Looper对象。使用Looper时需要在所在的线程调用静态方法Looper.prepare() ,这个方法会创建当前线程所属的Looper对象和MessageQueue对象。然后调用Looper.loop()方法开启一个死循环不断的从MessageQueue中取出消息交给Handler处理。

 

  1. 3MessageQueue

MQ本质上是一个存储Message的单链表,通过Handler对象发送Message对象加入到单链表里面,同时通过loop循环取出分发出来。

 

  1. 4Message

Message类描述了消息的类型以及它所承载的数据。创建Message最好的方法是调用Message.obtain()或者Handler.obtainMessage(),这两个方法是从一个Message对象的回收池获取对象。避免了频繁地创建Message对象产生额外的开销。

 

Handler机制大概原理如下图所示,首先Handler把Message发送到MessageQueue,同时Looper不断的从MessageQueue取出待处理的Message,然后回调Handler的dispatchMessage方法,把消息委托给Handler处理。

 

 

2.Handler之消息发送

Handler类包含如下方法,用于发送、处理消息
void handleMessage(Message msg):处理消息的方法。该方法在创建Handler时重写。处理消息
final boolean hasMessage(int what):检查消息队列中是否包含what属性为指定值(参数中的what)的消息
final boolean hasMessage(int what,Object object):检查消息队列中是否包含what属性值为指定值且object属性值为指定对象的消息
多个重载的 Message obtainMessage():获取Message,经常用于创建一个Message对象。例如obtainMessage(int what,Object object)。
sendEmptyMessage(int what):发送空消息
final sendEmptyMessageDelayed(int what,long delayMills):指定delayMills毫秒后发送空消息。
final boolean sendMessage(Message msg):立即发送消息
final boolean sendMessageDelayed(Message msg,long delayMills):指定delayMill毫秒后发送消息
 

 

一般是通过Handler中以下方法来创建的

public final Message obtainMessage()

public final Message obtainMessage(int what)

public final Message obtainMessage(int what, Object obj)

public final Message obtainMessage(int what, int arg1, int arg2)

public final Message obtainMessage(int what, int arg1, int arg2, Object obj)

3.Handler的使用方式

3.1子线程中创建Handler

*  class LooperThread extends Thread {
*      public Handler mHandler;
*      public void run() {
*          Looper.prepare();
*          mHandler = new Handler() {
*              public void handleMessage(Message msg) {
*                  // process incoming messages here
*              }
*          };
*          Looper.loop();
*      }
*  }

3.2UI线程创建Handler

static class MyHandler extends Handler{

        private WeakReference<HandlerActivity> activityRef;

        private MyHandler(HandlerActivity activity){

            this.activityRef = new WeakReference<>(activity);

        }

        @Override

        public void handleMessage(Message msg) {

            if (activityRef!=null) {

                HandlerActivity activity = activityRef.get();

                if (activity != null) {

                 ......

                }

            }

        }

4.Handler时序图

 

5.代码解析

5.1 发送消息

//发送消息,最终都会调用到此方法
689    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
690        MessageQueue queue = mQueue;
691        if (queue == null) {
692            RuntimeException e = new RuntimeException(
693                    this + " sendMessageAtTime() called with no mQueue");
694            Log.w("Looper", e.getMessage(), e);
695            return false;
696        }
697        return enqueueMessage(queue, msg, uptimeMillis);
698    }

//加入消息队列
740    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
741        msg.target = this;
742        if (mAsynchronous) {
743            msg.setAsynchronous(true);
744        }
745        return queue.enqueueMessage(msg, uptimeMillis);
746    }
   boolean enqueueMessage(Message msg, long when) {
537        if (msg.target == null) {
538            throw new IllegalArgumentException("Message must have a target.");
539        }
540        if (msg.isInUse()) {
541            throw new IllegalStateException(msg + " This message is already in use.");
542        }
543
544        synchronized (this) {
545            if (mQuitting) {
546                IllegalStateException e = new IllegalStateException(
547                        msg.target + " sending message to a Handler on a dead thread");
548                Log.w(TAG, e.getMessage(), e);
549                msg.recycle();
550                return false;
551            }
552
553            msg.markInUse();
554            msg.when = when;
555            Message p = mMessages;
556            boolean needWake;
557            if (p == null || when == 0 || when < p.when) {
558                // New head, wake up the event queue if blocked.
559                msg.next = p;
560                mMessages = msg;
561                needWake = mBlocked;
562            } else {
563                // Inserted within the middle of the queue.  Usually we don't have to wake
564                // up the event queue unless there is a barrier at the head of the queue
565                // and the message is the earliest asynchronous message in the queue.
566                needWake = mBlocked && p.target == null && msg.isAsynchronous();
567                Message prev;
568                for (;;) {
569                    prev = p;
570                    p = p.next;
571                    if (p == null || when < p.when) {
572                        break;
573                    }
574                    if (needWake && p.isAsynchronous()) {
575                        needWake = false;
576                    }
577                }
578                msg.next = p; // invariant: p == prev.next
579                prev.next = msg;
580            }
581
582            // We can assume mPtr != 0 because mQuitting is false.
583            if (needWake) {
584                nativeWake(mPtr);
585            }
586        }
587        return true;
588    }

5.2 消息队列的分发处理

public static void loop() {
//可以看到,在调用Looper.prepare()之前是不能调用该方法的,不然又得抛出异常了
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long traceTag = me.mTraceTag;
            if (traceTag != 0) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            try {
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "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);
            }

            msg.recycleUnchecked();
        }
    }
/*
这里我们看到,mLooper()方法里我们取出了,当前线程的looper对象,然后从looper对象开启了一个死循环 
不断地从looper内的MessageQueue中取出Message,只要有Message对象,就会通过Message的target调用
dispatchMessage去分发消息,通过代码可以看出target就是我们创建的handler。我们在继续往下分析Message的分发
*/
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}
/*好了,到这里已经能看清晰了
可以看到,如果我们设置了callback(Runnable对象)的话,则会直接调用handleCallback方法
*/
private static void handleCallback(Message message) {
        message.callback.run();
    }
//即,如果我们在初始化Handler的时候设置了callback(Runnable)对象,则直接调用run方法。比如我们经常写的runOnUiThread方法:
runOnUiThread(new Runnable() {
            @Override
            public void run() {
                
            }
        });
public final void runOnUiThread(Runnable action) {
      if (Thread.currentThread() != mUiThread) {
          mHandler.post(action);
      } else {
          action.run();
      }
  }
  /*
而如果msg.callback为空的话,会直接调用我们的mCallback.handleMessage(msg),即handler的handlerMessage方法。由于Handler对象是在主线程中创建的,
所以handler的handlerMessage方法的执行也会在主线程中。
  */

5.Handler内存泄漏

1. Handler在使用过程中,需要注意的问题之一便是内存泄漏问题。

为什么会出现内存泄漏问题呢?

首先Handler使用是用来进行线程间通信的,所以新开启的线程是会持有Handler引用的,

如果在Activity等中创建Handler,并且是非静态内部类的形式,就有可能造成内存泄漏。

 

首先,非静态内部类是会隐式持有外部类的引用,所以当其他线程持有了该Handler,线程没有被销毁,则意味着Activity会一直被Handler持有引用而无法导致回收。

同时,MessageQueue中如果存在未处理完的Message,Message的target也是对Activity等的持有引用,也会造成内存泄漏。

解决的办法:

 (1). 使用静态内部类+弱引用的方式:

 

  静态内部类不会持有外部类的的引用,当需要引用外部类相关操作时,可以通过弱引用还获取到外部类相关操作,弱引用是不会造成对象该回收回收不掉的问题,不清楚的可以查阅JAVA的几种引用方式的详细说明。

 

private Handler sHandler = new TestHandler(this);

 

static class TestHandler extends Handler {

    private WeakReference<Activity> mActivity;

    TestHandler(Activity activity) {

        mActivity = new WeakReference<>(activity);

    }

 

    @Override

    public void handleMessage(Message msg) {

        super.handleMessage(msg);

        Activity activity = mActivity.get();

        if (activity != null) {

            //TODO:

        }

    }

}

 (2). 在外部类对象被销毁时,将MessageQueue中的消息清空。例如,在Activity的onDestroy时将消息清空。

 

@Override

protected void onDestroy() {

    handler.removeCallbacksAndMessages(null);

    super.onDestroy();

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飞_哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值