Android 的消息机制(读书笔记)

1. Android 消息机制的概述

Android 的消息机制主要就是指的Handler 的运行机制及Handler 附带的Looper和MessageQueue的工作过程. Handler 的主要作用就是将一个操作切换到Handler对应的Looper所在进程来执行.
由于Android 要求对UI的访问要在main进程(UI进程)来做.实际中Handler的作用:就是主要用来在子进程中做完耗时的工作之后,再需要更新UI的时候可以把更新UI的操作切换main进程(UI进程)来执行.
如果直接在子进程访问UI会报异常.ViewRootImpl 的checkThread()是用来做这个验证的.
   
   
  1. void checkThread() {
  2. if (mThread != Thread.currentThread()) {
  3. String errorMessage = "";
  4. if (IS_APR_BUILD) {
  5. int wrongTid = Process.myTid();
  6. String currentStack = Debug.getCallers(10, "... ") + " \n";
  7. String currentThreadName = Thread.currentThread().getName();
  8. errorMessage = "\n ctid= " + mCreationTid
  9. + "\n cname = " + mCreationThreadName
  10. + "\n wtid= " + wrongTid
  11. + "\n wname = " + currentThreadName
  12. + "\n wstack = \n" + currentStack
  13. + "\n cstack = \n" + mCreationStack;
  14. }
  15. throw new CalledFromWrongThreadException(
  16. "Only the original thread that created a view hierarchy can touch its views.\n" + errorMessage);
  17. }
  18. }
Handler 创建时需要用到当前线程的Looper 对象来创建内部的消息循环系统.如果当前线程没有Looper对象,就会报错.
   
   
  1. public Handler(Callback callback, boolean async) {
  2. mLooper = Looper.myLooper();
  3. if (mLooper == null) {
  4. throw new RuntimeException(
  5. "Can't create handler inside thread that has not called Looper.prepare()");
  6. }
  7. mQueue = mLooper.mQueue;
  8. mCallback = callback;
  9. mAsynchronous = async;
  10. }

2. Android消息机制的分析

前面已经对Android 的消息机制组了一个简单的概述,下面会对相关的类做具体的分析.
关于Android 的消息机制主要涉及的类有:
Handler
Looper 
MessageQueue 
ThreadLocal
下面是在网上找的一个类图.
 

2.1 ThreadLocal 的工作原理

ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,同时也只能在指定的那个线程中获取之前存储的数据,其他线程是不能获取到这个数据的.
前面已经提到创建Handler 是需要获取当前线程的Looper对象,而Looper的作用域就是当前线程,不同的线程有不同的Looper.这种情况下使用ThreadLocal就可以实现Looper在线程中的存取.

为先具体理解一下上面的意思,看下面的demo和运行结果:
   
   
  1. private static ThreadLocal<Boolean> mtThreadLocal = new ThreadLocal<Boolean>();
  2. mtThreadLocal.set(true);
  3. System.out.println("Thread#main mtThreadLocal=" +mtThreadLocal.get());
  4. new Thread("Thread#1"){
  5. public void run() {
  6. mtThreadLocal.set(false);
  7. System.out.println("Thread#1 mtThreadLocal=" +mtThreadLocal.get());
  8. };
  9. }.start();
  10. new Thread("Thread#2"){
  11. public void run() {
  12. System.out.println("Thread#2 mtThreadLocal=" +mtThreadLocal.get());
  13. };
  14. }.start();
运行结果:
Thread#main mtThreadLocal=true  :这个很好理解,设置了为true,获取的结果也是如此.
Thread#1 mtThreadLocal=false       :在Thread#1 中设置为false
Thread#2 mtThreadLocal=null         :在Thread#2中没有设置值,所以获取的就是null.
上面的demo 就很好的解释了ThreadLocal可以在不同的线程中维护一套数据的不同的副本,并且彼此不干扰.

