所以为了解开UI Thread为何不需要创建Looper对象的原因,我们看下ActivityThread的main方法,如下:
public static void main(String[] args) {
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
Security.addProvider(new AndroidKeyStoreProvider());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0(“”);
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, “ActivityThread”));
}
Looper.loop();
throw new RuntimeException(“Main thread loop unexpectedly exited”);
}
看见22行没?没说错吧?Looper.prepareMainLooper();
,我们跳到Looper看下prepareMainLooper方法,如下:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException(“The main Looper has already been prepared.”);
}
sMainLooper = myLooper();
}
}
可以看到,UI线程中会始终存在一个Looper对象(sMainLooper 保存在Looper类中,UI线程通过getMainLooper方法获取UI线程的Looper对象),从而不需要再手动去调用Looper.prepare()方法了。如下Looper类提供的get方法:
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
看见没有,到这里整个Handler实例化与为何子线程在实例化Handler之前需要先调运Looper.prepare();语句的原理分析完毕。
3-1-3 Handler与Looper实例化总结
到此先初步总结下上面关于Handler实例化的一些关键信息,具体如下:
-
在主线程中可以直接创建Handler对象,而在子线程中需要先调用Looper.prepare()才能创建Handler对象,否则运行抛出”Can’t create handler inside thread that has not called Looper.prepare()”异常信息。
-
每个线程中最多只能有一个Looper对象,否则抛出异常。
-
可以通过Looper.myLooper()获取当前线程的Looper实例,通过Looper.getMainLooper()获取主(UI)线程的Looper实例。
-
一个Looper只能对应了一个MessageQueue。
-
一个线程中只有一个Looper实例,一个MessageQueue实例,可以有多个Handler实例。
Handler对象也创建好了,接下来就该用了吧,所以下面咱们从Handler的收发消息角度来分析分析源码。
3-2 继续看看Handler消息收发机制源码
3-2-1 通过Handler发消息到消息队列
还记得上面的例子吗?我们在Child Thread的最后通过主线程的Handler对象调运sendEmptyMessage方法发送出去了一条消息。
当然,其实Handler类提供了许发送消息的方法,我们这个例子只是用了最简单的发送一个empty消息而已,有时候我们会先定义一个Message,然后通过Handler提供的其他方法进行发送。通过分析Handler源码发现Handler中提供的很多个发送消息方法中除了sendMessageAtFrontOfQueue()方法之外,其它的发送消息方法最终都调用了sendMessageAtTime()方法。所以,咱们先来看下这个sendMessageAtTime方法,如下:
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);
}
再看下Handler中和其他发送方法不同的sendMessageAtFrontOfQueue方法,如下:
public final boolean sendMessageAtFrontOfQueue(Message msg) {
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, 0);
}
对比上面两个方法可以发现,表面上说Handler的sendMessageAtFrontOfQueue方法和其他发送方法不同,其实实质是相同的,仅仅是sendMessageAtFrontOfQueue方法是sendMessageAtTime方法的一个特例而已(sendMessageAtTime最后一个参数传递0就变为了sendMessageAtFrontOfQueue方法)。所以咱们现在继续分析sendMessageAtTime方法,如下分析:
sendMessageAtTime(Message msg, long uptimeMillis)方法有两个参数;msg是我们发送的Message对象,uptimeMillis表示发送消息的时间,uptimeMillis的值等于从系统开机到当前时间的毫秒数再加上延迟时间。
在该方法的第二行可以看到queue = mQueue,而mQueue是在Handler实例化时构造函数中实例化的。在Handler的构造函数中可以看见mQueue = mLooper.mQueue;,而Looper的mQueue对象上面分析过了,是在Looper的构造函数中创建的一个MessageQueue。
接着第9行可以看到,上面说的两个参数和刚刚得到的queue对象都传递到了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);
}
这个方法首先将我们要发送的消息Message的target属性设置为当前Handler对象(进行关联);接着将msg与uptimeMillis这两个参数都传递到MessageQueue(消息队列)的enqueueMessage()方法中,所以接下来我们就继续分析MessageQueue类的enqueueMessage方法,如下:
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(“MessageQueue”, 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;
}
通过这个方法名可以看出来通过Handler发送消息实质就是把消息Message添加到MessageQueue消息队列中的过程而已。
通过上面遍历等next操作可以看出来,MessageQueue消息队列对于消息排队是通过类似c语言的链表来存储这些有序的消息的。其中的mMessages对象表示当前待处理的消息;然后18到49行可以看出,消息插入队列的实质就是将所有的消息按时间(uptimeMillis参数,上面有介绍)进行排序。所以还记得上面sendMessageAtFrontOfQueue方法吗?它的实质就是把消息添加到MessageQueue消息队列的头部(uptimeMillis为0,上面有分析)。
到此Handler的发送消息及发送的消息如何存入到MessageQueue消息队列的逻辑分析完成。
那么问题来了!既然消息都存入到了MessageQueue消息队列,当然要取出来消息吧,不然存半天有啥意义呢?我们知道MessageQueue的对象在Looper构造函数中实例化的;一个Looper对应一个MessageQueue,所以说Handler发送消息是通过Handler构造函数里拿到的Looper对象的成员MessageQueue的enqueueMessage方法将消息插入队列,也就是说出队列一定也与Handler和Looper和MessageQueue有关系。
还记不记得上面实例部分中Child Thread最后调运的Looper.loop();方法呢?这个方法其实就是取出MessageQueue消息队列里的消息方法。具体在下面分析。
3-2-2 通过Handler接收发送的消息
先来看下上面例子中Looper.loop();这行代码调运的方法,如下:
/**
-
Run the message queue in this thread. Be sure to call
-
{@link #quit()} to end the 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();
}
}
可以看到,第6行首先得到了当前线程的Looper对象me,接着第10行通过当前Looper对象得到与Looper对象一一对应的MessageQueue消息队列(也就类似上面发送消息部分,Handler通过myLoop方法得到Looper对象,然后获取Looper的MessageQueue消息队列对象)。17行进入一个死循环,18行不断地调用MessageQueue的next()方法,进入MessageQueue这个类查看next方法,如下:
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;😉 {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (false) Log.v(“MessageQueue”, "Returning message: " + msg);
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(“MessageQueue”, “IdleHandler threw exception”, t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
可以看出来,这个next方法就是消息队列的出队方法(与上面分析的MessageQueue消息队列的enqueueMessage方法对比)。可以看见上面代码就是如果当前MessageQueue中存在待处理的消息mMessages就将这个消息出队,然后让下一条消息成为mMessages,否则就进入一个阻塞状态(在上面Looper类的loop方法上面也有英文注释,明确说到了阻塞特性),一直等到有新的消息入队。
继续看loop()方法的第30行(msg.target.dispatchMessage(msg);),每当有一个消息出队就将它传递到msg.target的dispatchMessage()方法中。其中这个msg.target其实就是上面分析Handler发送消息代码部分Handler的enqueueMessage方法中的msg.target = this;语句,也就是当前Handler对象。所以接下来的重点自然就是回到Handler类看看我们熟悉的dispatchMessage()方法,如下:
/**
- Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
可以看见dispatchMessage方法中的逻辑比较简单,具体就是如果mCallback不为空,则调用mCallback的handleMessage()方法,否则直接调用Handler的handleMessage()方法,并将消息对象作为参数传递过去。
这样我相信大家就都明白了为什么handleMessage()方法中可以获取到之前发送的消息了吧!
对了,既然上面说了获取消息在MessageQueue消息队列中是一个死循环的阻塞等待,所以Looper的quit方法也很重要,这样在不需要时可以退出这个死循环,如上面实例部分使用所示。
3-2-3 结束MessageQueue消息队列阻塞死循环源码分析
如下展示了Looper类的quit方法源码:
public void quit() {
mQueue.quit(false);
}
看见没有,quit方法实质就是调运了MessageQueue消息队列的quit,如下:
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException(“Main thread not allowed to quit.”);
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
看见上面2到4行代码没有,通过判断标记mQuitAllowed来决定该消息队列是否可以退出,然而当mQuitAllowed为fasle时抛出的异常竟然是”Main thread not allowed to quit.”,Main Thread,所以可以说明Main Thread关联的Looper一一对应的MessageQueue消息队列是不能通过该方法退出的。
你可能会疑惑这个mQuitAllowed在哪设置的?
其实他是MessageQueue构造函数传递参数传入的,而MessageQueue对象的实例化是在Looper的构造函数实现的,所以不难发现mQuitAllowed参数实质是从Looper的构函数传入的。上面实例化Handler模块源码分析时说过,Looper实例化是在Looper的静态方法prepare(boolean quitAllowed)中处理的,也就是说mQuitAllowed是由Looper.prpeare(boolean quitAllowed)参数传入的。追根到底说明mQuitAllowed就是Looper.prpeare的参数,我们默认调运的Looper.prpeare();其中对mQuitAllowed设置为了true,所以可以通过quit方法退出,而主线程ActivityThread的main中使用的是Looper.prepareMainLooper();,这个方法里对mQuitAllowed设置为false,所以才会有上面说的”Main thread not allowed to quit.”。
回到quit方法继续看,可以发现实质就是对mQuitting标记置位,这个mQuitting标记在MessageQueue的阻塞等待next方法中用做了判断条件,所以可以通过quit方法退出整个当前线程的loop循环。
到此整个Android的一次完整异步消息机制分析使用流程结束。接下来进行一些总结提升与拓展。
3-3 简单小结下Handler整个使用过程与原理
通过上面的源码分析原理我们可以总结出整个异步消息处理流程的关系图如下:
这幅图很明显的表达出了Handler异步机制的来龙去脉,不做过多解释。
上面实例部分我们只是演示了Handler的局部方法,具体Handler还有很多方法,下面详细介绍。
3-4 再来看看Handler源码的其他常用方法
在上面例子中我们只是演示了发送消息的sendEmptyMessage(int what)方法,其实Handler有如下一些发送方式:
sendMessage(Message msg);
sendEmptyMessage(int what);
sendEmptyMessageDelayed(int what, long delayMillis);
sendEmptyMessageAtTime(int what, long uptimeMillis);
sendMessageDelayed(Message msg, long delayMillis);
sendMessageAtTime(Message msg, long uptimeMillis);
sendMessageAtFrontOfQueue(Message msg);
方法。
这些方法不再做过多解释,用法雷同,顶一个Message决定啥时发送到target去。
post(Runnable r);
postDelayed(Runnable r, long delayMillis);
等post系列方法。
该方法实质源码其实就是如下:
public final boolean postDelayed(Runnable r, long delayMillis)
{
return sendMessageDelayed(getPostMessage®, delayMillis);
}
额,原来post方法的实质也是调运sendMessageDelayed()方法去处理的额,看见getPostMessage®方法没?如下源码:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
如上方法仅仅是将消息的callback字段指定为传入的Runnable对象r。其实这个Message对象的m.callback就是上面分析Handler接收消息回调处理dispatchMessage()方法中调运的。在Handler的dispatchMessage方法中首先判断如果Message的callback等于null才会去调用handleMessage()方法,否则就调用handleCallback()方法。那就再看下Handler的handleCallback()方法源码,如下:
private static void handleCallback(Message message) {
message.callback.run();
}
额,这里竟然直接执行了Runnable对象的run()方法。所以说我们在Runnable对象的run()方法里更新UI的效果完全和在handleMessage()方法中更新UI相同,特别强调这个Runnable的run方法还在当前线程中阻塞执行,没有创建新的线程(很多人以为是Runnable就创建了新线程)。
Activity.runOnUiThread(Runnable);
方法。
首先看下Activity的runOnUiThread方法源码:
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
看见没有,实质还是在UI线程中执行了Runnable的run方法。不做过多解释。
View.post(Runnable);和View.postDelayed(Runnable action, long delayMillis);
方法。
首先看下View的postDelayed方法源码:
public boolean postDelayed(Runnable action, long delayMillis) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.postDelayed(action, delayMillis);
}
// Assume that post will succeed later
ViewRootImpl.getRunQueue().postDelayed(action, delayMillis);
return true;
}
看见没有,实质还是在UI线程中执行了Runnable的run方法。不做过多解释。
到此基本上关于Handler的所有发送消息方式都被解释明白了。既然会用了基本的那就得提高下,接下来看看关于Message的一点优化技巧。
3-5 关于Handler发送消息的一点优化分析
还记得我们说过,当发送一个消息时我们首先会new一个Message对象,然后再发送吗?你是不是觉得每次new Message很浪费呢?那么我们就来分析一下这个问题。
如下是我们正常发送消息的代码局部片段:
Message message = new Message();
message.arg1 = 110;
message.arg2 = 119;
message.what = 0x120;
message.obj = “Test Message Content!”;
mHandler.sendMessage(message);
相信很多初学者都是这么发送消息的吧?当有大量多次发送时如上写法会不太高效。不卖关子,先直接看达到同样效果的优化写法,如下:
mHandler.sendMessage(mHandler.obtainMessage(0x120, 110, 119, ““Test Message Content!””));
咦?怎么send时没实例化Message?这是咋回事?我们看下mHandler.obtainMessage(0x120, 110, 119, "\"Test Message Content!\"")
这一段代码,obtainMessage是Handler提供的一个方法,看下源码:
public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
{
return Message.obtain(this, what, arg1, arg2, obj);
}
这方法竟然直接调运了Message类的静态方法obtain,我们再去看看obtain的源码,如下:
public static Message obtain(Handler h, int what,
int arg1, int arg2, Object obj) {
Message m = obtain();
m.target = h;
m.what = what;
m.arg1 = arg1;
m.arg2 = arg2;
m.obj = obj;
return m;
}
看见没有?首先又调运一个无参的obtain方法,然后设置Message各种参数后返回。我们继续看下这个无参方法,如下:
/**
-
Return a new Message instance from the global pool. Allows us to
-
avoid allocating new objects in many cases.
*/
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();
}
真相大白了!看见注释没有?从整个Messge池中返回一个新的Message实例,在许多情况下使用它,因为它能避免分配新的对象。
所以看见这两种获取Message写法的优缺点没有呢?明显可以看见通过调用Handler的obtainMessage方法获取Message对象就能避免创建对象,从而减少内存的开销了。所以推荐这种写法!!!
3-6 关于Handler导致内存泄露的分析与解决方法
正如上面我们实例部分的代码,使用Android Lint会提示我们这样一个Warning,如下:
In Android, Handler classes should be static or leaks might occur.
意思是说在Android中Handler类应该是静态的否则可能发生泄漏。
啥是内存泄漏呢?
Java通过GC自动检查内存中的对象,如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收。也就是说,一个对象不被任何引用所指向,则该对象会在被GC发现的时候被回收;另外,如果一组对象中只包含互相的引用,而没有来自它们外部的引用(例如有两个对象A和B互相持有引用,但没有任何外部对象持有指向A或B的引用),这仍然属于不可到达,同样会被GC回收。本该被回收的对象没被回收就是内存泄漏。
Handler中怎么泄漏的呢?
当使用内部类(包括匿名类)来创建Handler的时候,Handler对象会隐式地持有一个外部类对象(通常是一个Activity)的引用。而Handler通常会伴随着一个耗时的后台线程一起出现,这个后台线程在任务执行完毕之后,通过消息机制通知Handler,然后Handler把消息发送到UI线程。然而,如果用户在耗时线程执行过程中关闭了Activity(正常情况下Activity不再被使用,它就有可能在GC检查时被回收掉),由于这时线程尚未执行完,而该线程持有Handler的引用,这个Handler又持有Activity的引用,就导致该Activity暂时无法被回收(即内存泄露)。
Handler内存泄漏解决方案呢?
方案一:通过程序逻辑来进行保护
-
在关闭Activity的时候停掉你的后台线程。线程停掉了,就相当于切断了Handler和外部连接的线,Activity自然会在合适的时候被回收。
-
如果你的Handler是被delay的Message持有了引用,那么使用相应的Handler的removeCallbacks()方法,把消息对象从消息队列移除就行了(如上面的例子部分的onStop中代码)。
方案二:将Handler声明为静态类
静态类不持有外部类的对象,所以你的Activity可以随意被回收。代码如下:
static class TestHandler extends Handler {
@Override
public void handleMessage(Message msg) {
mImageView.setImageBitmap(mBitmap);
}
}
这时你会发现,由于Handler不再持有外部类对象的引用,导致程序不允许你在Handler中操作Activity中的对象了。所以你需要在Handler中增加一个对Activity的弱引用(WeakReference),如下:
static class TestHandler extends Handler {
WeakReference mActivityReference;
TestHandler(Activity activity) {
mActivityReference= new WeakReference(activity);
}
@Override
public void handleMessage(Message msg) {
final Activity activity = mActivityReference.get();
if (activity != null) {
mImageView.setImageBitmap(mBitmap);
}
}
}
如上就是关于Handler内存泄漏的分析及解决方案。
可能在你会用了Handler之后见过HandlerThread这个关键字,那我们接下来就看看HandlerThread吧。
4 关于Android异步消息处理机制进阶的HandlerThread源码分析
4-1 Android 5.1.1(API 22) HandlerThread源码
很多人在会使用Handler以后会发现有些代码里出现了HandlerThread,然后就分不清HandlerThread与Handler啥关系,咋回事之类的。这里就来分析分析HandlerThread的源码。如下:
public class HandlerThread extends Thread {
//线程的优先级
int mPriority;
//线程的id
int mTid = -1;
//一个与Handler关联的Looper对象
Looper mLooper;
public HandlerThread(String name) {
super(name);
//设置优先级为默认线程
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
总结
本文讲解了我对Android开发现状的一些看法,也许有些人会觉得我的观点不对,但我认为没有绝对的对与错,一切交给时间去证明吧!愿与各位坚守的同胞们互相学习,共同进步!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
进阶的HandlerThread源码分析**
4-1 Android 5.1.1(API 22) HandlerThread源码
很多人在会使用Handler以后会发现有些代码里出现了HandlerThread,然后就分不清HandlerThread与Handler啥关系,咋回事之类的。这里就来分析分析HandlerThread的源码。如下:
public class HandlerThread extends Thread {
//线程的优先级
int mPriority;
//线程的id
int mTid = -1;
//一个与Handler关联的Looper对象
Looper mLooper;
public HandlerThread(String name) {
super(name);
//设置优先级为默认线程
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-VHfanCbW-1712228647815)]
[外链图片转存中…(img-qwiqcjUm-1712228647816)]
[外链图片转存中…(img-8e2KTwz9-1712228647816)]
[外链图片转存中…(img-OvE5GFm3-1712228647816)]
[外链图片转存中…(img-XBBi0UVA-1712228647816)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
总结
本文讲解了我对Android开发现状的一些看法,也许有些人会觉得我的观点不对,但我认为没有绝对的对与错,一切交给时间去证明吧!愿与各位坚守的同胞们互相学习,共同进步!