Handler
一套Android的消息传递机制/异步通信机制。在多线程的应用场景中,将工作线程中需要更新UI的操作信息传递到UI主线程,从而实现工作线程对UI的更新处理,最终实现异步消息的处理。
- 消息
Message,是线程间通讯的数据单元(即Hnadler接受&处理的消息对象)。存储需要操作的通信信息。
- 消息队列
MessageQueue,一种数据结构(存储特点:先进先出)。存储Handler发送过来的消息(Message)。
- 处理者
Handler,是主线程与子线程的通信媒介,也是线程消息的主要处理者。添加消息(Message)到消息队列、处理循环器(Looper)分派过来的消息(Message)。
- 循环器
Looper,是消息队列(MessageQueue)与处理者(Handler)的通信媒介。消息循环即(循环取出消息队列的消息)、消息分发(将取出的消息发送给对应的处理者)
- 系统为什么不允许在子线程中去访问UI
这是因为Android的UI线程是不安全的,多线程并发访问可能导致UI控件处于不可预期的状态。
- 为什么不加锁呢
缺点有两个:首先加上锁机制会让UI控件的访问逻辑变得复杂,其次锁机制会降低UI的访问效率,锁会堵塞默写线程的执行。
Handler创建时会采用当前线程的Looper来构建内部消息循环系统,如果当前线程没有Looper就会报错。在当前线程创建一个Looper,或者在有Looper的线程中创建Handler。
handler.pngHandler通过post方法将一个Runnable投递到Handler内部中的Looper中去处理,也可以通过Handler的send方法发送一个消息,这个消息同样会在Looper中处理。(其实post方法最终也是由send方法完成的)。
当Handler的send方法被调用时,它会调用MessageQueue的enqueueMessage方法将消息放入消息队列中,然后Looper发现有新消息到来时就会处理这个消息,最终消息中的Runnable或者Handler的handMessage方法就会被调用。
注意Looper是运行在创建Handler的线程中的,这样一来Handler中的业务逻辑就被切换到创建Handler的线程中去了。
ThreadLocal的工作原理
ThreadLocal是一个线程内部的数据存储类,通过它可以在指定线程中存储数据,数据存储之后,值可以在指定的线程中获取到存储数据,其他线程中获取不到。
Looper、ActivityThread以及AMS中都用到了ThreadLocal。
Looper另一个使用场景是复杂逻辑下的对象传递,比如监听器的传递。它可以让监听器在线程内作为一个全局对象的存。
- 不同线程访问同一个ThreadLocal的get方法,ThreadLocal内部会从各自线程中取出一个数组,然后再从数组中根据当前ThreadLocal的索引去查找对应的value值。
- ThreadLocal的set方法
public void set(T value){
Thread currentThread = Thread.currentThread();
Values value = values(currentThread);
if(values == null){
values = initalizeValues(currentThread);
}
values.put(this,value);
}
首先通过values方法去获取当前线程中ThreadLocal中的数据。在Thread类的内部有一个成员专门用于存储ThreadLocal中的数据:ThreadValues.localValues。在localValues内部有一个table数组:private Object[] table,ThreadLocal的值就是存在在这个table数组中的。
ThreadLocal的值在table数组的存储位置总是ThreadLocal的reference字段所标识的对象的下一个位置,比如ThreadLocal的reference对象在table数据中的索引是index,那么ThreadLocal的值在table数组中的索引就是index+1,table[index+1]=value。
- ThreadLocal的set方法
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 = initalizeValues(currentThread);
}
return (T)values.getAfterMiss(this);
}
从ThreadLocal的set/get方法中可以看出,他们操作的对象都是当前线程中localValues的table数组,因此不同线程访问同一个ThreadLocal的set和get方法,他们对ThreadLocal所做的读写/写入操作仅限于各自线程内部。这就是为什么ThreadLocal可以在多个线程中互补干扰的存储和获取数据。
MessageQueue的工作原理
MessageQueuq虽然叫做消息队列,但实际是通过一个单链表结构来维护的消息队列,单链表在插入和删除上有优势
- enqueueMessage(插入操作)
就是单链表的插入操作
- next(读取消息,并从消息队列中删除)
next方法是一个无线循环的方法,如果消息队列中没有消息,next方法就会一直堵塞在这里,当有新消息是,next方法会返回这条消息并将从单链表中移除。
Looper的工作原理
Looper在Android消息机制中扮演着消息循环的角色,具体来说就是它会不停地从MessageQueue中查看是否有新消息,如果有新消息就会立即处理,否则会一直堵塞在那里。
- 构造方法
private Looper(boolean quitAllowed){
//创建消息队列
mQueue = new MessageQueue(quitAllowed);
//保存当前线程对象
mThread = Thread.currentThread();
}
- Looper的创建
new Thread("Thread#2"){
@Override
public void run(){
Looper.prepare();
Handler handler = new Handler();
Looper.loop();//开启消息循环
};
}.start();
Looper除了prepare方法外,还提供prepareMainLooper方法,这个方法主要是给主线程也就数ActivityThread创建Looper使用,其本质也是通过prepare实现的。
由于主线程Looper比较特殊,所以Looper提供一个getMainLooper方法,通过它可以在任何地方获取主线程的Looper
- Looper的退出
quit直接退出Looper
quitSafely设定一个退出标记,然后把消息队列中已有消息处理完后安全退出。
Looper退出后通过Handler发送消息就是失败,这个时候Handler的send方法返回就是false。
在子线程中,如果手动创建了Looper,那么在所有的事情处理完成后应立即调用quit方法来终止消息循环,否则在这个子线程就会一直处于等待的状态。
- Looper中的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.getCalss().getName()+" "
+msg.callback+" what="+msg.what);
}
msg.recycleUnchecked();
}
}
Hnadler的工作原理
Hnadler的工作主要包含消息的发送和接收过程。通过post或send来实现。post最终也是send实现的。
- send方法
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg,0):
}
public final boolean sendMessageDelayed(Message msg,long delayMillis){
if(delayMillis<0){
delayMillis=0;
}
return sendMessageAtTime(msg,SystemClock.uptimeMillis()+delayMillis);
}
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;
if(mAsynchronous){
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg,uptimeMillis);
}
可以发现Handler发送消息过程仅仅是向消息队列中插入一条消息,MessageQueue的next方法就会返回一条消息给Looper,Looper收到消息后开始处理,最终消息由Looper交由Handler处理,即Handler的dispatchMessage方法被调用,这是Handler进入消息处理阶段。
- dispatchMessage方法
public void dispatchMessage(Message msg){
if(msg.callback!=null){
handlerCallback(msg);
}else{
if(mCallback!=null){
if(mCallback.handleMessage(msg)){
return;
}
}
handleMessage(msg);
}
}
主线程的消息循环
Android的主线程就是ActivityThread,主线程的入口方法是main,在main方法中系统会通过Looper.prepareMainLooper()方法来创建主线程的Looper和MessageQueue,并通过Looper.loop()来开启主线程的消息循环。
- main方法
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();
throws new RuntimeException("Main thread loop unexpectedly exited");
}
主线程消息循环开启后,ActivityThread还需要一个Handler来和消息队列进行交互,这个Handler就是ActivityThread.H,它内部定义了一组消息模型,主要包含四大组件的启动和停止过程。
ActivityThread通过applicationThread和AMS进行进程间通信,AMS以进程间通信的方式完成ActivityThread的请求后会回调ApplicationThread中Binder方法,然后ApplicationThread会向H发送消息,H收到消息后会将ApplicationThread中的逻辑切换到ActivityThread中执行,即切换到主线程中去执行,这个过程就是主线程中的消息模型。
网友评论