Android基础篇 Handler
Posted 彭老希
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android基础篇 Handler相关的知识,希望对你有一定的参考价值。
一、Handler作用
1)传递消息Message
Message 的构造函数是公共的,但获得其中之一的最佳方法是调用Message.obtain()或Handler.obtainMessage()方法之一,这将从回收对象池中提取它们
//1.通过handler实例获取
Handler handler = new Handler();
Message message = handler.obtainMessage();
//源码 :第一种获取方式其实也是内部调用了第二种
public final Message obtainMessage(){
return Message.obtain(this);
}
//2.通过Message获取
Message message = Message.obtain();
不建议直接new Message,Message内部保存了一个缓存的消息池,我们可以用obtain从缓存池获得一个消息,Message使用完后系统会调用recycle回收,如果自己new很多Message,
每次使用完后系统放入缓存池,会占用很多内存的
。
//传递的数据
Bundle bundle = new Bundle();
bundle.putString("msg", "传递我这个消息");
//发送数据
Message message = Message.obtain();
message.setData(bundle); //message.obj=bundle 传值也行
message.what = 0x11;
handler.sendMessage(message);
//数据的接收
final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 0x11) {
//直接获取,不需要在new
Bundle bundle = msg.getData();
String date = bundle.getString("msg");
}
}
};
2)子线程通知主线程更新ui
//创建handler
final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 0x11) {
//更新ui
......
}
}
};
new Thread(new Runnable() {
@Override
public void run() {
//FIXME 这里直接更新ui是不行的
//还有其他更新ui方式,runOnUiThread()等
message.what = 0x11;
handler.sendMessage(message);
}
}).start();
二.常用API
//消息
Message message = Message.obtain();
//发送消息
new Handler().sendMessage(message);
//延时1s发送消息
new Handler().sendMessageDelayed(message, 1000);
//发送带标记的消息(内部创建了message,并设置msg.what = 0x1)
new Handler().sendEmptyMessage(0x1);
//延时1s发送带标记的消息
new Handler().sendEmptyMessageDelayed(0x1, 1000);
//延时1秒发送消息(第二个参数为:相对系统开机时间的绝对时间,而SystemClock.uptimeMillis()是当前开机时间)
new Handler().sendMessageAtTime(message, SystemClock.uptimeMillis() + 1000);
//避免内存泄露的方法:
//移除标记为0x1的消息
new Handler().removeMessages(0x1);
//移除回调的消息
new Handler().removeCallbacks(Runnable);
//移除回调和所有message
new Handler().removeCallbacksAndMessages(null);
三.handler使用避免内存泄露
1)handler怎么使用会产生内存泄露?
public class MainActivity extends AppCompatActivity {
final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
......
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//activity被执行时,被延迟的这个消息存于主线程消息队列中1分钟,
//此消息包含handler引用,而handler由匿名内部类创建,持有activity引用,
//activity便不能正常销毁,从而泄露
handler.postDelayed(new Runnable() {
@Override
public void run() {
......
}
}, 1000 * 60);
}
}
2)如何避免handler的内存泄露?
public class MainActivity extends AppCompatActivity {
private static final Runnable myRunnable = new Runnable() {
@Override
public void run() {
//执行我们的业务逻辑
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyHandler myHandler=new MyHandler(this);
//延迟5分钟后发送
myHandler.postDelayed(myRunnable, 1000 * 60 * 5);
}
//创建静态内部类
private static class MyHandler extends Handler{
//持有弱引用MainActivity,GC回收时会被回收掉.
private final WeakReference<MainActivity> mAct;
public MyHandler(MainActivity mainActivity){
mAct =new WeakReference<MainActivity>(mainActivity);
}
@Override
public void handleMessage(Message msg) {
MainActivity mainAct=mAct.get();
super.handleMessage(msg);
if(mainAct!=null){
//执行业务逻辑
}
}
}
}
3)雷区
a)Handler.post(Runnable)其实就是生成一个what为0的Message,调用
myHandler.removeMessages(0);
会使runnable任务从消息队列中清除。
b) 子线程直接创建Handler,抛异常Can’t create handler inside thread that has not called Looper.prepare()
原因是非主线程没有
Loop
对象,所以要调用Looper.prepare()方法,而且如果主线程给子线程发送消息,还要调用一个Looper.loop()的方法(此方法保证消息队列中的消息被不停的拿出,并被处理)
class MyThread extends Thread{
@Override
public void run() {
super.run();
Looper.prepare();
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//处理消息
}
};
Looper.loop();
}
}
c)activity如被finish,但是handler刚好还在处理消息,如果需要用的资源已被释放,则会出现空指针异常。
所以在ondestory中去remove掉我们要处理的事件,还是有必要的。不想处理就直接try catch或者判空。
d)有时候你会发现removeCallbacks会失效,不能从消息队列中移除。
出现这情况是activity切入后台,再回到前台,此时的runnable由于被重定义,就会和原先的runnable并非同一个对象。所以这么做,加上static即可
static Handler handler = new Handler();
static Runnable myRunnable = new Runnable() {
@Override
public void run() {
//执行我们的业务逻辑
}
};
以上是关于Android基础篇 Handler的主要内容,如果未能解决你的问题,请参考以下文章
Android Studio - 如何从片段中停止 handler.postDelayed?
Android之WIFI-基础篇NativeDaemonConnector分析
从Handler+Message+Looper源代码带你分析Android系统的消息处理机制