下面接着分析ThreadLocal的具体源码实现.ThreadLocal是一个泛型类,是属于java原生的类,只是在Android中有了一定的改动.
其中public的方法主要也就是是set() ,get(),remove().
先看set方法:
   
   
  1. public void set(T value) {
  2. Thread currentThread = Thread.currentThread();
  3. Values values = values(currentThread);
  4. if (values == null) {
  5. values = initializeValues(currentThread);
  6. }
  7. values.put(this, value);
  8. }
  9. Values values(Thread current) {
  10. return current.localValues;
  11. }
  12. Values initializeValues(Thread current) {
  13. return current.localValues = new Values();
  14. }

    
    
  1. private final Reference<ThreadLocal<T>> reference = new WeakReference<ThreadLocal<T>>(this);
上面的set()方法中,先是使用values()方法获取当前线程的ThreadLocal.Values localValues 对象.如果不存在就调用initializeValues()来初始化,并将 ThreadLocal的值存储到之前 初始化 ThreadLocal.Values中去.
具体是怎么保存 ThreadLocal 的值需要进一步看 ThreadLocal.Values 的put()方法.
   
   
  1. void put(ThreadLocal<?> key, Object value) {
  2. cleanUp();
  3. // Keep track of first tombstone. That's where we want to go back
  4. // and add an entry if necessary.
  5. int firstTombstone = -1;
  6. for (int index = key.hash & mask;; index = next(index)) {
  7. Object k = table[index];
  8. if (k == key.reference) {
  9. // Replace existing entry.
  10. table[index + 1] = value;
  11. return;
  12. }
  13. if (k == null) {
  14. if (firstTombstone == -1) {
  15. // Fill in null slot.
  16. table[index] = key.reference;
  17. table[index + 1] = value;
  18. size++;
  19. return;
  20. }
  21. // Go back and replace first tombstone.
  22. table[firstTombstone] = key.reference;
  23. table[firstTombstone + 1] = value;
  24. tombstones--;
  25. size++;
  26. return;
  27. }
  28. // Remember first tombstone.
  29. if (firstTombstone == -1 && k == TOMBSTONE) {
  30. firstTombstone = index;
  31. }
  32. }
  33. }
上面的put()代码可以出,主要是将数据保存到 ThreadLocal.Values这个内部类的一个数组table[ ]  中的.其中具体的逻辑暂时不关注,主要先明白ThreadLocal 的值在table数组中的位置总是ThreadLocal的reference字段所标识的对应的位置的下一个位置.

下面继续看get方法的源码
   
   
  1. public T get() {
  2. // Optimized for the fast path.
  3. Thread currentThread = Thread.currentThread();
  4. Values values = values(currentThread);
  5. if (values != null) {
  6. Object[] table = values.table;
  7. int index = hash & values.mask;
  8. if (this.reference == table[index]) {
  9. return (T) table[index + 1];
  10. }
  11. } else {
  12. values = initializeValues(currentThread);
  13. }
  14. return (T) values.getAfterMiss(this);
  15. }
上面的get()流程比较简单,如果之前有保存 ThreadLocal的值就从当前线程的Values对象的table数组中获取就可以了,如果没有保存就放回初始值,初始值就是用ThreadLocal的initialValue来获取的.默认是为null,我们自己也可以重写这个方法.

从上面的get 和set代码可以看出,在多个线程中访问ThreadLocal的get和set,其实它们所操作的对象就是当前线程的一个对象 localValues的里面的table数组.  也就是说对ThreadLocal的get和set操作仅局限于各个线程内部.这也就是为什么在多个线程中同一个ThreadLocal可以互不干扰的进行get和set操作.

2.2 MessageQueue 的工作原理

