Handler在Android的开发中算是最常用的东东,但有很多小小的tips或许很多人都不知道。关于Handler,Looper,MessageQueue网上已有很多文章了,这里就不在赘述。 1.从其他线程获取一个与UI线程关联的Handler:
Handler handler = new Handler(Looper.getMainLooper());
2.任务调度应该都知道可以使用post(),如果想使得你post出的runnable最快的执行,可以这样干:
handler.postAtFrontOfQueue(new Runnable(){
public void run() {
// do some work on the main thread.
}
});
3.Handler是如此的常见,所以在后台线程中如果想更新UI都可以不使用handler,因为他已经整合到view中了,直接使用view.post()即可更新UI:
final TextView text = (TextView) findViewById(R.id.text);
Thread thread = new Thread(){
public void run(){
// long time work
text.post(new Runnable(){
public void run() {
text.setText("xxxx");
}
});
}
};
thread.setPriority(Thread.MIN_PRIORITY);
thread.start();
4.如果你为耗时工作开了一个后台线程,最好是将线程的优先级设置为最低以防止主线程出现饥饿状态(CPU未分配足够的时间)。
thread.setPriority(Thread.MIN_PRIORITY);
5.可以通过删除已post的runnable来 取消待执行的runnable:
final Runnable runnable = new Runnable(){
public void run() {
// … do some work
}
};
handler.postDelayed(runnable, TimeUnit.SECONDS.toMillis(10));
Button cancel = (Button) findViewById(R.id.cancel);
cancel.setOnClickListener(new OnClickListener(){
public void onClick(View v) {
handler.removeCallbacks(runnable);
}
});
通过这种方式只能取消未执行的runnable,如果正在执行那就不会去取消了。 6.使用Message传递消息时,出于效率问题,最好使用Message.obtain(...)方法,如果每次都去用构造器new一个,如果频率较高,那就等于无形中给GC增加了很多工作:
handler.sendMessage(Message.obtain(handler, what, obj));
7.,send消息有与post类似的方法,如发送消息到队列的头部:
handler.sendMessageAtFrontOfQueue(msg);
8.取消已发送的消息。与取消runnable类似,我们也可以取消已发送但未执行的消息,而且取消message比取消post出的runnable更方便,想要取消runnable的话得需要一个runnable的引用,取消message只需要知道message的what值就行了:
handler.removeMessages(what);
9.HandlerThread的使用。Handler可以绑定到任何我们创建的线程上,这样一个线程就可以把工作交给另外一个线程来做,如可以将UI线程的工作交给后台线程,后台线程再将工作给其他线程来做。我们自己设置一个Looper thread和handler,但更方便的是使用系统提供的HandlerThread。
HandlerThread thread =new HandlerThread("tag", Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// HandlerThread包装了Looper
Handler handler = new Handler(thread.getLooper());
// 使用runnable
handler.post(new Runnable(){
public void run() {
// ... do some work in the background thread
}
});
// 发送message
Handler.Callback callback = new Handler.Callback(){ … };
Handler handler = new Handler(thread.getLooper(), callback);
为了防止内存的意外泄露,尽量将HandlerThread的生命周期和Activity的生命周期捆绑在一起。HandlerThread可以通过调用quit()来使其停止。 10.Handler使用过程中容易出现的问题。
final Runnable runnable = new Runnable(){
public void run() {
// … do some work
}
};
handler.postDelayed(runnable, TimeUnit.SECONDS.toMillis(10))
Java中匿名内部类对外部类实例有着一个隐式的引用。上面例子post了一个延迟10秒执行的任务,如果在任务执行之前activity就已经finish了,那么这个activity实例是不能被回收的,因为还存在对他的引用。通常的解决办法就是讲runnable或handler定义成top-level或者static的类,这样就很容易的将其置为空,方便垃圾回收。此外,当activity要finish的时候,如果创建了HandlerThread,要保证对其进行quit(),避免继续执行,且让GC能对其尽快的回收。
- 显式引用的泄露。在做界面交互时,可能会用到view的引用,并将其传给runnable或handler,如下:
static class MyRunnable implements Runnable {
private View view;
public MyRunnable(View view) {
this.view = view;
}
public void run() {
// … do something with the view.
}
}
这里保存对view的强引用,如果runnable的存活时间大于view,那么又造成了资源的泄露,一个解决办法就是使用WeakReference,使用的时候对view的引用进行null检查:
static class MyRunnable implements Runnable {
private WeakReference<View> view;
public MyRunnable(View view) {
this.view = new WeakReference<View>(view);
}
public void run() {
View v = view.get(); // might return null
if (v != null) {
// … do something with the view.
}
}
}
当view不存在任何强引用时,WeakReference也将失去对其的引用。