handler
发现handler有内存泄漏,但是度娘的那些静态类自定义handler并没有解决问题,并且那种方式局限性很严重。然后我用现在的这种方式完全解决内存泄漏问题。人格担保有用,不行就喷我。
前言
因为Android采取了单线程UI模型,开发者无法在子线程中更新UI,为此Android为我们提供了Handler这个类,实现ui线程的队列式更新,防止出现界面更新错乱。
问题来了:
@Override
protected void onCreate(Bundle savedinstanceState) {
super.onCreate(savedInstanceState);
setcontentView(R.layout.activity_bind_url);
mHandler.sendemptymessageDelayed(0, 1000 * 2);
}
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
intenMain();
}
};
public void intenMain(){
intent intent = new Intent(LogoActivity.this, MineDeviceActivity.class);
startActivity(intent);
overridePendingTransition(R.anim.fade_out, R.anim.fade_in);
LogoActivity.this.finish();
}
当handler延迟发送信息时,就会持续占用资源,而在activity跳转之后就出现内存没有回收,导致内存泄漏。项目中集成 Square 的开源库 LeakCanary,有关这个库的介绍及使用请看:Github.LeakCanary。
Handler对象隐性地持有了Activity的对象,当发生GC是以为 message – handler – acitivity 的引用链导致Activity无法被回收,所以发生了内存泄露的问题。
完美解决方式:
首先要感谢Badoo Trading limited提供的开源Weak Handler;
WeakHandler.java
/*
* Copyright (c) 2014 Badoo Trading Limited
* permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS provided "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTAbility,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* Portions of documentation in this code are modifications based on work created and
* shared by android open Source Project and used according to terms described in the
* Apache License, Version 2.0
*/
package com.librarypo.utilall;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import java.lang.ref.weakreference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Memory safer implementation of android.os.Handler
* <p/>
* Original implementation of Handlers always keeps hard reference to handler in queue of execution.
* If you create anonymous handler and post delayed message into it, it will keep all parent class
* for that time in memory even if it could be cleaned.
* <p/>
* This implementation is trickier, it will keep Weakreferences to runnables and messages,
* and GC could collect them once WeakHandler instance is not referenced any more
* <p/>
*
* @see Handler
*
* Created by Dmytro Voronkevych on 17/06/2014.
*/
@SuppressWarnings("unused")
public class WeakHandler {
private final Handler.Callback mCallback; // hard reference to Callback. We need to keep callback in memory
private final ExecHandler mExec;
private Lock mLock = new ReentrantLock();
@SuppressWarnings("constantConditions")
@VisibleForTesting
final ChainedRef mRunnables = new ChainedRef(mLock, null);
/**
* Default constructor associates this handler with the {@link Looper} for the
* current thread.
*
* If this thread does not have a looper, this handler won't be able to receive messages
* so an exception is thrown.
*/
public WeakHandler() {
mCallback = null;
mExec = new ExecHandler();
}
/**
* Constructor associates this handler with the {@link Looper} for the
* current thread and takes a callback interface in which you can handle
* messages.
*
* If this thread does not have a looper, this handler won't be able to receive messages
* so an exception is thrown.
*
* @param callback The callback interface in which to handle messages, or null.
*/
public WeakHandler(@Nullable Handler.Callback callback) {
mCallback = callback; // Hard referencing body
mExec = new ExecHandler(new WeakReference<>(callback)); // Weak referencing inside ExecHandler
}
/**
* Use the provided {@link Looper} instead of the default one.
*
* @param looper The looper, must not be null.
*/
public WeakHandler(@NonNull Looper looper) {
mCallback = null;
mExec = new ExecHandler(looper);
}
/**
* Use the provided {@link Looper} instead of the default one and take a callback
* interface in which to handle messages.
*
* @param looper The looper, must not be null.
* @param callback The callback interface in which to handle messages, or null.
*/
public WeakHandler(@NonNull Looper looper, @NonNull Handler.Callback callback) {
mCallback = callback;
mExec = new ExecHandler(looper, new WeakReference<>(callback));
}
/**
* Causes the Runnable r to be added to the message queue.
* The runnable will be run on the thread to which this handler is
* attached.
*
* @param r The Runnable that will be executed.
*
* @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean post(@NonNull Runnable r) {
return mExec.post(wrapRunnable(r));
}
/**
* Causes the Runnable r to be added to the message queue, to be run
* at a specific time given by <var>uptimeMillis</var>.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* The runnable will be run on the thread to which this handler is attached.
*
* @param r The Runnable that will be executed.
* @param uptimeMillis The absolute time at which the callback should run,
* using the {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the Runnable will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
return mExec.postAtTime(wrapRunnable(r), uptimeMillis);
}
/**
* Causes the Runnable r to be added to the message queue, to be run
* at a specific time given by <var>uptimeMillis</var>.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* The runnable will be run on the thread to which this handler is attached.
*
* @param r The Runnable that will be executed.
* @param uptimeMillis The absolute time at which the callback should run,
* using the {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the Runnable will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*
* @see android.os.SystemClock#uptimeMillis
*/
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) {
return mExec.postAtTime(wrapRunnable(r), token, uptimeMillis);
}
/**
* Causes the Runnable r to be added to the message queue, to be run
* after the specified amount of time elapses.
* The runnable will be run on the thread to which this handler
* is attached.
*
* @param r The Runnable that will be executed.
* @param delayMillis The delay (in milliseconds) until the Runnable
* will be executed.
*
* @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the Runnable will be processed --
* if the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public final boolean postDelayed(Runnable r, long delayMillis) {
return mExec.postDelayed(wrapRunnable(r), delayMillis);
}
/**
* Posts a message to an object that implements Runnable.
* Causes the Runnable r to executed on the next iteration through the
* message queue. The runnable will be run on the thread to which this
* handler is attached.
* <b>This method is only for use in very special circumstances -- it
* can easily starve the message queue, cause ordering problems, or have
* other unexpected side-effects.</b>
*
* @param r The Runnable that will be executed.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean postAtFrontOfQueue(Runnable r) {
return mExec.postAtFrontOfQueue(wrapRunnable(r));
}
/**
* Remove any pending posts of Runnable r that are in the message queue.
*/
public final void removeCallbacks(Runnable r) {
final WeakRunnable runnable = mRunnables.remove(r);
if (runnable != null) {
mExec.removeCallbacks(runnable);
}
}
/**
* Remove any pending posts of Runnable <var>r</var> with Object
* <var>token</var> that are in the message queue. If <var>token</var> is null,
* all callbacks will be removed.
*/
public final void removeCallbacks(Runnable r, Object token) {
final WeakRunnable runnable = mRunnables.remove(r);
if (runnable != null) {
mExec.removeCallbacks(runnable, token);
}
}
/**
* Pushes a message onto the end of the message queue after all pending messages
* before the current time. It will be received in callback,
* in the thread attached to this handler.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendMessage(Message msg) {
return mExec.sendMessage(msg);
}
/**
* Sends a Message containing only the what value.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendEmptyMessage(int what) {
return mExec.sendEmptyMessage(what);
}
/**
* Sends a Message containing only the what value, to be delivered
* after the specified amount of time elapses.
* @see #sendMessageDelayed(Message, long)
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
return mExec.sendEmptyMessageDelayed(what, delayMillis);
}
/**
* Sends a Message containing only the what value, to be delivered
* at a specific time.
* @see #sendMessageAtTime(Message, long)
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
return mExec.sendEmptyMessageAtTime(what, uptimeMillis);
}
/**
* Enqueue a message into the message queue after all pending messages
* before (current time + delayMillis). You will receive it in
* callback, in the thread attached to this handler.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
return mExec.sendMessageDelayed(msg, delayMillis);
}
/**
* Enqueue a message into the message queue after all pending messages
* before the absolute time (in milliseconds) <var>uptimeMillis</var>.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* You will receive it in callback, in the thread attached
* to this handler.
*
* @param uptimeMillis The absolute time at which the message should be
* delivered, using the
* {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
return mExec.sendMessageAtTime(msg, uptimeMillis);
}
/**
* Enqueue a message at the front of the message queue, to be processed on
* the next iteration of the message loop. You will receive it in
* callback, in the thread attached to this handler.
* <b>This method is only for use in very special circumstances -- it
* can easily starve the message queue, cause ordering problems, or have
* other unexpected side-effects.</b>
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendMessageAtFrontOfQueue(Message msg) {
return mExec.sendMessageAtFrontOfQueue(msg);
}
/**
* Remove any pending posts of messages with code 'what' that are in the
* message queue.
*/
public final void removeMessages(int what) {
mExec.removeMessages(what);
}
/**
* Remove any pending posts of messages with code 'what' and whose obj is
* 'object' that are in the message queue. If <var>object</var> is null,
* all messages will be removed.
*/
public final void removeMessages(int what, Object object) {
mExec.removeMessages(what, object);
}
/**
* Remove any pending posts of callbacks and sent messages whose
* <var>obj</var> is <var>token</var>. If <var>token</var> is null,
* all callbacks and messages will be removed.
*/
public final void removeCallbacksAndMessages(Object token) {
mExec.removeCallbacksAndMessages(token);
}
/**
* Check if there are any pending posts of messages with code 'what' in
* the message queue.
*/
public final boolean hasMessages(int what) {
return mExec.hasMessages(what);
}
/**
* Check if there are any pending posts of messages with code 'what' and
* whose obj is 'object' in the message queue.
*/
public final boolean hasMessages(int what, Object object) {
return mExec.hasMessages(what, object);
}
public final Looper getLooper() {
return mExec.getLooper();
}
private WeakRunnable wrapRunnable(@NonNull Runnable r) {
//noinspection ConstantConditions
if (r == null) {
throw new NullPointerException("Runnable can't be null");
}
final ChainedRef hardRef = new ChainedRef(mLock, r);
mRunnables.insertAfter(hardRef);
return hardRef.wrAPPer;
}
private static class ExecHandler extends Handler {
private final WeakReference<Callback> mCallback;
ExecHandler() {
mCallback = null;
}
ExecHandler(WeakReference<Callback> callback) {
mCallback = callback;
}
ExecHandler(Looper looper) {
super(looper);
mCallback = null;
}
ExecHandler(Looper looper, WeakReference<Callback> callback) {
super(looper);
mCallback = callback;
}
@Override
public void handleMessage(@NonNull Message msg) {
if (mCallback == null) {
return;
}
final Callback callback = mCallback.get();
if (callback == null) { // Already disposed
return;
}
callback.handleMessage(msg);
}
}
static class WeakRunnable implements Runnable {
private final WeakReference<Runnable> mDelegate;
private final WeakReference<ChainedRef> mReference;
WeakRunnable(WeakReference<Runnable> delegate, WeakReference<ChainedRef> reference) {
mDelegate = delegate;
mReference = reference;
}
@Override
public void run() {
final Runnable delegate = mDelegate.get();
final ChainedRef reference = mReference.get();
if (reference != null) {
reference.remove();
}
if (delegate != null) {
delegate.run();
}
}
}
static class ChainedRef {
@Nullable
ChainedRef next;
@Nullable
ChainedRef prev;
@NonNull
final Runnable runnable;
@NonNull
final WeakRunnable wrapper;
@NonNull
Lock lock;
public ChainedRef(@NonNull Lock lock, @NonNull Runnable r) {
this.runnable = r;
this.lock = lock;
this.wrapper = new WeakRunnable(new WeakReference<>(r), new WeakReference<>(this));
}
public WeakRunnable remove() {
lock.lock();
try {
if (prev != null) {
prev.next = next;
}
if (next != null) {
next.prev = prev;
}
prev = null;
next = null;
} finally {
lock.unlock();
}
return wrapper;
}
public void insertAfter(@NonNull ChainedRef candidate) {
lock.lock();
try {
if (this.next != null) {
this.next.prev = candidate;
}
candidate.next = this.next;
this.next = candidate;
candidate.prev = this;
} finally {
lock.unlock();
}
}
@Nullable
public WeakRunnable remove(Runnable obj) {
lock.lock();
try {
ChainedRef curr = this.next; // Skipping head
while (curr != null) {
if (curr.runnable == obj) { // We do comparison exactly how Handler does inside
return curr.remove();
}
curr = curr.next;
}
} finally {
lock.unlock();
}
return null;
}
}
}
自定义无内存泄漏handler,完全替代原生handler用法和原生差不多:
消息接收:
private WeakHandler mWeakHandler=new WeakHandler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
System.out.println("返回消息:"+msg.obj);
intenMain();
return false;
}
});
消息发送:
mWeakHandler.sendEmptyMessageDelayed(0, 1000 * 2);
发送的方法和原生是一样,比原生的方便使用多了。
总结:使用自定义的WeakHandler,基本使用方法和原生差不多,只不过不再使用handler。在这里只能吐槽Android技术并不是原生的一定是最好的,有时候我们开发者也是无所不能的。
有用请点赞或者评论,谢谢咯!!
相关阅读
Handle的原理(Looper、Handler、Message三者关系)
转载请注明出处:http://blog.csdn.net/lowprofile_coding/article/details/72580044 介绍 前面的内容对Handler做了介绍,也讲解
Handler大家应该都很熟悉了,毕竟只要是涉及到Android里面线程间的通讯,总会看到它的身影。我平时也经常使用Handler,今天整理笔记,就
Handler还需要用到弱引用(WeakReference)吗?
转自:https://blog.csdn.net/alcoholdi/article/details/54948058 写的很好网上很多文章都说写Hanlder,需要用static声明为静态的
Message.obtain() 和Handler.obtainMessage()的区别
Message.obtain() 和Handler.obtainMessage()的区别 性能更优越 尽管Message的构造器是公开的,但是获取Message对象的最好
Android HandlerThread使用介绍以及源码解析
1. 前言 首先,看一下官方对HandlerThread的解释: Handy class for starting a new thread that has a looper. The looper can th