MessageQueue叫着消息队列,但是它本身的内部实现并不是用的队列的形式,而是一个单链表的形式.MessageQueue主要有2个操作:插入和读取.读取操作本身会伴随着删除操作.
插入和读取分别对应着enqueueMessage 和next.
下面看一下相关的源码:
   
   
  1. boolean enqueueMessage(Message msg, long when) {
  2. if (msg.target == null) {
  3. throw new IllegalArgumentException("Message must have a target.");
  4. }
  5. if (msg.isInUse()) {
  6. throw new IllegalStateException(msg + " This message is already in use.");
  7. }
  8. /// M: Add message protect mechanism @{
  9. if (msg.hasRecycle) {
  10. Log.wtf("MessageQueue", "Warning: message has been recycled. msg=" + msg);
  11. return false;
  12. }
  13. /// Add message protect mechanism @}
  14. synchronized (this) {
  15. if (mQuitting) {
  16. IllegalStateException e = new IllegalStateException(
  17. msg.target + " sending message to a Handler on a dead thread");
  18. Log.w("MessageQueue", e.getMessage(), e);
  19. msg.recycle();
  20. return false;
  21. }
  22. msg.markInUse();
  23. msg.when = when;
  24. Message p = mMessages;
  25. boolean needWake;
  26. if (p == null || when == 0 || when < p.when) {
  27. // New head, wake up the event queue if blocked.
  28. msg.next = p;
  29. mMessages = msg;
  30. needWake = mBlocked;
  31. } else {
  32. // Inserted within the middle of the queue. Usually we don't have to wake
  33. // up the event queue unless there is a barrier at the head of the queue
  34. // and the message is the earliest asynchronous message in the queue.
  35. needWake = mBlocked && p.target == null && msg.isAsynchronous();
  36. Message prev;
  37. for (;;) {
  38. prev = p;
  39. p = p.next;
  40. if (p == null || when < p.when) {
  41. break;
  42. }
  43. if (needWake && p.isAsynchronous()) {
  44. needWake = false;
  45. }
  46. }
  47. msg.next = p; // invariant: p == prev.next
  48. prev.next = msg;
  49. }
  50. // We can assume mPtr != 0 because mQuitting is false.
  51. if (needWake) {
  52. nativeWake(mPtr);
  53. }
  54. }
  55. return true;
  56. }
插入方法很简单就是一个单链表的操作操作.同时根据 needWake 来判断是否执行唤醒操作.
下面接着看读取方法next()
   
   
  1. Message next() {
  2. // Return here if the message loop has already quit and been disposed.
  3. // This can happen if the application tries to restart a looper after quit
  4. // which is not supported.
  5. final long ptr = mPtr;
  6. if (ptr == 0) {
  7. return null;
  8. }
  9. int pendingIdleHandlerCount = -1; // -1 only during first iteration
  10. int nextPollTimeoutMillis = 0;
  11. for (;;) {
  12. if (nextPollTimeoutMillis != 0) {
  13. Binder.flushPendingCommands();
  14. }
  15. nativePollOnce(ptr, nextPollTimeoutMillis);
  16. synchronized (this) {
  17. // Try to retrieve the next message. Return if found.
  18. final long now = SystemClock.uptimeMillis();
  19. Message prevMsg = null;
  20. Message msg = mMessages;
  21. if (msg != null && msg.target == null) {
  22. // Stalled by a barrier. Find the next asynchronous message in the queue.
  23. do {
  24. prevMsg = msg;
  25. msg = msg.next;
  26. } while (msg != null && !msg.isAsynchronous());
  27. }
  28. if (msg != null) {
  29. if (now < msg.when) {
  30. // Next message is not ready. Set a timeout to wake up when it is ready.
  31. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
  32. } else {
  33. // Got a message.
  34. mBlocked = false;
  35. if (prevMsg != null) {
  36. prevMsg.next = msg.next;
  37. } else {
  38. mMessages = msg.next;
  39. }
  40. msg.next = null;
  41. if (false) Log.v("MessageQueue", "Returning message: " + msg);
  42. return msg;
  43. }
  44. } else {
  45. // No more messages.
  46. nextPollTimeoutMillis = -1;
  47. }
  48. // Process the quit message now that all pending messages have been handled.
  49. if (mQuitting) {//MessageQueue退出了
  50. dispose();
  51. return null;
  52. }
  53. // If first time idle, then get the number of idlers to run.
  54. // Idle handles only run if the queue is empty or if the first message
  55. // in the queue (possibly a barrier) is due to be handled in the future.
  56. if (pendingIdleHandlerCount < 0
  57. && (mMessages == null || now < mMessages.when)) {
  58. pendingIdleHandlerCount = mIdleHandlers.size();
  59. }
  60. if (pendingIdleHandlerCount <= 0) {
  61. // No idle handlers to run. Loop and wait some more.
  62. mBlocked = true;
  63. continue;
  64. }
  65. if (mPendingIdleHandlers == null) {
  66. mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
  67. }
  68. mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
  69. }
  70. // Run the idle handlers.
  71. // We only ever reach this code block during the first iteration.
  72. for (int i = 0; i < pendingIdleHandlerCount; i++) {
  73. final IdleHandler idler = mPendingIdleHandlers[i];
  74. mPendingIdleHandlers[i] = null; // release the reference to the handler
  75. boolean keep = false;
  76. try {
  77. keep = idler.queueIdle();
  78. } catch (Throwable t) {
  79. Log.wtf("MessageQueue", "IdleHandler threw exception", t);
  80. }
  81. if (!keep) {
  82. synchronized (this) {
  83. mIdleHandlers.remove(idler);
  84. }
  85. }
  86. }
  87. // Reset the idle handler count to 0 so we do not run them again.
  88. pendingIdleHandlerCount = 0;
  89. // While calling an idle handler, a new message could have been delivered
  90. // so go back and look again for a pending message without waiting.
  91. nextPollTimeoutMillis = 0;
  92. }
  93. }
