handler

xiaoxiao2021-02-28  87

Handler总结

一、概述

Handler、Looper、Message介绍handler内存泄露处理主线程Looper

Handler、Looper、Message

Looper介绍

构造方法:私有的,里面创建了一个MessageQuery对象,绑定了当前的线程

private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }

prepare():初始化当前线程为looper线程,先判断当前线程变量中是否创建过Looper对象,如果创建过,抛出异常,如果没有创建过,则创建

private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }

这也是为什么只能调用一次Looper.prepare()的原因,也正因为如此,一个线程中只能有一个Looper对象,一个Looper对象中只有一个MessagerQueue.

loop():启动循环执行消息方法

public static void loop() { 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 Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); 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(); } }

可以看到,里面有个for(;;)循环,从消息队列中取消息Message msg = queue.next();,消息为空就会阻塞。取出消息之后调用msg.target.dispatchMessage(msg);分发消息,获取Message的target,这个target就是handler对象,也就是会调用handler的dispatchMessage方法分发消息。

myLooper():获取当前线程绑定的Looper对象

public static @Nullable Looper myLooper() { return sThreadLocal.get(); }

myQueue():获取当前线程的MessageQueue对象,MessageQueue其实是绑定在Looper中的,线程和Looper绑定

public static @NonNull MessageQueue myQueue() { return myLooper().mQueue; }

quit():退出,调用MessageQueue方法,一般在destroy调用

总结:

Looper对象调用prepare与当前线程绑定,并创建MessageQueue,保证一个线程只能有一个Looper实例,一个Looper实例也只有一个MessageQueueloop方法循环的从MessageQueue中取消息,交给Message.target的dispatchMessage去处理,其实就是handler的dispatchMessage方法使用ThreadLocal为了将Looper实例存在线程中,因为线程是不能保存数据,使用ThreadLocal本地线程变量则可以,方便在handler中获取当前线程的Looper对象,将Looper对象与handler绑定

Hanlder介绍

构造方法

public Handler(Callback callback, boolean async) { 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()); } } mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }

在构造方法中获取当前线程的Looper对象,并将Looper对象的MessageQueue绑定,也就是在创建Handler时handler实例就和当前线程的Looper对象绑定了,在哪个线程创建的Handler对象,这个handler就接受哪个线程的消息。

dispatchMessage(Message msg)方法

public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } private static void handleCallback(Message message) { message.callback.run(); } public void handleMessage(Message msg) { }

先判断Message中的callback不为空就执行message的callback.run()方法,否则执行handleMessage方法,消息的最终回调是由我们控制的,我们在创建handler的时候都是复写handleMessage方法,然后根据msg.what进行消息处理。

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) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }

可以看到,发送消息会把当前hander对象赋值给Message.target,loop方法中取出每个msg然后交给msg.target.dispatchMessage(msg)去处理消息;赋值target之后再将该消息添加到消息队列中。总之,调用sendMessage会把消息添加到Looper对象的MessageQueue中,然后looper对象的loop放循环取出消息,交给handler的dispatchMessage方法处理,进而执行我们复写的handleMessage方法。

sendEmptyMessage(int what):空消息其实内部还是会创建一个Message,最终也会调用到sendMessageAtTime(Message msg, long uptimeMillis)

public final boolean sendEmptyMessage(int what) { return sendEmptyMessageDelayed(what, 0); } public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { Message msg = Message.obtain(); msg.what = what; return sendMessageDelayed(msg, delayMillis); }

post(Runnable r):其实也会创建一个Message,dispatchMessage方法会执行第一个判断,即callback不为空

public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }

内部会创建一个Message对象,并把runnable赋值给Message的callback,在dispatchMessage方法中会执行第一个判断,即执行run方法

总结:

创建Handler(构造方法)时通过ThreadLoacl获取当前线程的Looper对象,将Looper、MessageQueue和Handler绑定发送消息可以使用sendMessage和post方式发送,最终都会创建一个Message消息,将消息的target对象设置为当前handler,并将消息添加到Looper对象的MessageQueue中。在loop循环取消息处理时,调用msg.target.dispatchMessage处理消息,target保证了每个handler发送的消息只能自己处理,多个handler不会受到影响。dispatchMessage处理sendMessage消息会调handleMessage方法,处理post消息是会调用post的run方法处理在同一线程中创建的多个handler共享同一个Looper对象,同一MessageQueue对象,处理消息时通过target区分不同的handler

handler内存泄露处理

原因

在Java中,非静态(匿名)内部类会默认隐性引用外部类对象。而静态内部类不会引用外部类对象。在Activity中创建匿名Handler对象,Activity finish后,延迟Message会存在主线程的MessageQueue中,而该消息的target引用的是handler对象,handler对象又引用了activity,这些引用对象会一直保持到消息处理完,就会导致activity对象无法被回收,从而引起activity的泄露

处理

把Handler生声明为静态的,就不会只有activity对象,但是这样做handler对象还是会在MessageQueue中,正常activity finish后没必要对消息处理了,可以使用第二种方式解决在activity finish时移除handler中所有的message和callback:removeCallbacksAndMessages(Object token)

主线程Looper

Android应用程序进程在启动的时候,会在进程中加载ActivityThread类,并且执行这个类的main函数,应用程序的消息循环过程就是在这个main函数里面实现的,我们来看看这个函数的实现

public final class ActivityThread { ...... public static final void main(String[] args) { ...... Looper.prepareMainLooper(); ...... ActivityThread thread = new ActivityThread(); thread.attach(false); ...... Looper.loop(); ...... thread.detach(); ...... }

}

所以主线程不需要我们手动调用Looper.prepare和loop方法

转载请注明原文地址: https://www.6miu.com/read-77630.html

最新回复(0)