Handler是什么
Handler是Android提供的:
- 一套更新UI的机制.
- 一套消息处理的机制,我们可以通过Handler发送和处理消息.
为什么使用Handler
Android在设计时,就封装了一套消息的创建、传递、处理机制,如果不遵循这样的机制,就没有办法更新UI信息,就会抛出异常信息.
Google为什么只设计了Handler来解决更新UI的问题
解决多线程并发问题. 假设如果在一个Activity当中,有多个线程去更新UI,并且都没有加锁机制,那么就会出现更新界面错乱. 但是如果都进行加锁处理的话,又会导致性能下降. 因此,产生了Handler的更新机制,不用去关心多线程的问题,所有的更新UI的操作,都是在主线程的消息队列当中去轮询处理.
首先提出一个BUG
new Thread(new Runnable() {
@Override public void run() {
SystemClock.sleep(1000);
tv.setText("hello catface");
}
}).start();
上代码片会报异常:只有主线程能进行更新UI的操作
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
解决办法如下
new Thread(new Runnable() {
@Override public void run() {
SystemClock.sleep(1000);
// 通过handler的post()方法在子线程中更新UI
handler.post(new Runnable() {
@Override public void run() {
tv.setText("hello catface");
}
});
}
}).start();
实现简单轮播图(本例使用ImageView,自行可用ViewPager)
图片轮播ImageRunnable实现
private int[] images = {R.drawable.1, R.drawable.2, R.drawable.3};
private int index; // 记录当前图片
private Handler handler = new Handler();
private ImageRunnable imageRunnable = new ImageRunnable();
class ImageRunnable implements Runnable {
@Override public void run() {
index++;
index = index % 3;
// 向ImageView控件设置轮播图片
iv.setImageResource(images[index]);
handler.postDelayed(imageRunnable, 1000);
}
}
主线程中开启图片轮播
handler.postDelayed(imageRunnable, 1000);
handler.sendMessage()的使用:传递int和Object对象
private TextView tv;
private Handler handler = new Handler() {
// msg为发送来的携带数据的消息
@Override public void handleMessage(Message msg) {
super.handleMessage(msg);
tv.setText(msg.arg1 + "::" + msg.arg2 + "::" + msg.obj);
}
};
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
tv = (TextView) findViewById(R.id.tv_handler);
new Thread(new Runnable() {
@Override public void run() {
SystemClock.sleep(2000);
// Message msg = new Message(); // 不推荐使用
// Message内部维护了Message池用于消息复用,避免消息对象过多浪费. 推荐使用Message.obtain()
Message msg = handler.obtainMessage(); // 获取系统消息对象
msg.arg1 = 111; // int类型
msg.arg2 = 222;
msg.obj = new Person("zhangsan", 99); // 向消息中添加对象
handler.sendMessage(msg); // 发送消息给Handler
// msg.sendToTarget(); // 效果同上
}
}).start();
}
class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override public String toString() {
return name + "------" + age;
}
}
分析上述代码片中两处:
obtainMessage()源码分析:避免没必要的消息对象的创建
public final Message obtainMessage() {
return Message.obtain(this);
}
.
.
.
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h; // target消息发送对象为Handler本身
return m;
}
.
.
.
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
// 系统中没有Message对象时才会创建消息对象
return new Message();
}
sendToTarget()源码分析
public void sendToTarget() {
// 调用Handler自己的sendMessage()方法,本质上一样
target.sendMessage(this);
}
.
.
.
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
Handler消息的移除:removeCallbacks()
handler.removeCallbacks(Runnable);
Handler消息拦截:Callback接口
private Handler handler = new Handler(new Handler.Callback() {
@Override public boolean handleMessage(Message message) {
ShowTool.show(HandlerActivity.this, "1111...");
return false; // true为截获Handler发送来的消息
}
}) {
@Override public void handleMessage(Message msg) {
ShowTool.show(HandlerActivity.this, "2222...");
}
};
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
new Thread(new Runnable() {
@Override public void run() {
handler.sendEmptyMessageDelayed(1, 2000);
}
}).start();
}
自定义与线程相关的Handler
private Handler handler = new Handler(){
@Override public void handleMessage(Message msg) {
super.handleMessage(msg);
LogTool.d("HandlerThread", "main:" + Thread.currentThread());
}
};
class MyThread extends Thread {
public Handler handler;
@Override public void run() {
Looper.prepare(); // 子线程中必须必须必须创建Looper对象
handler = new Handler() {
@Override public void handleMessage(Message msg) {
LogTool.d("HandlerThread", "thred:" + Thread.currentThread());
}
};
Looper.loop();
}
}
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
MyThread thread = new MyThread();
thread.start();
SystemClock.sleep(800);
thread.handler.sendEmptyMessage(1); // 子线程
handler.sendEmptyMessage(1); // 主线程
}
HandlerThread:解决多线程并发问题
先看存在BUG的代码片
private Handler handler = new Handler() {
// 主线程的handleMessage方法中不要执行耗时操作,否则出现卡死状态
@Override public void handleMessage(Message msg) {
super.handleMessage(msg);
LogTool.d("HandlerThread", "UI..." + Thread.currentThread());
}
};
class MyThread extends Thread {
public Handler handler;
public Looper looper;
@Override public void run() {
Looper.prepare();
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
LogTool.d("HandlerThread", "==..." + Thread.currentThread());
}
};
Looper.loop();
}
}
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
MyThread thread = new MyThread();
thread.start();
// 空指针:主线程执行到此方法时,MyThread类中Looper对象还没有创建成功
handler = new Handler(thread.looper) {
@Override public void handleMessage(Message msg) {
}
};
handler.sendEmptyMessage(1);
}
上代码片会报异常:MessageQueue空指针
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.catface/com.catface.demos.HandlerActivity}: java.lang.NullPointerException: Attempt to read from field ‘android.os.MessageQueue android.os.Looper.mQueue’ on a null object reference异常分析见代码片
handler = new Handler(thread.looper)
解决办法:通过HandlerThread
// 解决多线程并发问题 --> 将耗时操作交给子线程
thread = new HandlerThread("handler thread");
thread.start();
handler = new Handler(thread.getLooper()) { ...
主线程与子线程之间的信息交互
主线程和子线程互相定时发送消息给对方,通知对方运行相关逻辑.
private Handler threadHandler; // 子线程Handler
private Handler handler = new Handler() { // 主线程Handler
@Override public void handleMessage(Message msg) {
Message message = new Message();
LogTool.d("threadchat", "main handler");
threadHandler.sendMessageDelayed(message, 1000); // 主线程给子线程发送消息
}
};
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
// 解决Looper空指针异常
HandlerThread handlerThread = new HandlerThread("handler thread");
handlerThread.start();
// 通过HandlerThread来解决线程并发问题
threadHandler = new Handler(handlerThread.getLooper()) {
@Override public void handleMessage(Message msg) {
Message message = new Message();
LogTool.d("threadchat", "thread handler");
handler.sendMessageDelayed(message, 1000); // 子线程给主线程发送消息
}
};
// 开始主子线程间的交互,互相定时发送消息给对方
findViewById(R.id.bt_start).setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View view) {
handler.sendEmptyMessage(1);
}
});
findViewById(R.id.bt_end).setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View view) {
handler.removeMessages(1);
}
});
}
更新UI的四种方式
post
new Thread(new Runnable() {
@Override public void run() {
// post中sendMessageDelayed(getPostMessage(r), 0)
// get中调用通过Message.obtain()、callback(Runnable对象)
handler.post(new Runnable() {
@Override
public void run() {
...
}
});
}
}).start();
sendMessage:最常用
View.post(new Runnable() {
run() {
...
}
})
常见BUG:子线程创建Handler对象
new Thread(new Runnable() {
@Override public void run() {
Looper.prepare(); // 创建Handler之前必须创建Looper对象
Handler handler = new Handler();
}
}).start();
若未创建Looper对象,而直接创建Handler对象,则会抛出异常
java.lang.RuntimeException: Can’t create handler inside thread that has not called Looper.prepare()
非UI线程真的不能进行更新UI的操作吗?(否)
更新UI时会调用invalidate() –> viewParent.invalidateChild() –> invalidateChildInParent().
viewRootImpl()方法会检测当前是否为非UI线程,而该方法在Activity的onResume()中执行. 所以在onCreate()的子线程中更新UI速度很快时,viewRootImpl()方法还未执行,所以就不会报异常.
PART-C_Handler源码简介
Looper创建MessageQueue消息队列,然后无限轮询该MessageQueue中的消息,而消息的创建者即为Handler.
Looper源码
构造函数
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mRun = true;
mThread = Thread.currentThread();
}
创建了一个MessageQueue对象(消息实例).
prepare()
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(true));
}
将Looper实例存储在线程中,并保证线程中只有一个Looper实例.
loop()
public static void loop() {
final Looper me = myLooper();
if (me == null) { // loop方法必须在prepare方法之后运行
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();
/**
* 无限循环
*/
for (;;) {
Message msg = queue.next(); // 取消息,若没有则阻塞
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
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
// 将消息交给target(handler对象)的dispatchMessage方法处理
msg.target.dispatchMessage(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.recycle(); // 释放消息占据的资源
}
}
Looper的主要作用如下
- 绑定当前线程,保证一个线程仅有一个Looper实例,同时一个Looper实例中仅有一个MessageQueue实例.
- loop()方法,轮询MessageQueue中的消息,交给消息的target属性的dispatchMessage处理.
Handler源码
构造函数
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
// 获取当前线程保存的Looper实例
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
// 获取Looper实例中保存的MessageQueue()消息队列
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
.
.
.
/** sendMessage() */
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
.
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
.
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
...
// 最后均调用sendMessageAtTime()方法
// 方法内获取MessageQueue后调用enqueueMessage()方法
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; // 将当前handler作为target属性
if (mAsynchronous) {
msg.setAsynchronous(true);
}
// 将handler发送的消息最终保存到消息队列中
return queue.enqueueMessage(msg, uptimeMillis);
}
.
.
.
/**
* 上面说道Looper会不断轮询Handler发送到MessageQueue中的消息,然后回调发送该消息的handler中的dispatchMessage方法,下面请看
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 该方法为空,即我们自己复写的handleMessage方法,然后根据msg.what进行消息处理
handleMessage(msg);
}
}
PART-C_小结