系统为什么不允许在子线程里访问UI?那是因为Android的UI控件线程不安全,如果多线程并发访问的话会导致UI控件处于不可预期的状态。
ThreadLocal 工作原理
ThreadLocal是一个线程内部的储存类,通过它可以在指定的线程中存储数据,数据储存以后,只有在指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据。
ThreadLocal里边维护了一个Thread的内部类ThreadLocalMap,每个Thread初始化时都会构建一个新的ThreadLocalMap,从而达到多个线程下数据的独立性,ThreadLocalMap是一个以当前ThreadLocal为key,内部类Entry为值的Map,Entry是ThreadLocalMap的内部类,里边维护了一个数组。
Handler机制 四种主体类
- Lopper:消息轮训器
- MessageQueue:消息队列
- Handler:处理消息
- Message:消息对象
开启应用时
- 1) ActivityThread.java
当应用启动时,会调用ActivityThread的main方法,在main方法里调用prepareMainLooper()方法和loop()方法
// ActivityThread.java
public static void main(String[] args) {
Looper.prepareMainLooper();
........
Looper.loop();
}
- 2)Looper.java
# 1 在prepare()方法里回去获取当前的Looper,如果没有的话(启动应用时是没有的)会走下面sThreadLocal.set(new Looper(quitAllowed)),在Looper的初始化操作中会初始化MessageQueue,并且绑定当前线程(当前即为ui线程);
# 2 在loop()方法里会去获取当前Looper,并获取MessageQueue的实例,如果为null的话会有异常报出,然后遍历mQueue去拿到Message的引用msg,调用dispatchMessage(msg)(其实就是handler的dispatchMessage(msg))
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
// prepareMainLooper() =>
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构造
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
// loop()
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;
....
for (;;) {
Message msg = queue.next(); // might block
//这里的msg.target指的就是handler
msg.target.dispatchMessage(msg);
msg.recycle();
}
}
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
初始化Handler
- 1)MainActivity.java
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler.sendEmptyMessageAtTime(1,1000);
}
- 2 )Handler.java
#1 在handler初始化方法里获取looper对象,通过looper对象回去mQueue,在mHandler.sendEmptyMessage()时,通过msg.target = this将当前handler绑定到message中,再通过获取到的mQueue.enqueueMessage()将当前message传入mQueue中
public Handler() {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
}
// => sendEmptyMessageAtTime
public boolean sendMessageAtTime(Message msg, long uptimeMillis){
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
//这里其实就是把handler赋值给message.target
msg.target = this;
sent = queue.enqueueMessage(msg, uptimeMillis);
} else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
}
return sent;
}
// msg.target.dispatchMessage(msg); 接上边的looper
// handleMessage(msg) 创建handler时,复用的方法
public void handleMessage(Message msg) {
}
总结
-
当应用启动的时候,在ActivityThread的main方法中会调用Looper.prepareMainLooper(),执行Looper.prepare()方法,创建Looper对象,绑定与ThreadLocal的关系,在Looper对象的构造里创建MessageQueue对象
-
应用里使用mHandler的sendMessage...方法,将当前的handler传入message中,执行mQueue.enqueueMessage(msg)方法,插入到消息队列中
-
Looper.loop()无限循环调用MessageQueue.next()方法获取message,如果获取到就传递给 Handler 的 dispatchMessage 方法,如果没有消息就堵塞住。
网友评论