Andorid:Handler多种使用方式
Hanlder的作用
相信做Andorid开发的工程师对Handler的使用都不陌生,那为什么还会有这篇文章呢,只是为了加强自己的记忆,好了言归正传,话说为什么Android要引入Handler呢?我想最本质的目的是为了实现跨线程通信的,那为什么要设计成只能通过Handler机制来更新UI呢?我想最本质的目的就是解决多线程并发的问题。就比如如果没有这套机制,有多个线程去同时更新UI,会出现什么问题,会是界面很混乱,无法控制更新UI的先后顺序,但这时会有人说,那就加锁呗,可加锁势必会影响响应速度,给用户的体验势必会下降。所以Handler机制就应运而生了。Handler机制就是不断的从消息队列中取消息,然后再发送到UI线程进行UI的更新。既然是消息队列,那肯定也就保证了消息的先进先出顺序执行了。
这篇文章的重点是Handler的使用,当然也会简单的分析一下源码, 至于Hanlder的工作原理请关注另一篇文章。
Hanlder的使用
首先在Activity中先创建一个Handler对象如下:
private static Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Object object = msg.obj;
Bundle bundle = msg.getData();
int anInt = bundle.getInt("handler", 0);
Log.d("HandlerActivity:", "arg1:" + msg.arg1 + " arg2:" + msg.arg2 + " object:" + object.toString() + " bundleInt:" + anInt);
break;
case 2:
Log.d("HandlerActivity:", "Message.obtain()");
break;
case 3:
Log.d("HandlerActivity:", "handler.obtainMessage();");
break;
default:
Log.d("liuy=default=", "handler");
break;
}
}
};
细心的同学可能已经发现了,刚刚创建Handler的代码有一个警告,鼠标放上给出的提示是:This Handler class should be static or leaks might occur (null) more,大致意思是Handler类应该定义成静态类,否则可能导致内存泄露。所以有强迫症的同学要想去除这些警告,那就需要把Handler定义成静态类并加上WeakRefrence的机制,基本上可以避免出现泄漏的情况。但这种写法太繁琐了,所以就有了另一种写法,如下:
private static Handler handler2 = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
Log.d("HandlerActivity:", "handle---");
return true;
}
});
这种写法只是创建了一个静态对象,并不是匿名内部类,所以也就不会持有外部类的引用,就不会出现警告了。可这里又有一个返回值,那返回值的作用是什么呢?这就需要看下源码:
首先创建一个有参构造的Handler对象和Callback对象
public Handler(Callback callback) {
this(callback, false);
}
public interface Callback {
public boolean handleMessage(Message msg);
}
再跟进入是:
public Handler(Callback callback, boolean async) {
....省略....
mCallback = callback;
....省略....
}
这里我们只需知道把我们在外面创建的Callback对象赋值给了Handler的全局变量mCallback了。
当有消息需要处理时,会回调Hanlder的dispatchMessage()方法:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
if (msg.callback != null)这条语句等到下一遍源码分析再讲解,先专注else里的代码,首先判断了下mCallback是否为空,既然我们在上边已经给他赋值了,那肯定不为空,那就会调用Callback的handleMessage方法,并把消息也传递过去,当消息处理完以后,如果返回false,那就会继续往下执行,执行第10行的handleMessage(msg)方法,当然这时又会出现内存泄漏的警告提示,所以一般我们都返回true就可以了。
Message的使用
Message对象获取有很多种,下面就一一介绍:
第一种:
private void sendMsg0() {
Message msg = new Message();
msg.what = 1;
msg.arg1 = 1;
msg.arg2 = 2;
msg.obj = new Object();
msg.obj = new People("小明");
Bundle bundle = new Bundle();
bundle.putInt("handler", 1);
msg.setData(bundle);
handler.sendMessage(msg);
}
msg.what是消息的识别码,便于在Handler的handleMessage方法中根据what识别出不同的消息
msg.arg1,msg.arg2是Message给我们提供的两个int类型的传递,是setData的低成本替代品
msg.obj可以传递一个对象
msg.setData()可以传递一个Bundle对象
Message对象可以直接new一个,这种方式官方是不建议的,因为可以使用消息池的对象。
第二种:
private void sendMsg2() {
Message msg = Message.obtain();
msg.what = 2;
handler.sendMessage(msg);
}
通过obtain()先从消息池中获取Message对象,如果池中没有则创建一个对象并返回,空说无凭来看下源码:
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
sPool是消息池中的一个对象
第3行先判断消息池中是否有对象,没有则直接new一个Message,有则把sPool赋值给Message并把消息池中的上一条赋值给sPool,然后再把消息池中的数量减一。
看到这肯定会有一个疑问,那什么时候赋值的呢,那就简单的介绍一下,具体介绍请看下一篇Handler的源码解析。
首先在Looper.loop()方法中对消息进行循环,消息执行完以后会放到消息池进行缓存
public static void loop() {
....省略......
for (;;) {
....省略......
msg.recycleUnchecked();//消息进行回收
}
}
void recycleUnchecked() {
....省略......
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;//把使用完的消息赋值给next
sPool = this;
sPoolSize++;//消息池数量+1
}
}
}
看到这里应该能明白点了吧,其实就是把不用的Message放到消息池里边
第三种:
private void sendMsg3() {
Message msg = handler.obtainMessage();
msg.what = 3;
//只有通过handler获取的的Message对象,才能使用下边这种
//方法发送消息
msg.sendToTarget();
}
这种写法其实就是把第二种写法又封装了下,可以看到里边还是调用了obtain()方法
public final Message obtainMessage()
{
return Message.obtain(this);
}
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;//把hanlder赋值给target
return m;
}
再来看一下sendToTarget()方法
public void sendToTarget() {
target.sendMessage(this);
}
是不是和hanlder发送消息差不多呢,其实就是一样的,target就是handler。
Handler的post理解与用法
先看一下用法:
private void sendMsg6() {
handler.post(new Runnable() {
@Override
public void run() {
Log.d("HandlerActivity:", "postRunnable");
}
});
}
咋一看像是开了一个子线程,其实静心一想不对,开线程调用的是start()方法啊,这里什么都没调用,所以并没有改变当前的线程。先看下源码吧
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
再看下getPostMessage()方法
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
第2行还是从消息池中获取Message
第3行把Runnable对象赋值给callback
第4行返回Message
再看下sendMessageDelayed方法
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;//获取消息队列
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//把消息放到消息队列中
return queue.enqueueMessage(msg, uptimeMillis);
}
这里其实就是把Runnable对象封装到Message对象中并压入到消息队列,当消息队列中有消息后,就会循环消息,当消息执行完以后就会回调dispatchMessage方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
还记得开头说的这个if语句吗,当我们发送一个post方法时,已经给callback赋值了,所以这里走if语句,那再看下handleCallback方法
private static void handleCallback(Message message) {
message.callback.run();
}
还记得message.callback是什么吗,就是把Runnable压入到Message对象中的Runnable对象,所以这个方法就是执行了下Runnable的run方法。说的有些啰嗦,大家理解就好。
Handler其他使用
private void sendMsg4() {
Message msg = handler.obtainMessage();
handler.sendEmptyMessage(4);
//SystemClock.uptimeMillis():从开机到现在的毫秒数(手机深度睡眠的时间不包括在内)
//System.currentTimeMillis():获取的是系统的时间,可以使用SystemClock.setCurrentTimeMillis(long millis)进行设置
//如果人为设置这个时间,那发送的延迟任务就不准了
//sendMessageAtTime->enqueueMessage
handler.sendEmptyMessageAtTime(5, 2000);//2000:延迟时间
//sendMessageDelayed-> sendMessageAtTime->enqueueMessage
handler.sendEmptyMessageDelayed(6, 2000);
//enqueueMessage
handler.sendMessageAtTime(msg, 2000);
//sendMessageAtTime->enqueueMessage
handler.sendMessageDelayed(msg, 2000);
//删除消息
handler.removeMessages(1);
}
这几种发送消息的方法其实最终都是调用了enqueueMessage()方法,也没什么好说的。
下一篇: IO流:Reader/Write字符流
推荐阅读
-
使用docker创建静态网站应用(多种方式)
-
Mysql count 的多种使用方式性能比较
-
Andorid:Handler多种使用方式
-
vue中使用axios的多种方式
-
phalcon 自定义事件使用的多种方式
-
使用session_set_save_handler函数重载SESSION存储方式之MYSQL
-
【黑马Android】(05)短信/查询和添加/内容观察者使用/子线程网络图片查看器和Handler消息处理器/html查看器/使用HttpURLConnection采用Post方式请求数据/开源项目_html/css_WEB-ITnose
-
phalcon 自定义事件使用的多种方式
-
使用session_set_save_handler函数重载SESSION存储方式之MYSQL
-
使用docker创建静态网站应用(多种方式)