handler主要用法有两各
- 在新启动的线程中发送消息
- 在主线程中获取 处理消息
Android的消息传递机制是另一种形式的"事件处理" 这种机制主要是为了处理android的多线程问题——android只允许UI线程(当一个程序第一次启动时创建Main线程)修改activity中的UI组件 这样会导致新启动的线程无法动态改变界面组件的属性值。
如采用下图创建线程方式修改textview的值
会报如下错
注意:旧的API版本可以直接new Handler(MainActivity中 子线程不行) 在新版本中该使用方法被废弃了 需要重写一个类继承Handler 然后去使用自己写的handler 如果想在子线程里new一个handler 需要先调用Looper.prepare() 再去创建 并且调用 Looper.loop();接收信息
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
Looper.loop();
}
})
在实际android开发中 尤其时涉及动画的游戏开发 需要让新启动的线程周期性的改变界面组件的属性值 这就需要Handler的消息传递机制
如下修改即可
在此处修改textview
实现方法
- 延迟跳转 postDelayed方法 如图设置3s钟后跳转到另一个页面
- 线程间通信 通过重写handleMessage方法处理消息
- 使用hasMessages(int what)检查消息队列中是否包含what属性为指定值的消息
- 使用hasMessages(int what,Object object)检查消息队列中是否包含what属性为指定值且object属性为指定对象的消息
- 多个重载的Message obtainMessage():获取消息
- sendMessage(Message msg)立即发送消息(还有其他类似方法如发送空消息 指定多少毫秒后发送消息)
实例 自动播放动画
public ImageView mContentVideo = itemView.findViewById(R.id.im_video);
{
// onCreat或者其他处理数据的方法里
MyHandler handler = new MyHandler();
// 定义一个计时器 让该计时器周期性执行任务
new Timer().schedule(new TimerTask() {
@Override
public void run() {
// 发送空消息 0x:代表十六进制
handler.sendEmptyMessage(0x1233);
}
},0,3000);
}
class MyHandler extends Handler{
// 定义周期性显示的图片
int[] imageIds = {R.drawable.im_v6,
R.drawable.im_v2, R.drawable.im_v1, R.drawable.im_v4, R.drawable.im_v3};
int currentImageId = 0;
@Override
public void handleMessage(@NonNull Message msg) {
// 如果该消息是本程序发送的
if (msg.what == 0x1233) {
// 动态修改显示的图片
mContentVideo.setImageResource(imageIds[(currentImageId++) % imageIds.length]);
}
}
}
实例 使用handler实时更新进度条
与Looper MessageQueue关系
MessageQueue: 消息队列 采用先进先出的方式来管理Message 程序创建Looper对象时会在他的构造器中创建MessageQueue对象(private修饰 表名无法通过构造器创建Looper对象)
Message:Handler接受和处理的消息对象
其自身属性
创建方式:
Looper:每个线程只能拥有一个Looper 他的loop方法负责读取MessageQueue中的消息 读到消息后就把消息交给发送该消息的Handler进行处理
Handler:发送消息和处理消息 使用Handler发送的消息必须被送到指定的MessageQueue 也就是说当前线程必须要有一个MessageQueue对象 否则就没有地方保存消息
并且MessageQueue是由Looper负责管理的 也就是说线程中必须也要有个Looper对象 为了保证此状态 可分如下两种情况:
- 在主UI线程中 系统已经初始化了一个Looper对象 因此程序直接创建Handler即可
- 在子线程中创建Looper的步骤
- 调用Looper中的prepare()方法为当前线程创建Looper对象 其构造器会创建与之配套的MessageQueue
- 有了Looper之后创建Handler子类实例 重写handleMessage()方法 该方法负责处理来自其他线程的消息
- 调用Looper的loop()方法启动Looper
prepare()该方法保证了每个线程只有一个Looper对象 如果有多个会抛出一个RuntimeException
//这是第一个开始使用的地方
public static void prepare() {
prepare(true);
}
//主要创建了Looper对象,MessageQueue对象,指定了创建的线程
private static void prepare(boolean quitAllowed) {
//只能调用一次,第二次就抛异常了
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
调用Looper的静态loop()方法启动它 该方法采用一个死循环 不断取出MessageQueue中的消息 并将取出来的消息分给对应的Handler处理
//开始循环分发MessageQueue中的消息到Handler
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
boolean slowDeliveryDetected = false;
for (;;) {
//获取消息队列中的消息
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
if (thresholdOverride > 0) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
final boolean needStartTime = logSlowDelivery || logSlowDispatch;
final boolean needEndTime = logSlowDispatch;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
try {
//然后分发消息到Handler的处理中
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logSlowDelivery) {
if (slowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
slowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
slowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
//释放消息
msg.recycleUnchecked();
}
}