next方法就是一个for循环,如果消息队列中没有消息就一直堵塞在那里,如果队列中有消息就返回这个消息,并从队列中删除这个消息.上面的代码其实我们主要看中间那一部分就可以了.
其中如果MessageQueue被调用了quit()方法时会被标记为退出状态,这时next就是会返回null.其实MessageQueue的quit的调用,是因为Looper调用自己的quit()间接导致的.Looper会在后面分析.

2.3 Looper 的工作原理

Looper在Android消息机制中负责消息的循环处理.就是从MessageQueue这个消息队列中循环取出消息来处理.有新消息就处理,如果没有消息需要处理了就堵塞在那里.
下面从构造函数开始:
   
   
  1. private Looper(boolean quitAllowed) {
  2. mQueue = new MessageQueue(quitAllowed);
  3. mThread = Thread.currentThread();
  4. }
Looper创建时会创建一个MessageQueue消息队列.并获取当前线程并保存起来.
Handler创建的时候是需要有一个Looper 的,否则就会报错.
   
   
  1. new Thread(new Runnable() {
  2. @Override
  3. public void run() {
  4. //Looper.prepare();
  5. mHandler = new MyHandler(Looper.getMainLooper());
  6. mHandler.post(new Runnable() {
  7. @Override
  8. public void run() {
  9. Log.d(TAG,"Thread start mHandler post 2,thread:" + Thread.currentThread().getName());
  10. }
  11. });
  12. //Looper.loop();
  13. }
  14. }).start();
创建Handler 时可以使用带参数构造函数给Handler传递一个Looper对象.或者在创建Handler之前使用 Looper.prepare() 来创建,同时使用 Looper.loop() 启动消息循环. Looper 还有一个 prepareMainLooper()主要是用来给主线程ActivityThread 来创建Looper对象的.

如果在子线程中为Handler超级了Looper对象,在消息处理完成之后应该调用quit()来终止消息的循环.否则这个子线程一直处理等待状态.而Looper退出了,子线程就会立刻终止,因此在需要的时候请终止Looper.
Looper的最主要的一个方法就是loop(),具体代码如下,其中mtk 添加的部分关于log相关的代码已经删除了,避免代码块太长.
   
   
  1. /**
  2. * Run the message queue in this thread. Be sure to call
  3. * {@link #quit()} to end the loop.
  4. */
  5. public static void loop() {
  6. final Looper me = myLooper();
  7. if (me == null) {
  8. throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
  9. }
  10. final MessageQueue queue = me.mQueue;
  11. // Make sure the identity of this thread is that of the local process,
  12. // and keep track of what that identity token actually is.
  13. Binder.clearCallingIdentity();
  14. final long ident = Binder.clearCallingIdentity();
  15. for (;;) {
  16. Message msg = queue.next(); // might block
  17. if (msg == null) {
  18. // No message indicates that the message queue is quitting.
  19. return;
  20. }
  21. // This must be in a local variable, in case a UI event sets the logger
  22. Printer logging = me.mLogging;
  23. if (logging != null) {
  24. logging.println(">>>>> Dispatching to " + msg.target + " " +
  25. msg.callback + ": " + msg.what);
  26. }
  27. msg.target.dispatchMessage(msg);
  28. if (logging != null) {
  29. logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
  30. }
  31. // Make sure that during the course of dispatching the
  32. // identity of the thread wasn't corrupted.
  33. final long newIdent = Binder.clearCallingIdentity();
  34. if (ident != newIdent) {
  35. Log.wtf(TAG, "Thread identity changed from 0x"
  36. + Long.toHexString(ident) + " to 0x"
  37. + Long.toHexString(newIdent) + " while dispatching to "
  38. + msg.target.getClass().getName() + " "
  39. + msg.callback + " what=" + msg.what);
  40. }
  41. msg.recycleUnchecked();
  42. }
  43. }
