1、什么是handler?
handler主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另一个部分在消息队列中逐一将消息取出,然后对消息进行处理。
1、handler内存泄漏的测试?
private Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
startActivity(new Intent(MainActivity.this, PersonalActivity.class));
return false;
}
});
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
test();
}
private void test() {
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.obj = "handler内存泄漏测试";
message.what = 3;
SystemClock.sleep(3000);
handler.sendMessage(message);//跳转的第二个界面
}
}).start();
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.e("handler内存泄漏测试 ", "onDestroy");
}
如上面代码,在进入MainActivity后,退出后,但是还会跳转到PersonalActivity界面。这个怎么解决呢?
方法1:在发送消息的时候判断是否为空。在销毁activity的时候,handler移除对应的message和handler置空
private void test() {
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.obj = "handler内存泄漏测试";
message.what = 3;
SystemClock.sleep(3000);
//不为空的时候调用
if (handler != null) handler.sendMessage(message);
}
}).start();
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.e("handler内存泄漏测试 ", "onDestroy");
handler.removeMessages(3);
handler = null;
}
方法2:延时发送消息,销毁activity的时候,移除对应的message
private void test() {
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.obj = "handler内存泄漏测试";
message.what = 3;
//延时发送消息
handler.sendMessageDelayed(message,3000);
}
}).start();
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.e("handler内存泄漏测试 ", "onDestroy");
handler.removeMessages(3);
}
2、为什么不能在子线程创建Handler
//错误的使用
private void test() {
new Thread(new Runnable() {
@Override
public void run() {
new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
}
}).start();
}
//正确的使用
private void test2(){
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();//为子线程创建Looper
new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//子线程消息处理
}
};
Looper.loop(); //开启消息轮询
}
}).start();
}
直接在子线程创建Handler,会报如下错误,因为没有给子线程创建一个Looper对象。
java.lang.RuntimeException: Can't create handler inside thread >Thread[Thread-2,5,main] that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:207)
at android.os.Handler.<init>(Handler.java:119)
at com.netease.handler.sample.MainActivity$4.run(MainActivity.java:98)
at java.lang.Thread.run(Thread.java:919)
主线程是什么时候创建Looper的呢?
3、new Handler()两种写法有什么区别?
private Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
startActivity(new Intent(MainActivity.this, PersonalActivity.class));
return false;
}
});
// 这是谷歌备胎的api,不推荐使用
private Handler handler2 = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
textView.setText(msg.obj.toString());
}
};
4、ThreadLocal用法和原理
public class ThreadLocalTest {
@Test
public void test() {
// 创建本地线程(主线程)
final ThreadLocal<String> threadLocal = new ThreadLocal<String>() {
@Override
protected String initialValue() {
// 重写初始化方法,默认返回null,如果ThreadLocalMap拿不到值再调用初始化方法
return "冯老师";
}
};
// 从ThreadLocalMap中获取String值,key是主线程
System.out.println("主线程threadLocal:" + threadLocal.get());
//--------------------------thread-0
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// 从ThreadLocalMap中获取key:thread-0的值?没有,拿不到值再调用初始化方法
String value1 = threadLocal.get();
System.out.println("thread-0:" + threadLocal.get());
// ThreadLocalMap存入:key:thread-0 value:"熊老师"
threadLocal.set("熊老师");
System.out.println("thread-0 set >>> " + threadLocal.get());
// 使用完成建议remove(),避免大量无意义的内存占用
threadLocal.remove();
}
});
thread.setName("thread-0");
thread.start();
//--------------------------thread-1
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
// 从ThreadLocalMap中获取key:thread-1的值?没有,拿不到值再调用初始化方法
String value1 = threadLocal.get();
System.out.println("thread-1:" + threadLocal.get());
// ThreadLocalMap存入:key:thread-1 value:"刘老师"
threadLocal.set("刘老师");
System.out.println("thread-1 set >>> " + threadLocal.get()); // 刘老师
// 使用完成建议remove(),避免大量无意义的内存占用
threadLocal.remove();
}
});
thread.setName("thread-1");
thread2.start();
}
}
2、Handler+Message原理分析
- Handler.sendMessage()发送消息时,会通过MessageQueue.enqueueMessage()向MessageQueue中添加一条消息;
- 通过Looper.loop()开启循环后,不断轮询调用MessageQueue.next();
- 调用目标Handler.dispatchMessage()方法,然后调用handler中的handleMessage方法
handler原理流程图
sendMessage发送消息
1、为什么主线程用Looper死循环不会引起ANR异常?
因为在Looper.next()开启死循环的时候,一旦需要等待时 或 还没有执行到执行的时候,会调用NDK里面的JNI方法,释放当前时间碎片,这样就不会引发ANR异常了。
2、为什么handler构造方法里面的Looper不是直接new?
如果在Handler构造方法里面newLooper,怕是无法保证Looper的唯一,只有用Looper.prepare()才能保证唯一性,具体去看prepare方法。
3、MessageQueue为什么要放在Looper私有的构造方法里初始化?
因为一个线程只绑定一个Looper,所以在Looper构造方法李曼初始化就可以保证mQueue也是唯一的的,Thread对应一个Looper对应一个mQueue
网友评论