什么是Handler?

抛出三个问题
1 handler内存泄漏?
2 handler为什么不能在子线程中 new 出来?
3 new Handler() 两种写法的区别
1 handler内存泄漏?通过代码分析
在 MainActivity 中创建 Handler对象,当收到消息后 就跳转到另外一个 PersonActivity
public Handler handler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
super.handleMessage(msg);
startActivity(new Intent(MainActivity.this, PersonActivity.class));
}
};
触发事件
button.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
new Thread(new Runnable()
{
@Override
public void run()
{
SystemClock.sleep(5000);
if (handler != null)
{
Message message = handler.obtainMessage();
message.what = 1;
handler.sendMessage(message);
}
}
}).start();
}
});
执行逻辑
当我们点击Button按钮后,在子线程中执行上述代码,休眠5秒后,发送一条消息执行跳转逻辑,我们点击按钮后立即back健返回桌面,此时是否会跳转到PersonActivity这个界面了?
重写onDestroy方法,查看当点击back按钮后,MainActivity 是否销毁,销毁就会执行onDestroy()方法
@Override
protected void onDestroy()
{
super.onDestroy();
Log.e(TAG, "=======>>onDestroy");
}
最后结果发现onDestroy()执行了,也跳转到了PersonActivity这个界面,按道理说MainActivity 销毁后,就不应该再去执行跳转逻辑啊,事实上 这就是Handler的内存泄漏点,MainActivity 其实并没有被真正销毁,因为
handler 对象在子线程中被引用导致handler对象不能被回收,MainActivity也不能被真正的销毁,我们只需要在onDestroy()方法里加上 handler = null;
@Override
protected void onDestroy()
{
super.onDestroy();
handler = null;
}
这样,当MainActivity执行onDestroy()方法时,我们手动把handler 设置为空,这样handler 就能被gc,这样就能解决 由handler 造成的内存泄漏问题
2 handler为什么不能在子线程中 new 出来?
button.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
new Thread(new Runnable()
{
@Override
public void run()
{
new Handler();
}
}).start();
}
});
这样一段代码,当在部分手机,例如华为手机上,它是不会报错的,猜想华为应该把android原生的改了吧,但是在谷歌的模拟器上,它是会抛出异常的
11-25 13:48:07.967 1367-1428/? E/AndroidRuntime: FATAL EXCEPTION: Thread-86
Process: com.rx.hander, PID: 1367
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:200)
at android.os.Handler.<init>(Handler.java:114)
at com.rx.hander.MainActivity$4$1.run(MainActivity.java:72)
at java.lang.Thread.run(Thread.java:818)
Can’t create handler inside thread that has not called Looper.prepare()
我们来看看 new Handler() 的源码

再往下找

关键代码
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
这就是抛出上面异常的代码,它的执行条件是 mLooper == null,就是说 Looper.myLooper()取出的对象是一个空,为什么会这样了?我们先来了解一个ActivityThread这个类
ActivityThread可以说是我们App真正的入口, 当应用程序启动时,会执行ActivityThread里面的main方法
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
// 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());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
//*****************************
Looper.prepareMainLooper();
//*****************************
// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
// It will be in the format "seq=114"
long startSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SEQ_IDENT.length()));
}
重点关注 Looper.prepareMainLooper(); 创建一个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();
}
}
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));
}
sThreadLocal.set(new Looper(quitAllowed));
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
ActivityThread 它所处的是一个主线程,当app启动时,会执行里面的main()方法,执行 main()方法中的Looper.prepareMainLooper(),创建一个消息循环对象,并且这个 Looper对象是全局唯一的
//在Looper类里面 它是 static 修饰的
private static Looper sMainLooper; // guarded by Looper.class
sThreadLocal.set(new Looper(quitAllowed));,重点关注里面的 new Looper(quitAllowed)
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
创建一个Looper对象,对象里面包含一个 MessageQueue队列,包含一个当前线程 mThread ,
再回到 这段代码
sThreadLocal.set(new Looper(quitAllowed));
把创建这个Looper对象set进 sThreadLocal 里面来
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
最终 执行 createMap(t, value); 当前线程作为 key值 , looper对象作为值被放在 map对象中去,那么既然向map中添加对象,那肯定有从map中取出对象才有意义, 那在那里取出的对象了?
答案是 在new Hander()对象时,就有取的操作了
第一步
new Thread(new Runnable()
{
@Override
public void run()
{
new Handler();
}
}).start();
第二步
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 " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
第四步
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
第五步
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
Thread t = Thread.currentThread(); 重点来了 t 当前线程作为key值 去map里面取值,我们是在子线程里面new 的 handler 对象,那么当前线程就是 子线程,我们APP启动时是在主线程里面创建的Looper对象,并且 map的key值是主线程,现在我们现在用子线程key值去取 当然取不到了?
所以
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
子线程当中取到的 mLooper 为空,所以抛出异常,到此我们解决了第二个问题,handler为什么不能在子线程中 new 出来。
3 new Handler() 两种写法的区别
第一种写法
public Handler handler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
super.handleMessage(msg);
}
};
第二种写法
public Handler handler1=new Handler(new Handler.Callback()
{
@Override
public boolean handleMessage(Message msg)
{
return false;
}
});
找到 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);
}
}
当 mCallback 不为空 就调用
mCallback.handleMessage(msg)
否则就执行
handleMessage(msg);
这个handleMessage(msg) 就是 Handler中的一个空实现方法
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
上面的第一种方法就是重写的这个方法,所以说了,推荐使用第二种写法,第一种写法更像备胎写法
本文深入探讨了Android中Handler的工作原理,解析了其内存泄漏的原因及解决方案,解释了为何不能在子线程直接创建Handler,以及两种创建Handler方式的区别。
4451

被折叠的 条评论
为什么被折叠?



