Handler是音讯机制的上层接口,开发中基本只用和Handler交互即可。Handler能够将一个使命切换到Handler指定的线程中履行。如在用Handler在子线程更新UI。

Android音讯机制主要便是Handler的运行机制。HandlJ G L u 4er的i w j % ! ^ +运行还依赖MessageQueue、Looper,及Looper内部运用到的ThreadLocal。
MessageQueue是音讯行列,用于寄存Handler发送的音讯,实践是单链表的结构。
Looper会在音讯行列中无限循环的查找音讯,有音讯就取出v K s Q n,没有就等待。
ThreadLocal本质效果是在每个线程中存储数据。在Looper中的效果便是给每个线程存3 H – ~Looper实例。由于咱们知道,创立Hak # ^ q Xndler时是需求线程的Loo5 } q 8per实例的,而非UI线程默许是没有Looper的。

一、Handler运用与概述

1Y f p 6 6 _ # ?.1运用步骤` 9 D t j O l

  1. 在使命履行的线程,运用Looper.prepare()来给线程创立Looper实例。
  2. 在使命| D B x m Z = – A履行 ; ^ ; R ~的线程,创立Handler实例。
  3. 在使命履行的线程,运用LoW N ( / [ Q @oper.loop()敞开音讯循环。
  4. 使命发出的线程,运用Handler实例发送音讯。

举个比如
. L ` S x ^ – = ?下所示,点击按钮,在主线程发送音讯,就会在子线程履行。
(这个比如为了完好展现运用步骤,所以在子线程创立了handler,在主线程发送和H @ 4 b B i { f音讯。通常实践咱们运用是在主线程创立handler,在子线程发送音讯然后再主线程履行UI的更新,而主线程默许是有Looper并敞开的,所以一般不需求第一步和第三部。)

    @Overri Y ) % Q ]ide
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanz 3 b A ` A XceState);
setContentView(R.layout.activity_main);
testHandler();
}
private void testHan6 n O U j *dler() {
new Thread(new Runnable() {
@Override
public void run() {
//1、预备looper,即threadLocal<Looper>.set(newa 7 $ ~ Y B @ U + Looper())
Looper.prepary K z r +  ) 7 .e();
//2、创立handler实例
// 这个重写了B i E W nhandleMeso V N % M % ~sage,handlerC J ! r是属于HandL _ ^  q S & 8ler的子类的实例
mHandler = new Handler() {
@Override
public_ : { ! ] void handle p i X x Y NMessage(Message msg) {
super.h; @ 8 A I j P S ,andleMessage(msg);
Log.i(TAG, "child t/ r D / @ b h M ^hreaU T t ( w 3 n od, handleMessage: what="+0 z :msg.what);
}
};
//3、looper启动,sThreadLocal.get()拿到looper,拿到queue,开` : 2 % h Y端queue.next
Looper.loop();
}
}).start();
}
pubB ^ r t , q z  Ulic void onCle f j C ^ ( z Fick(){
//4.2、handler.s/ H X dendMessage发送音讯,queue.enqueueMessage(msg),即音讯入行列。
Log.i(TAG, "main thread, s _ 3 D h 3endMessage");
Message message = Message.obtain();
message.what = 100;
mHandler.sendMessage(message);
}

1.2Handler的运用布景

Handler能够将子线程中更新UI的使命4 = 9切换到主线程。为什么要切换呢?咱们知道,UI的拜访只能在主E 7 f R线! v R 8 = F程进行5 m t # : X C o子线程拜访UI就会出现反常,由于在ViewRootImpl中对线程做了校e V ( U W S * n W验,只有创立了这个View树的线程,才干拜访这个view。 一般状况创立View的线程便是主线程,即UH # w m { G NI线程,所以子线程拜访会} B % s – @反常。

    void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a viewM * J N ( H v hierarchy can touch its views.");
}
}

