《Android开发艺术探索》第10章的笔记~
1、简介
- Android规定访问UI只能在主线程,否则程序会抛出异常。ViewRootImpl的checkThread对UI的操作的线程进行了验证,同时又规定了耗时操作必须在子线程完成,否则会导致程序无法响应即ANR。因此系统提供了Handler进行对UI的操作。
- Handler创建时会采用当前线程的Looper构建消息循环系统,在无Looper的线程,则需要为当前线程创建Looper。
- Handler创建后,通过post或send方法将Runnable放到Handler内部的Looper中去处理。当send方法被调用时,他会调用MessageQueue的enqueueMessage将消息放入消息队列,然后循环并处理新消息。
2、消息机制分析
2.1、ThreadLocal工作原理
ThreadLocal是一个线程内部的数据存储类,在指定线程中存储数据,同时存储的数据只能从指定线程中访问,从其他线程中无法访问。对于Handler来说,需要获取当前线程的Looper,这个时候在不同的线程则需要不同的Looper,使用ThreadLocal则可以实现Looper在线程中的存取。
实例
- 定义一个Boolean类型的ThreadLocal
private ThreadLocal<Boolean> mBooleanThreadLocal = new ThreadLocal<Boolean>();
mBooleanThreadLocal.set(true);
new Thread("Thread1"){
@Override
public void run(){
mBooleanThreadLocal.set(false);
mBooleanThreadLocal.get();
};
}.start();
new Thread("Thread2"){
@Override
public void run(){
mBooleanThreadLocal.get();
};
}.start();
在主线程设置mBooleanThreadLocal的值为true,在子线程1中设置为false,在子线程2中不设置,后通过get方法获取,在主线程中为true,子线程1为false,子线程2为null。
即在不同线程访问同一个ThreradLocal对象,会得到不同的结果。在不同的线程访问同一个ThreadLocal,ThreadLocsl会从各自的线程取出一个数组,然后根据当前的ThreadLocal的索引去数组中查找对应的value值。不同的线程数组是不同的。
- ThreadLocal的set方法
public void set(T value){
Thread currentThread=Thread.currentThread();
Values values=values(currentThread);
if(values==null){
values=initializeValues(cuttentThread);
}
values.put(this,value);
}
在Thread内部有一个成员(ThreadLocal.Values localValues)专门用于储存线程的ThreadLocal数据,因此在set方法中,通过values方法获取当前线程的ThreadLocal数据时,如果localValue的值为null,则需要初始化,初始化完成后在进行储存。
- localValues的存储
void put(ThreadLocal<?> key, Object value){
cleanUp();
int firstTombstone=-1;
for(int index=key.hash & mark;;index=next(index)){
Object k=table[index];
if(k==key.reference){
table[index+1]=value;
return;
}
if(k==null){
if(firstTombstone==-1){
table[index]=key.reference;
table[index+1]=value;
size++;
return;
}
table[firstTombstone]=key.reference;
table[firstTombstone+1]=value;
tombstones--;
size++;
return;
}
if(firstTombstone==-1 && k==TOMBSTONE){
firstTombstone=index;
}
}
}\
以上实现了数据的存储过程,ThreadLocal的值在table数组中的位置在ThreadLocal的reference所表示的对象的下一个位置。
- ThreadLocal的get方法
public T get(){
Thread currentThread = Thread.currentThread();
values values=values(currentThread);
if(values!=null){
Object[] table=values.table;
int index=hash&values.mask;
if(this.reference==table[index]){
return (T)table[index+1];
}
}
else{
values=initializeValues(currentThread);
}
return (T)values.getAfterMiss(this);
}
get方法为取出当前线程的localValues对象,如果对象为null则返回初始值(初始值morenqingkuangxiaweinull)。如果对象不为null,取出table数组并且找出ThreadLocal的reference对象在数组中的位置,然后获取下一个值,即为ThreadLocal的值。
2.2、消息队列
消息队列即指MessageQueue,MessageQueue包含插入和读取两个操作。同时在读取的时候回进行删除。插入为enqueueMessage,作用是往消息队列中插入一条消息,读取为next,读取队列中的消息并且删除。MessageQueue内部使用的是单链表,便于插入和删除。
- enqueueMessage
boolean enqueueMessage(Message msg,long when){
...
synchroized(this){
...
msg.markInUse();
msg.when=when;
Message p=mMessage;
boolean needWake;
if(p==null || when==0 || when<p.when){
msg.next=p;
mMessage=msg;
needWake=mBlocked;
}
else{
needWake=mBlocked && p.target==nulol && msg.isAsynchronous();
Message prev;
for(;;){
prev=p;
p=p.next;
if(p=null || when<p.when){
break;
}
if(needWake && p.isAsynchronous()){
needWake=false;
}
}
msg.next=p;
prev.next=msg;
}
if(needWake){
nati9veWake(mPtr);
}
}
return true;
}
enqueueMessage主要为单链表的插入操作。
- next方法
Message next(){
...
int pendingIdleHandlerCount=-1;
int nextPollTimeoutMillis!=0;
for(;;){
if(nextPollTimeoutMillis!=0){
Binder.flushPendingCommands();
}
nativePollOnce(ptr,nextPollTimeoutMillis);
synchronized(this){
final long now=SystemClock.uptimeMillis();
Message prevMsg=null;
Message msg=mMessage;
if(msg!=null && msg.target==null){
do{
prevMsg=msg;
msg=msg.next;
}while(msg != null && !msg.isAsynchronous());
}
if(msg!=null){
if(now < msg.when){
nextPollTimeoutMillis=(int)Math.min(msg.when-now,Integer.MAX_VALUE);
}
else{
mBlocked=false;
if(mPrevMsg!=null){
prevMsg.next==msg.next;
}
else{
mMessage=msg.next;
}
msg.next=null;
if(false) log.v("MessageQueue", "Returning message:"+msg);
return msg;
}
}
else{
nextPollTimeoutMillis=-1;
}
...
}
...
}
}
next方法是无限循环的,当消息队列中没有相应的消息,next方法会阻塞在这里,当有新消息时,next方法会返回新消息并删除这条消息。
2.3、Looper
Looper会不断从MessageQueue中查看有无新消息,有则立刻处理,没有就会堵塞在那里。在构造方法中会创建MessageQueue,然后保存当前线程的对象。
private Looper(boolean quitAllowed){
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Looper.prepare()会为当前线程创建一个Looper,然后通过Looper开启消息循环。
new Thread(){
@Override
public void run(){
Looper.prepare();
Handler handler=new Handler();
Looper.loop();
};
}.start();
同时还有prepareMainLooper方法,这个方法用于给主线程创建Looper,本质也是通过prepare方法实现。
Looper同时提供了quit和quitSafely方法来推出一个Looper,其中quit会直接退出Looper,而quitSafely会把消息队列中的消息处理完后退出。在Looper退出后,Handler发送消息就会失败。
- 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;
Binder.clearCallingIdentity();
final long ident=Binder.clearCallingIdentity();
for(;;){
Message msg=queue.next();
if(msg==null){
return;
}
Printer logging=me.mLogging;
if(logging!=null){
logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ":" + msg.what);
}
msg.target.dispatchMessage(msg);
if(logging != null){
logging.println(" <<<<< Finished to " + msg.target + "" + msg.callback);
}
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();
}
}
loop是一个死循环,只有当MessageQueue的next方法返回了null,才会跳出循环。当Looper的quit或quitSafely方法被调用,就会将消息队列标记退出,此时next就会返回null。loop方法调用MessageQueue的next方法获得新消息。
2.3、Handler
Handler的工作包含了消息的发送和接收,使用post或send发送消息。post的一系列方法是通过send来实现的。
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg,0);
}
public final boolean sendMessage(Message msg,long delayMillis){
if(delayMillis<0){
delayMillis=0;
}
return sendMessageAtTime(msg,SystemClock.uptimeMillis()+delayMillis);
}
public final boolean sendMessageAtTime(Message msg,long uptimeMillis){
MessageQueue queue=mQueue;
if(queue==null){
RuntimeExecption e=new RuntimeExecption(
this+"sendMessageAtTime() called with no mQueue");
log.w("Looper", e.getMessage(),e);
return false;
}
return enqueueMessage(queue,msg,uptimeMills);
}
public final boolean enqueueMessage(MessageQueue queue,Message msg,long uptimeMillis){
msg.target=this;
if(mAsynchronous){
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg,uptimeMills);
}
Handler发送消息是向消息队列中插入了一条消息,MessageQueue的next方法就会返回消息给Looper,Looper收到消息后就开始处理,最终消息交给handler处理,即调用Handler的dispatchMessage方法。
- dispatchMessage
public void dispatchMessage(Message msg){
if(msg.callback!=null){
handleCallback(msg);
}
else{
if(mCallback!=null){
if(mCallback.handleMessage(msg)){
return ;
}
}
handleMessage(msg);
}
}
dispachMessage首先会检查callback是否为null,不为null就直接通过handleMessage处理消息,否则判断mCallback是否为null,不为null则调用mCallback的handleMessage方法,最后调用Handler的handleMessage方法。
- handleCallback
private static void handleCallback(Message message){
message.callback.run();
}
- Callback
public interface Callback{
public boolean handleMessage(Message msg);
}
3、主线程的消息循环
Android的主线程就是ActivityThread,主线程的入口方法为main,在main方法中系统会通过Looper.prepareMainLooper()来创建主线程的Looper以及MessageQueue,并通过Looper.loop()来开启主线程消息循环。
public static void main(String[] args){
...
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread=new ActivityThread();
thread.attach(false);
if(sMainThreadHandler==null){
sMainThreadHandler=thread.getHandler();
}
AsyncTask.init();
if(false){
Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG,"ActivityThread"));
}
Looper.loop();
throw new RuntimeExeception("Main thread loop unexpectedly exited");
}
主线程消息循环开始后,ActivityThread还需要Handler和消息队列进行交互,此时的Handler就是ActivityThread.H。其内部定义了一组消息类型,包含了四大组件的启动和停止等过程。
ActivityThread通过ApplicationThread和AMS进行进程间通信,AMS以进程间通信的方式完成ActivityThread请求后回调ApplicationThread中的Binder方法,然后ApplicationThread会向H发送消息,H收到消息后会将ApplicationThread中的逻辑切换到ActivitytThread中去执行。
参考来源:
《Android开发艺术探索》