voidcheckThread(){
if (mThread != Thread.currentThread()) {
thrownew CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
複製代碼
ThreadLocal<Boolean> booleanThreadLocal = new ThreadLocal<>();
ThreadLocal<Integer> integerThreadLocal = new ThreadLocal<>();
booleanThreadLocal.set(true);
integerThreadLocal.set(0);
Log.i(TAG, "testThreadLocal: main thread, boolean= "+booleanThreadLocal.get());
Log.i(TAG, "testThreadLocal: main thread, int = "+integerThreadLocal.get());
new Thread(new Runnable() {
@Overridepublicvoidrun(){
booleanThreadLocal.set(false);
integerThreadLocal.set(1);
Log.i(TAG, "testThreadLocal: a thread, boolean="+booleanThreadLocal.get());
Log.i(TAG, "testThreadLocal: a thread, int = "+integerThreadLocal.get());
}
}).start();
new Thread(new Runnable() {
@Overridepublicvoidrun(){
booleanThreadLocal.set(null);
integerThreadLocal.set(2);
Log.i(TAG, "testThreadLocal: b thread, boolean="+booleanThreadLocal.get());
Log.i(TAG, "testThreadLocal: b thread, int = "+integerThreadLocal.get());
}
}).start();
複製代碼
結果:post
2020-01-0810:15:38.6238976-8976/com.hfy.demo01 I/hfy: testThreadLocal: main thread, boolean= true2020-01-0810:15:38.6238976-8976/com.hfy.demo01 I/hfy: testThreadLocal: main thread, int = 02020-01-0810:15:38.6248976-9226/com.hfy.demo01 I/hfy: testThreadLocal: a thread, boolean=false2020-01-0810:15:38.6248976-9226/com.hfy.demo01 I/hfy: testThreadLocal: a thread, int = 12020-01-0810:15:38.6268976-9227/com.hfy.demo01 I/hfy: testThreadLocal: b thread, boolean=null2020-01-0810:15:38.6268976-9227/com.hfy.demo01 I/hfy: testThreadLocal: b thread, int = 2複製代碼
下面看下ThreadLocal的get()、set()方法。ui
/** * Returns the value in the current thread's copy of this * thread-local variable. If the variable has no value for the * current thread, it is first initialized to the value returned * by an invocation of the {@link #initialValue} method. * * @return the current thread's value of this thread-local */public T get(){
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
/** * Variant of set() to establish initialValue. Used instead * of set() in case user has overridden the set() method. * * @return the initial value */private T setInitialValue(){
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
複製代碼
/** * Sets the current thread's copy of this thread-local variable * to the specified value. Most subclasses will have no need to * override this method, relying solely on the {@link #initialValue} * method to set the values of thread-locals. * * @param value the value to be stored in the current thread's copy of * this thread-local. */publicvoidset(T value){
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
/** * Create the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @param firstValue value for the initial entry of the map */voidcreateMap(Thread t, T firstValue){
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
複製代碼
/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
複製代碼
/** * Construct a new map initially containing (firstKey, firstValue). * ThreadLocalMaps are constructed lazily, so we only create * one when we have at least one entry to put in it. */
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
/** * Get 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. * * @param key the thread local object * @return the entry associated with key, or null if no such */private Entry getEntry(ThreadLocal<?> key){
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
elsereturn getEntryAfterMiss(key, i, e);
}
/** * Set the value associated with key. * * @param key the thread local object * @param value the value to be set */privatevoidset(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.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
複製代碼
ThreadLocal<Boolean> booleanThreadLocal = new ThreadLocal<>();
ThreadLocal<Integer> integerThreadLocal = new ThreadLocal<>();
booleanThreadLocal.set(true);
integerThreadLocal.set(0);
Log.i(TAG, "testThreadLocal: main thread, boolean= "+booleanThreadLocal.get());
Log.i(TAG, "testThreadLocal: main thread, int = "+integerThreadLocal.get());
new Thread(new Runnable() {
@Overridepublicvoidrun(){
booleanThreadLocal.set(false);
integerThreadLocal.set(1);
Log.i(TAG, "testThreadLocal: a thread, boolean="+booleanThreadLocal.get());
Log.i(TAG, "testThreadLocal: a thread, int = "+integerThreadLocal.get());
}
}).start();
new Thread(new Runnable() {
@Overridepublicvoidrun(){
booleanThreadLocal.set(null);
integerThreadLocal.set(2);
Log.i(TAG, "testThreadLocal: b thread, boolean="+booleanThreadLocal.get());
Log.i(TAG, "testThreadLocal: b thread, int = "+integerThreadLocal.get());
}
}).start();
複製代碼
結果:
2020-01-0810:15:38.6238976-8976/com.hfy.demo01 I/hfy: testThreadLocal: main thread, boolean= true2020-01-0810:15:38.6238976-8976/com.hfy.demo01 I/hfy: testThreadLocal: main thread, int = 02020-01-0810:15:38.6248976-9226/com.hfy.demo01 I/hfy: testThreadLocal: a thread, boolean=false2020-01-0810:15:38.6248976-9226/com.hfy.demo01 I/hfy: testThreadLocal: a thread, int = 12020-01-0810:15:38.6268976-9227/com.hfy.demo01 I/hfy: testThreadLocal: b thread, boolean=null2020-01-0810:15:38.6268976-9227/com.hfy.demo01 I/hfy: testThreadLocal: b thread, int = 2複製代碼
booleanenqueueMessage(Message msg, long when){
if (msg.target == null) {
thrownew IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
thrownew IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
returnfalse;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == 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 the 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 asynchronous message in the queue.
needWake = mBlocked && p.target == null && 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; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.if (needWake) {
nativeWake(mPtr);
}
}
returntrue;
}
複製代碼
next():取一條消息,沒有消息就無限循環,會阻塞。
Message next(){
//...//有msg就return,沒有消息就無限循環,會阻塞。如quit,return null。for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.finallong now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// 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) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
//有消息就returnreturn msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.if (mQuitting) {
dispose();
//quit後返回nullreturnnull;
}
// ...
}
複製代碼
2.3 Looper
looper,消息循環器。
先看靜態方法prepare():
// sThreadLocal.get() will return null unless you've called prepare().staticfinal ThreadLocal<Looper> sThreadLocal = new ThreadLocal<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 * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */publicstaticvoidprepare(){
prepare(true);
}
privatestaticvoidprepare(boolean quitAllowed){
if (sThreadLocal.get() != null) {
thrownew RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
/** * Initialize the current thread as a looper, marking it as an * application's main looper. The main looper for your application * is created by the Android environment, so you should never need * to call this function yourself. See also: {@link #prepare()} */publicstaticvoidprepareMainLooper(){
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
thrownew IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
privateLooper(boolean quitAllowed){
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
複製代碼
/** * Returns the application's main looper, which lives in the main thread of the application. */publicstatic Looper getMainLooper(){
synchronized (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. */publicstatic@NullableLooper myLooper(){
return sThreadLocal.get();
}
/** * 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 thrown. */publicstatic@NonNullMessageQueue myQueue(){
return myLooper().mQueue;
}
複製代碼
/** * Quits the looper. * <p> * Causes the {@link #loop} method to terminate without processing any * more messages in the message queue. * </p><p> * Any attempt to post messages to the queue after the looper is asked to quit will fail. * For example, the {@link Handler#sendMessage(Message)} method will return false. * </p><p class="note"> * Using this method may be unsafe because some messages may not be delivered * before the looper terminates. Consider using {@link #quitSafely} instead to ensure * that all pending work is completed in an orderly manner. * </p> * * @see #quitSafely */publicvoidquit(){
mQueue.quit(false);
}
/** * Quits the looper safely. * <p> * Causes the {@link #loop} method to terminate as soon as all remaining messages * in the message queue that are already due to be delivered have been handled. * However pending delayed messages with due times in the future will not be * delivered before the loop terminates. * </p><p> * Any attempt to post messages to the queue after the looper is asked to quit will fail. * For example, the {@link Handler#sendMessage(Message)} method will return false. * </p> */publicvoidquitSafely(){
mQueue.quit(true);
}
複製代碼
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */publicstaticvoidloop(){
final Looper me = myLooper();
if (me == null) {
thrownew RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// ...for (;;) {
//沒有msg ,queue.next()阻塞,loop() 也就阻塞了。next有msg就處理,無限循環。
Message msg = queue.next(); // might blockif (msg == null) {
//調用quit()時纔會 跳出循環// No message indicates that the message queue is quitting.return;
}
// ...//用target(handler)處理消息,dispatchMessage執行在loop() 調用的地方,即looper所在線程。try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
}
}
複製代碼
/** * Default constructor associates this handler with the {@link Looper} 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. */publicHandler(){
this(null, false);
}
publicHandler(Callback callback){
this(callback, false);
}
publicHandler(Callback callback, boolean async){
...
mLooper = Looper.myLooper();
if (mLooper == null) {
thrownew RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
複製代碼
發送消息,就是把消息放入隊列
privatebooleanenqueueMessage(MessageQueue queue, Message msg, long uptimeMillis){
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
複製代碼
/** * Initialize the current thread as a looper, marking it as an * application's main looper. The main looper for your application * is created by the Android environment, so you should never need * to call this function yourself. See also: {@link #prepare()} */publicstaticvoidprepareMainLooper(){
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
thrownew IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
複製代碼
ActivityThread的靜態方法main:
final H mH = new H();
publicstaticvoidmain(String[] args){
...
//一、準備主線程的Looper
Looper.prepareMainLooper();
// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.// It will be in the format "seq=114"long startSeq = 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_SEQ_IDENT.length()));
}
}
}
//這裏實例化ActivityThread,也就實例化了上面的mH,就是handler。
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
//獲取handlerif (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...
//主線程looper開啓
Looper.loop();
//由於主線程的Looper是不能退出的,退出就沒法接受事件了。一旦意外退出,會拋出異常thrownew RuntimeException("Main thread loop unexpectedly exited");
}
複製代碼