loop方法就是一个死循环.唯一能退出这个死循环的条件就是MessageQueue的next()返回的是null.只有在Looper的quit 被调用回间接调用MessageQueue的quit()或quitSafley()时,才会将消息队列标记为退出状态,MessageQueue 的next才会返回null.
如果MessageQueue消息队列有新消息,就调用 MessageQueue的next()来获取新消息,并处理: msg . target . dispatchMessage ( msg ) , msg . target 其实就是Handler,也就是调用Handler的 dispatchMessage来处理.
如果一直有新消息就会这样一直循环处理.由于dispatchMessage()是在Looper中调用,所以 dispatchMessage就被切换到Looper所在的线程了.
如果 MessageQueue 没有新消息,Looper就堵塞在loop里面.

2.4 Handler的工作原理

Handler 在Android的消息机制中主要是用来发送消息和接受消息.
其中发送消息是用send和post 2个方法,但是post最终也是调用send.
   
   
  1. public final boolean sendMessage(Message msg){
  2. return sendMessageDelayed(msg, 0);
  3. }
  4. public final boolean sendMessageDelayed(Message msg, long delayMillis){
  5. if (delayMillis < 0) {
  6. delayMillis = 0;
  7. }
  8. return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
  9. }
  10. public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
  11. MessageQueue queue = mQueue;
  12. if (queue == null) {
  13. RuntimeException e = new RuntimeException(
  14. this + " sendMessageAtTime() called with no mQueue");
  15. Log.w("Looper", e.getMessage(), e);
  16. return false;
  17. }
  18. return enqueueMessage(queue, msg, uptimeMillis);
  19. }
  20. private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
  21. msg.target = this;
  22. if (mAsynchronous) {
  23. msg.setAsynchronous(true);
  24. }
  25. return queue.enqueueMessage(msg, uptimeMillis);
  26. }
sendMessage 最终会调用MessageQueu的enqueueMessage()把消息放到MessageQueue这个消息队列中.MessageQueue的next()会获取这个消息,并将消息给Looper,Looper处理消息时最终会传递给Handler 来处理,也是就Handler的dispatchMessage(). dispatchMessage进是最新消息的处理的地方.
   
   
  1. public void dispatchMessage(Message msg) {
  2. if (msg.callback != null) {
  3. handleCallback(msg);
  4. } else {
  5. if (mCallback != null) {
  6. if (mCallback.handleMessage(msg)) {
  7. return;
  8. }
  9. }
  10. handleMessage(msg);
  11. }
  12. }
在上面的代码中.
如果发送消息时是用用的post(Runnable) 这个方法,msg.callback就是post()传递的参数Runnable对象.handleCallback()就是执行 Runnable 的run方法.
如果在创建Handler 时是调用 Handler(Callback callback)这种构造函数,那边mCallback 就是构造函数传递过来的对象.这个写法有个好处就是不用创建一个继承了Handler的单独的类(也就是派生一个子类),直接在传递参数的时候实现 Callback 的 handleMessage方法就可以.
如果不是上面的2种情况就是调用的send来发送消息的,就是直接调用 handleMessage() 来处理.一般自定义的Handler 会重写这个方法.

那我们在创建Handler是自己并没有去创建Looper ,为什么没有报错呢.是因为系统已经默认给我们创建了.这个是在ActivityThread的main()里面做的.
   
   
  1. public static void main(String[] args) {
  2. ...
  3. Looper.prepareMainLooper();
  4. ...
  5. Looper.loop();
  6. ...
  7. }

3.总结