并且,UI线程一般不能做耗时操作,不然会发c ~ 2 I _ Z生ANR。: E s 9 3所以当 在子线程做完耗时操作后 又需求更新UI,这时就需求用到Handler了。那为啥一定要用chec^ l _ 4 0 9 dkThread()保证不让E i 7 + u L h +子线程拜访UI呢? 由于UI控件不是线程安全的。那为啥不加锁呢?一j $ ) l , }是加锁会让UI拜访变得复杂;二是加锁会降低UI拜访功率,会堵塞一些线程拜 . = X j q 5 % –访UI。所以爽性运用单线程模型处理UI操作,运用时用Handler切换即可。

y ; 0 8 N ; $ M ?、Android音讯r a v ) :机制剖析

前面说了,Android音讯机制包括几个概念:p X Q /Handler、MessageQueue、Looper、Looper内部运用到的ThreadLocal。下面详细介绍下。

2.1 ThreadLocal

外界想要在不同thread中存值,就能够threadLocal = new ThreadLocal,然后在不同线程S j = : L X &中threadLocal.set(val; B G zue)就能够了,获取值用threadLocalH : = g.get(D h . , 4 ! G R ) 。

举个比如,下面比如中 先只看booleanThreadLocal,在主线程设置true,a线程^ a m Z = z m 2 C设置false,b线程设置null,然后每个线程都打印 booleanThreadLocal.get()的成果,发现每个线程get的值是不同的,是在每个线程中set的值。这便是神奇之处,同样的booleanThreadLocK U Q 2 Z Qal.get(),地点线程不同,成果就不同。

     	ThreadLocal<Boolean&g: 7 4 Q 1 2 D I Pt; booleanThreadLocal = new ThreadLocal<>();
ThreadLocal<Integer> integerThreadLocal = new ThreadLocal<>();
boole} B p p  R M z 1anThreadLocal.set(true);
integerThreadLocal.set(0);
Log.i(T[ P wAG, "testThread0 N H ) 9 fLocaT & ; U 4 j 6 Fl: main thread, boolean= "+boolean* r HThreadLocal.get());
Log.i(TAG, "testThreadLocal: main thread, int = "+integerThreadv ? w F V q X U tLocal.get());
new Thread(new Runnable() {
@Override
public void run() {
booleanThreadLocal.set(y 5 8 vfalse);
integV m # @ ? &erThreadLocal.set(1);
Log.i(b [ ] Z Q y W G nTAG, "testTp # N ? { I ghreadLocal: a thread, boolean="+booleanThreadLocal.get());
Log.i(TAG, "testThreadLocal: a tP N s u V ? # [ @hrj % s m Head, int = "+integerThreadLocal.get());
}
}).start();
new Thread(new Runnable() {
@Override
public void ruM R z y d x v v Kn() {
b4 = z t q E .ooleF ! # eanB B DThreadLocal.set(null);
integerThreadLocal.set(2);
Log, j ) U Z L V.i(TAG, "testThreadLocal: b thread, boolean="+booleanThreadLocal.get());
Log.i(TAG, "testThreadLocal: b thread, int = "+integerThreadLocal.get());
}
}).start();

成果:

