Android开发之Handler消息传递机制总结
Handler消息传递机制:主要用于对异步任务的消息处理机制;
Handler工作原理:当任务发出消息时,会将所发的消息压入消息队列(队列:先进先出)中,然后通过轮询的方式对队列里面的消息逐一取出,并在handleMessage事件中对取出的消息进行监听,然后做出相应的处理, 这种机制通常用来处理相对耗时比较长的操作。如下图所示:
Handler应用场景:在Android开发中,一些耗时的操作一般会放入其他线程内运行,这样可防止堵塞主线程运行;但是由于线程安全机制,开启的其他线程不能直接操作UI;UI操作只能在主线程(也称之为UI线程)内进行操作;如果其他线程内的任务想修改UI,那怎么办呢?不慌:通过Handler消息传递机制来通知主线程对UI进行修改;
Handler使用案例:短信验证码发送的倒计时功能,效果图如下:
这里不涉及对上述Demo中UI布局的介绍,只说明倒计时功能实现的具体操作:
倒计时功能,主要用到了Thread和Handler这两个类,同时还涉及一些弱引用的概念;弱引用Activity主要为了GC(垃圾回收机制)对Activity进行回收;
在使用之前先大概了解下弱引用,在Android中通过WeakReference来对引入的Activity进行弱引用;
为什么要用弱引用?
Java的垃圾回收机制(GC)对于对象回收的条件:1.对象没有没有被引用;2.GC处于运行状态;
如果在Handler类中直接引入Activity,由于Handler处于运行状态,导致Activity仍然处于应用状态,GC无法回收Activity,容易导致内存泄漏;
WeakReference原理:当一个对象被WeakReference指向,而没有其他非WeakReference(强引用)指向时,就会被GC给回收;在使用被WeakReference指向的对象时,需要先判断该对象是否已经被回收;
WeakReference wk=new WeakReference<>(Object); //Object是自定义的对象
if(wk.get() != null){ //判断引用的对象有没有被回收
//dosometime
}
if(wk.get() != null){ } //通过WeakReference类的get实例方法来判断对象是否被回收
WeakReference特点:它何时被回收是不可确定的, 因为这是由GC运行的不确定性所确定的. 所以, 一般用weak reference引用的对象是有价值被cache, 而且很容易被重新被构建, 且很消耗内存的对象;
声明MyHandler类并继承Handler类,同时对Handler中的handleMessage方法进行重写;代码如下:
package com.utils;
import android.app.Activity;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.RequiresApi;
import android.widget.Button;
import com.foodie.pver.titlefoodie.R;
import java.lang.ref.WeakReference;
public class MyHandler extends Handler {
WeakReference<Activity> wr;
private Button yzm_btn_text;
public MyHandler(Activity activity,int reid){
wr=new WeakReference<>(activity); //对activity进行弱引用,防止Activity未被垃圾回收机制GC回收
yzm_btn_text=activity.findViewById(reid); //获取需要操作的控件,也就是验证码Button控件
}
@Override
public void handleMessage(Message msg){//重写Handler类中的handleMessage()
super.handleMessage(msg);
Activity activity=wr.get();
if(activity != null){ //判断弱引用是否存在
switch (msg.what){ //msg.what标识设置
case 0x00:
yzm_btn_text.setClickable(false);
yzm_btn_text.setBackgroundResource(R.drawable.no_activity);
yzm_btn_text.setText(String.format("已发送%ss", String.valueOf(msg.arg1)).toLowerCase());
break;
case 0x01:
yzm_btn_text.setClickable(true);
yzm_btn_text.setBackgroundResource(R.drawable.yes_activity);
yzm_btn_text.setText("重新获取");
break;
default:
break;
}
}
}
}
MyHandler类继承Handler类,注意Android中Handler引入的包是Android.os.Handler,而不是Java方面的Handler包;同时重写handleMessage()方法监听handler发送过来的数据,并作出相应的处理;在这里Handler处于主线程位置,所以可以对Android中的UI控件进行修改;
上面监听消息的功能有了,接下来就要对开启在其他线程里面的任务来发送消息给Handler消息队列;
public void startTimeNum(final int num){
new Thread(){
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
@Override
public void run(){
super.run();
for(int i=num;i>=0;i--){
Message mes = obtainMessage();
mes.arg1 = i;
mes.what = 0x00;//计时中的标志
sendMessage(mes);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Message mes = obtainMessage();
mes.what = 0x01;//计时中的标志
sendMessage(mes);
}
}.start();
}
以上代码中主要有3点:1.通过Thread开启线程;2.obtainMessage()方法主要是用来获取Message对象,然后发送数据;3.通过Thread.sleep()方法设置延时;
获取Message对象的方式有3种:
1.通过new Message()实例化构造函数获取对象;
2.通过obtain()方法获取对象;
3.通过obtainMessage()方法获取对象;
获取Message对象的最好方法是调用Message.obtain()或者Handler.obtainMessage(), 这样是从一个可回收对象池中获取Message对象。
总结:通过obtain()方法获取对象,就是从整个Messge池中返回一个新的Message实例,在许多情况下使用它,因为它能避免分配新的对象,从而减少内存的开销了。