最好总结一下Android 的消息机制
Handler使用send和post相关的方法去发送消息,Handler调用 sendMessageAtTime() 和enqueueMessage() 将消息交给MessageQueue处理;
MessageQueue 接到消息之后将消息保存到一个链表中;
Looper 调用MessageQueue的next()方法来获取消息,再调用Handler的dispatchMessage来处理;
Handler 接收到消息之后根据msg.callback 和mCallBack也就是post传递的参数和构造函数传递的参数 来执行它们的相关的回调方法,如果都未null就执行handleMessage(这个方法我们一般会重写).

4. Android中使用Handler造成内存泄露的分析和解决


关于内存泄露这个部分是网上找的一个分析,感觉很好,先记录一下.便于后续研究和使用.

4.1 什么是内存泄露?

Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收。也就是说,一个对象不被任何引用所指向,则该对象会在被GC发现的时候被回收;另外,如果一组对象中只包含互相的引用,而没有来自它们外部的引用(例如有两个对象A和B互相持有引用,但没有任何外部对象持有指向A或B的引用),这仍然属于不可到达,同样会被GC回收。
Android中使用Handler造成内存泄露的原因

Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        mImageView.setImageBitmap(mBitmap);
    }
}

上面是一段简单的Handler的使用。当使用内部类(包括匿名类)来创建Handler的时候,Handler对象会隐式地持有一个外部类对象(通常是一个Activity)的引用(不然你怎么可能通过Handler来操作Activity中的View?)。而Handler通常会伴随着一个耗时的后台线程(例如从网络拉取图片)一起出现,这个后台线程在任务执行完毕(例如图片下载完毕)之后,通过消息机制通知Handler,然后Handler把图片更新到界面。然而,如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,它就有可能在GC检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用(不然它怎么发消息给Handler?),这个Handler又持有Activity的引用,就导致该Activity无法被回收(即内存泄露),直到网络请求结束(例如图片下载完毕)。另外,如果你执行了Handler的postDelayed()方法,该方法会将你的Handler装入一个Message,并把这条Message推到MessageQueue中,那么在你设定的delay到达之前,会有一条MessageQueue -> Message -> Handler -> Activity的链,导致你的Activity被持有引用而无法被回收。
个,那就是虚拟机占用内存过高,导致OOM(内存溢出),程序出错。对于Android应用来说,就是你的用户打开一个Activity,使用完之后关闭它,内存泄露;又打开,又关闭,又泄露;几次之后,程序占用内存超过系统限制,FC。

4.2 使用Handler导致内存泄露的解决方法


方法一:通过程序逻辑来进行保护。
1.在关闭Activity的时候停掉你的后台线程。线程停掉了,就相当于切断了Handler和外部连接的线,Activity自然会在合适的时候被回收。
2.如果你的Handler是被delay的Message持有了引用,那么使用相应的Handler的removeCallbacks()方法,把消息对象从消息队列移除就行了。

方法二:将Handler声明为静态类。
静态类不持有外部类的对象,所以你的Activity可以随意被回收。代码如下:
static class MyHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        mImageView.setImageBitmap(mBitmap);
    }
}

但其实没这么简单。使用了以上代码之后,你会发现,由于Handler不再持有外部类对象的引用,导致程序不允许你在Handler中操作Activity中的对象了。所以你需要在Handler中增加一个对Activity的弱引用(WeakReference):

static class MyHandler extends Handler {
    WeakReference<Activity > mActivityReference;

    MyHandler(Activity activity) {
        mActivityReference= new WeakReference<Activity>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        final Activity activity = mActivityReference.get();
        if (activity != null) {
            mImageView.setImageBitmap(mBitmap);
        }
    }
}

将代码改为以上形式之后,就算完成了。
延伸:什么是WeakReference?
WeakReference弱引用,与强引用(即我们常说的引用)相对,它的特点是,GC在回收时会忽略掉弱引用,即就算有弱引用指向某对象,但只要该对象没有被强引用指向(实际上多数时候还要求没有软引用,但此处软引用的概念可以忽略),该对象就会在被GC检查到时回收掉。对于上面的代码,用户在关闭Activity之后,就算后台线程还没结束,但由于仅有一条来自Handler的弱引用指向Activity,所以GC仍然会在检查的时候把Activity回收掉。这样,内存泄露的问题就不会出现了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值