2020-01-08 10:15:38.623 8976-8976/com.hfy.demo01 I/hfy: testThreadLocal: main thread, boolean= true
202e i V g 90-01-08 10:15:38.623 8976-8976/com.hfy.demo01 I/hfy: testThreadLocal: main thread, int = 0
2020-01-08 10:15:38.624 8976-9226/com.hfy.demo01 I/hfy: testThq C h Zrp } c `eadLocal: a thread, boolean=false
2020-01-08 10:15:38.624Z j ` * ? g 8976-9226[ 4 V @ 2 0/com.hfy.demo01 I/hfy: testThreadLocal: a thread, int = 1
2020-01-i Y D 2 (08 10:15:38.626 8976-9227/co@ s , g ` r H # qm.hfy.demo01 Ik 4 ! f/hfy: testThreadLocal: b thread, boolean=null
2020-01-08 10:15:38.626 8976-9227/com.hfy.demo01 I/hfy: testThreadLocal: b thread, int = 2

下面看下ThreadLocal的get()、set()办法。

    /**
* Returns the value in the current thread's copy of this
* thread-local variu C I #  j e `able.  If the variable has no value for the
* current| - R I F * 4 I thread, it isk f 5 firstv 0 } W initialized to the valb ^ e n e H Q yue returned
* by an invocation of the {@link #initialValue} method.
*^ ( d 1 G _
* @return the current thv } Wread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThreaM W d N 2 - ; wd();
ThreadLocalMap m- h _ V = a z /ap = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = m { Lap! ` V p 6 Y.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked" # @ A l D p)
T result = (T)e.value;
return result;
}
}
return setInitiali ; 5 . ,Value();
}
/**
* Variant of set(O r Q ? O) to establish initialValue. Used instead
* of set(9 & p Y 1 ] $ ^ |) in case user has overridden the set() method.
*
* @return the initial value
*/
private T setInitialValue() {
T value = initialValue();
Thr + F - 0  kead t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMaW R * -p(t, value);
return valu5 * @ =e;
}

get():获取当时线程的Threa} j | E B S KdLocf 6 PalMap,这儿能够先了解成普通 键值对的Map。然后传入threadLocal实例,获 . `取键值对Entry,然后| N O h获取Entry的value。如果map为空或value为空则会初始化map、value。

    /**
* Sets the current thread's copy of this thread-local variable
* to t) 8 K 9he sR h G e K i vpecified value.  Most subclasses will have no need to
* override this method, relying solely on the {@link #initialc ^ , p / ` c 7 eValue}
* method to set the values of thread-6 / 0localsb Q . } =.
*
* @param value the value to be stored in the current thread's copy of
*        this thread-local.
*/
public void set(T value) {
Thread t = Thread.cO ? u % 5 0 IurrentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
/**
* Create the map associated with a ThreadLocal. Overridd6 D c - v Cen in
* InheritableThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the map
*/
void createMap(Thren C Q o S f v Mad t, T firstVz A T j h X Y = dalue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}

se, P @t()中也是获取当时线程的ThreadLocalMau j q – – D T Lp,然后ThreadLocal实! . w ( a W例作为key, 和value一起设置给map。没有map就去创立并把value初始化进去。

咱们再去看下Thread,有个默许为空的ThreadLocalMapd i U }实例threadLocals。

    /* ThreadLocal values pert: R p R w O I kai { + lning to this thread. Thih O D c x O 7 } Qs map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

那ThreadLox u –calMap是啥呢?ThreadLocalMapQ G s 0 u A x s是ThreadLocal的内部类,效果相似Map,内部有个Entry[]的特点tabC r b s T g Kle。所以上面看的get、set办法便是对ThreadLocalMap, 7 1的Entry[]取和存
。下面详细看下。

/**
* Construct a new map initially containiJ } V r ] Ung (firstKey, firstValue).
* ThreadLocalMaps are& c 4 i ; X constructed la L A Nazily, so we only create
* one when we have at least on7 x W a j ~ `e entry to put in it.
*/
ThreadLocalMap(ThreadLocal<?> firT r L @ 3 k Z astKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_C_ D } W f U P Z HAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;6 w & y j X
setThreshold(INITIAL_c 8 b P , `CAPACITY);
}
/**
* GetD O v y # 8 T V 1 the entry associated with key.  This method
* itself handles only the fast path: a direct hit of existing
* key. It otherwise relays to getEntryAfterMiss.  This is
* designed to maximize performance for direct hits, in part
* by making this method readily inlinable.
*
* @param4 K w P O . V  key the thread local object
* @return the entry associated with key, or null if no such
*/
private Entry getEntry(T@ } . . C phreadLocal<?> key) {
int i = key.threadLocalHashCode & (tabl[ U V 3 5 ] n e.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return gK T c B x { ]etEntryAfterMiss(key, i, e);
}
/**
* Set the value associated with km B h ey.
*
* @param key the thread local object
* @param value the value to be set
*/
private void set(ThreadLocal<?> key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashK . P - # b &Code & (len-1);
foi J w ) X ! ^ ( Vr (Entry e = tab[i];
e != null;
e = tab[i = nextI0 Y x ] ` 5 J V ]ndex(i, len)]) {4 R M ! = 4 ? V
ThreadLocal<?> k = e.get();
if (k == key) {
e.value =^ l c value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;[ Q e F ) O O
}
}
tab[i] = n1 [ u q $ew Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots1 X & Z { N X e R(i, sz) && sz >= threshold)
rehash()6 } m a ~ % ( q;
}

运用Entry[] 存多个threadLocal-value键值对,数组下标index与是ThreadLocal 实例的hashCode相关。而ThreadLocalMap仅有实例是createMap(Thread t, T firstValue)赋给Thread的变量threadLocals。
例如 线程A thrL E S ) k s ] R 1eadLocalMap的table[] 能够存储 int、Sq O xtring、boolean类型的3个键值对threadLocal-int, threadLocal-Stringl M m ; H U :、threadLocal-B{ u K d [ p / :oolean。仍是上面的比如。

(惯例的HashMap的键值得类型是固定的;threadLocalMap的key是ThreadLocal,value是T,即能够存多种类型的value)

     	ThreadLocal<Boolean; [ { H 8 Q 4 E j> booleanThreadLocal = new Thz z k I = L DreadLocal<>();
ThreadLocal<Integer> integerThreadLocal = new Td # V ! ` 5 0hreadLocal<>();
booleanThreadLocal.set(true);
integerThreadLocal.set(0);
Log.i(TAG, "testThreadLocal: main thread, boolean= "+booleanThreadLocal.get());& [ u , p $
Log.i(T6 M I  P  V ! 8AG, "testThreadLocal: main thread, int = "+integerThreadLocal.get());
new Thread(new Runnable() {
@OverridR z  7e
public void run() {
booleanTy T YhreadLocalb ` K y & V.seZ M n | C zt(false);
iq ! % G 1 { X n tntegerThreadLocal.set(1~ z k : $ P L);
Log.i(TAG, "testThreadLocal: a thread, boolean="+booleanz y = r u ]T; n o Q )hreadLocal.get());
Log.i(TAG, "testTj x 8 : r 8 hreadLocal: a thread, int = @ g 1"+integerThrU p ` o U a 5 oeadLocal.get());
}o z 6 w
}).start();
ne: m G B Z [w Thread(new Runnable() {
@Override
publd % Tic void run() {
booleanThreadLocal.sZ  7 t U G o { yet(null);
integerThreadLocal.set(2);
Log.i(TAG, "testThreadLocal: b threadI / = :, boolean="+booleanThreadLocal.get());} N P p x
Log.i(TAG, "testThreadLocal: b thread, int = "+integerThreadLocal.get());
}
}).start();

成果:

2020-01-08 10:15:38.623 8976-8976/com.hT e [ % 1 s b  Dfy.demo01 I/hfy: tE U + C y y 3estThreaK e z i ^dLocal: m; P / 9ain thread, boolean= true
2020-01-08 10:n X %15:38.623 8976-8976/com.hfy.demo01 I/hfy: testThreadLocal: main thread, int = 0
2020-01-08 10:8 @ j T / [ = }15:38.624 8976-9226/com.hfy.demo01 I/hfy: testThreadLocal: a thread, boolean=false
2020-01-08 10:15:38.624 8976-9226/com.hfy.demo01 I/hfy:& ? U , v N @ 5 testThreadLocal: a thread, int = 1
2020-01-08 10:15:38.626 8976-9227/com.hfy.demo01 I/hfyz r p ! q x 8: testThreadLocal: b thread, boolean=null
2020-01-08 1! * a x Q s0:1R Y _5:0 . z / E38.626 8976-9227/com.hfy.demo01 I/hfy: testThreadLocal: b thread, int = 2

到目前为止咱们知道,ThreadLocal的效果,便是操作线程内部的threadLocals,存和L m k Q ~取valu_ v F {e。value的实践类型便是 实例化ThreadLocal时界说的泛型T。

2.2 messageQueue

messageQueue,音讯行列,实践是单向链表。看下存、取音讯。

enqueueMessage(),存音讯,单链表的刺进。

    boolean enqueueMessage(Message msg( b L P, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have aL ; B c target.");
}
if (msg.isIn[ x ! 5 N yUse()) {
throw new Ille% W 5 p q T A YgR X N 6 $ u m naY M t IlStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
Illeg#  . @ M w W [ FalStateException e = new IllegalStateException(| @ U O
msg.target + " sending message to a Handler on a dead thread");
Logv 6 U 7 m ^ $ e.w(TAG, e.geB _ ktMessaw - K 7 g L Xge(), e);
msO o - + $ Qg.recycle();
return false;
}
m= u Q = | &sg.markInUse();
msg.when = when;
Message p = mMessages;
booQ A s G $lean needWake;
if (p =T r p 3 z % / b n= null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within t- j ( R ehe middle of the queue.  Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asyn# n D { } Y Qchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
iP a X 7 u %f (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}

next():取一条音讯,没有音讯就无限循环,会堵塞。

    Message next() {
//...
//有msg就return,没有音讯就无限循环,会堵塞。如quit,return null。
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis)3 p F 3 };
synchronized (this) {
// Try to retrieve the next message.  Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target ==N 6 ] s null)@ [ p m 5 z {
// Stalled by a barrier.  Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {y r ! ( 8 R
if (now < msg.when) {
// Next* l k _ message is not ready.  Set a timeout to wake up when it is ready.[ N f
nextPollTimeout& 1 [ a P A nMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
/u 7 P : f/ Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next_ 5 h w w @;
} else {
mMessages = msg.next;
}
msg.next = nul2 L $ Hl;
if (DEBUG) Log.v(TAG, "Returning message:g m 5 $ = U A I " + msg);
msg.markIN j i % 9 @ D PnUse();
//有音讯就return
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit mU  ( ~ ~ W G ) iessage now that all pending messages have been hQ - 3andledX D k ) D.
if (mQuitting) {
dispose();
//q9 ~ @ Ouit后5 @ B回来null
return null;
}
// .k ~ N o &..
}

2 h X 4 ( T c.3 Looper

looper,音讯循环器。

先看静态, v – 0办法prepare():


// sThreadLocal.get() will ret[ = ) T t v | 7 Iurn null unless you've called prepare().
static final ThreadLocal<Ls 6 H 5 u 8 ,ooper> sThreadLocal = new ThreadLocal&l~ N L c 1 5 *t;Looper>();
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@linF D * R A s Jk #loopy . , T @ 6 8 X()} after calling t_ k @ ^his method, and end it bV 3 ) Vy calling
* {@link #quit()}.
*/
public static void prepare() {
prepare(true);
}
prO j u u ! ? e /ivate static void6 w Z  prepare(booleax U P 0n qu, S ) V ! C s vitAllowed) {
if (sThreadLocal.g! i A F :et() != null) {
throw new RuntimeE# e 3 , q @ 7xception("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAlloc E N @ Z N 9 ` Ewed));
}
/9 X [ x a**
* Initialize the current threa, ^ v @ M 9 Od as a looper, marking it as an
* ap2 j o cplication'sI _ 9 F d z main looperE e M f ( [  g. The main looper for your applica[ } d Y k E g o &tion
* is crea3 v u X v P X ~ted by1 - = | # ; s the Android enviri A : B monment, so yoq X J - 5u should never need
* to call this function yo @ 1ourself.  See aj { d V + ; )lso: {@link #prepare()}
*/
public st@ ] yatic void prepareMainLoA 8 o ~oper() {
prepare(false);
syB A A 9 ) 4 @nchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateExcf f ` % @ m S $eption("The main Looper has alrea{  X } * 8dy been prepared.");
}
sMainLooper = myLooper();
}
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowe% l O y g Kd);
mThread = Thread.currentThread();
}

可见s8 W F d B + #ThreadLocal是个静态常量,value类型是Looper。
prepare()办法调sThreadLocal.set(new Looper),创{ C p Y I 立looper实例,设置给当时线程ThreadLa T L % ] | ?ocalMap特点中的table[i](i是threadLocal实例的hashCode相关)。

且创立loopJ 4 ^ c E der实例时默许创立了对应的音讯行列mQueue实例。另外,prepareMainLooper()是主线程,是给主线程创立looper实例。

再看下获取looper实例、queue实例的办法:

    /**
* Returns the application's main looper, which lives in the m{ ! k T T m , q Oain thread of the application.
*/
public static Looper getMainLooperB $ 4 P r 9 . Y +() {
synchr6 d 1 ) yonized (Looper.class) {
return sMainLooper;
}
}
/**
* Return the Looper object associated with the current thread.  Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLoc7 w ! j W = q ] Eal.get(f ~ R .);
}
/**
* Return the {@link MessageQueue} object associated with the current
* thread.  This must be called from a thread running a Looper, or a
* NullPointerException will be throw) Q - Sn.
*/
pul = - Jblic? i g v static @NonNull MessageQueue myQueue() {
return myLooper().mQ? N K 2ueue;
}

myLooper() 办法,调用sThreadLocal.get()。便是上面讲解的ThreadLocal的运用办法。经过静态j n T 6 I W .常量sThreadLocal获取对应每个线程的Looper实例。

looper的quit,两种,当即退出,履行完音讯再退出。

    /**
* Quits the looper.
* <p>
* Causes the {@link #loop} method to terminate without processing any
* more messages in the message queue.Z d m u i a f e
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link HandlF 2 = { 1er#sendM9 B P = R j yessage(Me&  ~ g + ` F kssage)} method will return false.
* </p><p class="note">
* Using this method may be unsafe because some messages may not be delivered
* before th; h @e looper terminates.  Consider using {@link #quitSafely} instead to ensure
* that all pending work isb v # ; k t _ completed in an orderly manner.
* </p>
*
* @s, n E g T u y ?ee #quitSafel= n % ` sy
*/
public void quit() {
mQueue.quit(fau z ? 8 Ylsl } o h ~ s = /e);
}
/**
* Quits the looper saf2 y m - A )el] ; J T ;y.
* <p>
* Causes the {@link #loop} method to terminate as soon as all rei ] : P 1 - Emaining messages
* iZ P c r v X J } ln th! 9 1 9 Ke message? R c e queue that are already due to be delivered have been handle^ } dd.
* However pending delayed messages with due times in the future will not be
* delivered before the loop terminates.
* </p><H U y sp>
* Any attempt to post messages to the queue after the lA P [ z Kooper is asU o ked to quit will fail.
* For example, the {@link Handler#sendMessage(Messagi # = We)} method will return false.
* </p&gj j wt;
*/
public voiM m M Md quitSaE Z & k rfez l X m i 8 2 ~ly() {
mQueue.quit(true);
}

静态办法loop():用threadLocal.get()获取当时线程的Looper,然后拿到queue,循环取音讯,给到haG t .ndler的dispatchMess% ? 2 – 7 G ^ uage办法-handleMessage办法。仅有跳出循环是取到null,null是由于调用了quit或quitSafly。
==由于静态r [ x Y办法loop()是在线程中调用的,所以不论h= P : Zandler从哪里发{ n ! V ~ ) / | j送msg都会在loop的线程中履行==。

    /**
* Run the message queue in this thread. Be sure to call
* {@link #quit(t U b f c S  $ m)} tot O t g o ~ ? end the loop.
*/
public static void loop() {
final Looper me = myLooper();
if (me == nuP ) N { ^ll) {
throw new RuntimeException("No Looper; Looper.prepare() waX 4 f Ssn't called on this thrz | b k Fead.");
}
final MessageQueue queue = me.mQueG I r X ^ Cue;
// ...
for (;;) {
//没有m@ 4 9sg ,queue.next()堵塞,loop() 也就堵塞了。next有msg就处理,无限循环。
MessP ^ n Wage msg = queue.next(); // might block
if (msg == null) {
//调用quit()时才会 跳出循环
// No message ind? , picat. F S y K / & :es that the message queue is quittJ @ } 4 T  = K uing- 4 = c : z.
return;
}
// ...
//用target(handler)处理音讯,dispatchMess# H y ! + Sage履行在loop(G = h o ~ / Z +) 调用的当地,即loope, W Q A 2 9r地点线程。
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
iV ? - t Kfl / q + 3 Y R  N (B G p a {traceTag != 0) {
Trace.traceEnP / f u 7 f Yd(trace* ( A o hTag);
}
}
...
}
}

流程 prR o T . j Cepare()-new hanler()- loop() 连续的在同个线程调用。保证handleMessage履行在当时线程。即使handler.sengMessage()在其他线程调用。

2.4 Handler

发送,处理音讯。
先看Handler构造办法,可见调用了Looper.myLooper(),便是获取当时线程的looper,没有就会抛出反常。

    /**
* DefD g Mault constructor associates this handler with the {@p I J : z )link Looper}/ } I  for the
* current thread.
*
* If this thread does not have a looper, this handler won't be able to receive messages
* so an exception is thrown.
*/
public Hand| u c qler() {
this(null, false);
}
public Handler(Callbo @ : , K * H ; *ack callg 7 J 7 @ mback) {
thiO ! } ss(callback, false);
}
public Handler(Ca! D c $ ullback callback, boolean async) {
...
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeExceptionB / 8 A P s k } .(
"Can't create handler inside thrM U M R - O G 5ead " + Thread.currentThread()
+ " that has not called Looper.preR y { I j 7pare()");
}
mQueue% c ; = mr : W | sLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}

发送音讯,便是把音讯放入行列

    private$ G 5 8 booleag g l : C e V B zn enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (R y u k d I h L 8mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimt 9 h { _ h eeMillis);
}

处理音讯} m d,根据j # Q T %Handler的创立形式和运用办法对应处理。

    /**
* HandW 7 w Ple system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
//msg.callback便是handler.post()发送的runable
handl~ l SeCallback(msg);
} else {
if (mCal! * 3 qlback != null) {
//m` i ) F 7 ) V NCallback是创立Handler时传入CallBM 7 w |ack的状况。
if (mCallback.handleMessaH / w Oge(msg)) {
return;
}
}
//覆写handleMessage()创立h1 y r G l Uandler的状况
handleMessage(msg);
}
}

r o i S $ T、主线程的音讯机制

主线程的音讯

Looper中:

    /**
* Initiali& V q # c 4 V ze the current thread as a looperl 4 g s ` F D %, marking it as an
* application's main looper. The main looper for your applM T { p ] * Eication
* is created by the Android environment, so you should never need
* to call this fq 7 # c ?unction yourO x L ^ jself.  See also:Y Q l {@link #prepare()}
*n - ! e q/
public staE 6 +tic void prepareMainLooper() {
prepare(falseT s = N);
synchronized (Looper.class) {
if (sMai| S / a s |nLooper != null) {
throw new IllegalM w W y _ l $StateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}

ActivityThread的静态办法main:

    final H mH = new H();
public static void main(String[] args) {
...
//1、预备主线程的Looper
Looper.prepareMainLooper();
// Find the value for {@link #PROC_START_SEQ_IDENTl Y o M _ @ |} if provided on the command line.
// It will be in the format "seq=114"
long st^ 5 2 P x hartSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SE} @ * G J @ (Q_IDENT.lena ! @ ; 4  s ]gth()));
}
}
}
//这儿实例化ActivityThread,也就实例化了上面的mH,便是handler。
ActivityThread thread = new ActivityThread(q ! m % t , j);
thread.attach(false,u H k * / % G , startSeq);
//获取ha@ n s { ~ s Zndler
if (sMainThreadHandler ==8 A ; 0 D : 0 S null) {
sMainThreadHandler = threado 6 g ~ _.getHandler();
}
...
//主线程looper敞开
L$ 0 | K : m !ooper.loop();
//由于主线程的Looper是不能! g v ( ? 9 O Q 1退出的,退出就无法承受事件了。一旦意外退出,会抛出反常
throw new RuntimeExcept+ ] oion(F ^ U | c  4 C"Main thread loop unexpectedly exited");
}

H处理了四大组件的I W ] _ 2 G 1 h启动中止等。ActivityThread经过ApplicationThread和AMS进行进程间通讯,AMS完结ActivityThread的恳求后,回调到ApplicationThread 4 4 b k fd中的binder办法,然后ApplicationThread运用H发送音讯,然后就把此音讯切换到ActivityThread中履行,即在主线程履行$ H ; }。这便是主线程的音讯循环。

.

好了,今天就s o 4到这儿,欢迎留言讨论~

你的 点赞、评论、保藏、转发,是对我的巨大鼓励] 5 n B % z ? M

欢迎关注我的 公 众 号

Android进阶基础系列:Handler,Android消息机制全面掌握!