Handler 引起内存泄露
1.因为new Handler() 重写handleMessage ,handleMessage属于匿名内部类,匿名内部类是持有外部引用,就是说Handler是持有Activity引用的,然后Handler在发送消息时候又将Handler赋值给Message(msg.target = this;),所以就是Message 也持有了Activity,我们知道loop创建是在ActvityThread,而且loop是在循环,当我们发现MessageQueue有消息会取出发送到handleMessage,如果没消息属于阻塞状态,当我们已经执行stop状态,Handler有消息没有执行完情况下,因为Message持有Actvity引用,Actvity就无法进行回收,导致内存泄露
public static void main(String[] args) {
CloseGuard.setEnabled(false);
Looper.prepareMainLooper();
//创建Looper,MessageQueue,将looper存放ThreadTLocal并与主线程绑定
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
执行loop方法
throw new RuntimeException("Main thread loop unexpectedly exited");
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
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;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
msg.recycleUnchecked();
}
}
Handler主要做什么?机制是怎样的?有其他相同或者类似的机制吗?
我们知道,Binder主要用于跨进程通信,Handler呢?主要用于进程内部进行通信,或者说进程内部不同线程之间进行通信,即是不同线程之间互相发送消息
额外知识:
libcore/libart/src/main/java/java/lang/Thread.java
libcore/luni/src/main/java/java/lang/ThreadLocal.java
Thread类的内容有一个成员专门用于存储线程的ThreadLocal的数据。
ThreadLocal是做啥的呢? 啥都不说,直接上例子:
final ThreadLocal[] mBooleanThreadLocal = {new ThreadLocal()};
mBooleanThreadLocal[0].set(true);
Log.d("TAG", "[Thread#main]mBooleanThreadLocal="+ mBooleanThreadLocal[0].get());
new Thread("Thread#1") {
@Override
public void run() {
mBooleanThreadLocal[0].set(false);
Log.d("TAG","[Thread#1]mBooleanThreadLocal[0] = "+ mBooleanThreadLocal[0].get());
};
}.start();
new Thread("Thread#2") {
@Override
public void run() {
Log.d("TAG","[Thread#2]mBooleanThreadLocal[0] =" + mBooleanThreadLocal[0].get());
};
}.start();
}
输出结果:
2021-02-19 14:57:44.589 19352-19352/com.cnr.myapplication D/TAG: [Thread#main]mBooleanThreadLocal=true
2021-02-19 14:57:44.589 19352-19518/com.cnr.myapplication D/TAG: [Thread#1]mBooleanThreadLocal[0] = false
2021-02-19 14:57:44.590 19352-19519/com.cnr.myapplication D/TAG: [Thread#2]mBooleanThreadLocal[0] =null
很神奇吧,可以这样理解, ThreadLocal虽然作为主线程的全部变量,但是在不同的字线程中都有各自一个独立的副本,彼此之间互不干扰。也就是说ThreadLocal是基于线程的。
主要包括三个类:handler, Looper, MessageQueue,还有Message
消息的表示:Message
消息队列:MessageQueue
消息循环,用于循环取出消息进行处理:Looper
消息处理,消息循环从消息队列中取出消息后要对消息进行处理:Handler
一个进程仅有一个looper,looper中包含一个消息链表(按照时间排序).
同一个handler可以对应(处理)不同的消息(Message)
在apk启动时,会创建一个主线程,在ActivityThread的main函数中
【frameworks/base/core/java/android/app/ActivityThread.java】
public static void main(String[] args) {
Looper.prepareMainLooper();
.....
Looper.loop();
.....
}
@Deprecated
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 构造当前线程的Looper并且保存在sThreadLocal中
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
MessageQueue(boolean quitAllowed) {
// True if the message queue can be quit.
mQuitAllowed = quitAllowed;
mPtr = nativeInit();// 该函数是一个C/C++层函数
}
可以看看mQuitAllowed的定义:
该变量为false时表示不可以退出,只有主线程在调用prepare函数时传入false。也就是说仅有主线程不允许退出。
获取当前线程的Looper
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
获取消息队列
public static @NonNull MessageQueue myQueue() {
return myLooper().mQueue;
}
ThreadLocal是基于线程的,在apk启动时,会创建一个主线程,在ActivityThread的main函数中,主线程默认创建一个Looper并将其存放在ThreadLocal中,创建Looper也会创建一个MessageQueue,所以证实一个线程只能关联一个Looper和一个MessageQueue
Handler
public Handler(@Nullable Callback callback, boolean async) {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
由Handler的构造函数以及上面分析可知,1个线程仅有1个Looper对象,并且一个Looper仅有一个消息队列 MessageQueue。
消息队列MessageQueue
重要变量和函数:
89. Message mMessages; // 该变量表示第一个消息,根据next可以遍历所有消息,也可以说它是一条消息链表
90. Runnable callback;
91. private native static long nativeInit();
92. private native static void nativeDestroy(longptr);
93. private native void nativePollOnce(long ptr,int timeoutMillis);
94. private native static void nativeWake(longptr);
所谓的消息入队列就是将新建的Message插入mMessages中,在mMessages找到合适的位置。那么mMessages中消息排序的依据是什么呢? 延迟时间
从使用方法上可以看到,延迟运行线程多了一个步骤: postDelayed
95. public final boolean postDelayed(Runnable r,long delayMillis)
96. {
97. returnsendMessageDelayed(getPostMessage(r), delayMillis);
98. }
99. private static MessagegetPostMessage(Runnable r) {
100. Message m = Message.obtain();
101. m.callback = r;
102. return m;
103. }
主要是为callback 变量赋值,当发送普通消息时, callback=null,仅运行线程时才有值,这在消息的处理时有一点差别。
104.public final booleansendMessageDelayed(Message msg, long delayMillis)
105. {
106. if (delayMillis < 0) {
107. delayMillis = 0;
108. }
109. return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
110. }
关键是这句 SystemClock.uptimeMillis() + delayMillis以系统的绝对时间为基准,对消息进行插入操作。
111.boolean enqueueMessage(Message msg, longwhen) {
112. synchronized (this) {
113. if (mQuitting) {
114. msg.recycle();
115. return false;
116. }
117.
118. msg.markInUse();
119. msg.when = when;
120. Message p = mMessages;
121. boolean needWake;
122. if (p == null || when == 0 || when < p.when) {
123. // New head, wake up the eventqueue if blocked.
124. msg.next = p; // 还未有消息,作为链表的表头
125. mMessages = msg;
126. needWake = mBlocked;
127. } else {
128. // Inserted within the middleof the queue. Usually we don't have towake
129. // up the event queue unlessthere is a barrier at the head of the queue
130. // and the message is theearliest asynchronous message in the queue.
131. needWake = mBlocked &&p.target == null && msg.isAsynchronous();
132. Message prev;
133. for (;;) {
134. prev = p;
135. p = p.next; // 找到合适的位置并插入链表
136. if (p == null || when <p.when) {
137. break;
138. }
139. if (needWake &&p.isAsynchronous()) {
140. needWake = false;
141. }
142. }
143. msg.next = p; // invariant:p == prev.next
144. prev.next = msg;
145. }
146.
147. // We can assume mPtr != 0 because mQuitting is false.
148. if (needWake) {
149. nativeWake(mPtr);
150. }
151. }
152. return true;
153. }
消息循环
Looper只是个简单的类而已,它虽然提供了循环处理方面的成员函数loop(),却不能自己凭空地运行起来,而只能寄身于某个真实的线程。
在线程中,调用loop方法,不断从MessageQueue中去取消息,交给消息的target属性的dispatchMessage去处理。
154.public static void loop() {
155. final Looper me = myLooper(); // 获取当前线程的Looper
156. final MessageQueue queue = me.mQueue; // 获取消息链表
157.
158. // Make sure the identity of this thread is that of the local process,
159. // and keep track of what that identity token actually is.
160. for (;;) {
161. Message msg = queue.next(); // 直接获取下一条消息
162. if (msg == null) {
163. // No message indicates thatthe message queue is quitting.
164. return; 停止不会继续执行,等待最新消息
165. }
166. // This must be in a local variable, in case a UI event sets the logger
167.
168. msg.target.dispatchMessage(msg);// 分发处理消息
169. msg.recycleUnchecked(); // 消息本身的处理,进行回收
170. }
171. }
loop方法主要做3件事情:
1,从消息链表中取出下一条消息。
2,将消息分发给Handler去处理(msg.target的值就是handler)
3,处理完消息之后,消息本身也需要做后续处理。
首先说说消息的分发,然后解析如何从消息队列中取出消息。
分发消息
分发消息比较简单,上面流程图中的步骤22到25.
172.public void dispatchMessage(Message msg) {
173. if (msg.callback != null) {
174. handleCallback(msg); // 运行线程时调用
175. } else {
176. if (mCallback != null) {
177. if(mCallback.handleMessage(msg)) {
178. return;
179. }
180. }
181. handleMessage(msg);// 发送消息时调用
182. }
183. }
184.private static void handleCallback(Messagemessage) {
185. message.callback.run();
186. }
187.
分别调用自写的handleMessage 方法和Runnable 的run方法
188.private class H extends Handler {····}
Handler 基本使用
public class DynamicSurfacesActivity extends AppCompatActivity {
private static final Handler handler = new Handler()
{
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 1){
Log.d("TAG", "handleMessage: "+msg.obj.toString());
}
}
};
double d = 5.3E12;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dynamic_surfaces);
// ((CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar_layout)).setTitle(getString(R.string.dynamic_surfaces_title));
new Thread(new mRunnable()).start();
}
static class mRunnable implements Runnable{
@Override
public void run() {
Message message = Message.obtain();
message.what = 1;
message.obj = "text";
handler.sendMessage(message);
}
}
}
在工作线程往UI线程发消息 ,在此之前我们需要先把发送的消息封装到Message中 ,然后通过handler.sendMessage(message) 发送一个message。
在new handler 重写 handleMessage 方法 ,处理msg
我们来看一下 handler.sendMessage 实现源码
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); }
SystemClock.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);
}
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
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; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
when 是 SystemClock.uptimeMillis() 系统时间 如果
when < p.when 这是插入合适位置
if (needWake) {
nativeWake(mPtr);
}
如果需要唤醒 则唤醒线程
这里面有mQueue mQueue消息队列是同过New Handler 时候 通过调用Looper传过来的
在Handler 中通过Looper.myLooper(); 获取一个Looper对象 在通过mLooper.mQueue; 获取mQueue消息队列。
message.target = this this是当前handler对象。将msg.target 赋值成 Handler
queue.enqueueMessage(msg,uptimeMillis); 将message存储到消息队列中
看一下new Handler 都做了什么
public Handler() {
this(null, false);
}
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;
}
在Handler 中通过Looper.myLooper(); 获取一个Looper对象 在通过mLooper.mQueue; 获取mQueue消息队列。
在看一下Looper 代码的实现
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
这一个主线程的looper对象,它位于应用程序的主线程中
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
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));
}
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
将当前Looper存储在 ThreadLocal中 确保唯一
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
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.isTagEnabled(traceTag)) {
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();
}
}
通过loop方法遍历消息队列 mLooper获取Looper 然后 Looper.mQueue 获取消息队列
我们在 handler 中 queue.enqueueMessage(msg,uptimeMillis); 将message存储到消息队列中
开始死循环 for(;;){
然后 Queue.next 获取一条消息(msg)
msg 等于null 就 return;
在通过msg.target.dispatchMessage(msg)
}
上面说道 msg.target 是一个handler对象 调用Hanlder 的 dispatchMessage(msg)发送和处理消息
看下 dispatchMessage方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
public void handleMessage(Message msg) {
}
这个方法相信大家都比较熟悉了吧。