Handler使用與原理解析
使用
1.在主線程中使用:
public static final int UPDATE_NAME=2;
private MyHandler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler=new MyHandler(new WeakReference<MainActivity>(this));
}
@Override
protected void onResume() {
super.onResume();
new Thread(new Runnable() {
@Override
public void run() {
Log.d("ZX", "發送消息"+Thread.currentThread().getName());
mHandler.sendEmptyMessage(UPDATE_NAME);
}
},"thread-2").start();
}
static class MyHandler extends Handler{
public MyHandler(WeakReference<MainActivity> activity) {
reference=activity;
}
WeakReference<MainActivity> reference;
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_NAME:
Log.d("ZX", "處理消息"+Thread.currentThread().getName());
break;
default:
break;
}
}
}
說明:
- 新建一個靜態內部類MyHandler,使用弱引用引用外部實例,主要是爲了防止內存泄漏。
- 在點擊的時候創建了一個子線程,在子線程中用handler發送了一個消息。
- MyHander接收到消息時打印出線程名稱
執行結果:
進程號100695是主線程,名字叫做main,這個是在Activity創建的時候系統設置的名稱,那又是另外一個故事了,這裏暫時先不管。進程號10707是我們創建的子線程,名字叫做thread-2,這個是我們自己取的。因爲在linux看來沒有進程和線程的區別都是進程,唯一的區別就是進程會有獨立的內存等資源,而線程沒有。
2.在子線程中使用。
public static final int UPDATE_NAME=2;
private TextView mTvName;
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTvName=(TextView) findViewById(R.id.inset_tv);
findViewById(R.id.boast).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
Log.d("ZX", "發送消息"+Thread.currentThread().getName());
mHandler.sendEmptyMessage(UPDATE_NAME);
}
},"thread--2").start();
}
});
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
mHandler=new Handler(){
@Override
public void dispatchMessage(Message msg) {
Log.d("ZX", "處理消息"+Thread.currentThread().getName());
}
};
Looper.loop();
}
},"thread--1").start();
}
說明:
- 新建一個線程取名thread-1,在其內部創建mHhandler。
- 在點擊的時候創建了一個子線程thread-2,在子線程中用mHhandler發送了一個消息。
- mHhandler接收到消息時執行打印。
執行結果:
這裏就實現了線程間通信,要知道他們是怎麼做到的,就需要進入源碼分析了。
原理
我們還是以主線程中的使用來分析吧,畢竟用的多。這裏我們要知道Activity的創建是從ActivityThread.java的main函數開始的。本文源碼來自android10。
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
//無本文無關的代碼暫時去掉
Looper.prepareMainLooper();//步驟 step 1
// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
// It will be in the format "seq=114"
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();//step 2
throw new RuntimeException("Main thread loop unexpectedly exited");
}
//step 1
public static void prepareMainLooper() {
prepare(false);//step 1.1
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();//step 1.2
}
}
按順序先看//step 1.1
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)//step 1.1.1);//step 1.1.2
}
第一進來肯定是null,
//step 1.1.1
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//step 1.1.1.1
mThread = Thread.currentThread();//step 1.1.1.2
}
這裏就是創建了一個MessageQueue對象,獲取當前線程。
MessageQueue我們稍後再看,繼續看Looper類。
//step 1.1.1 執行完了,我們回到//step 1.1.2
public void set(T value) {
Thread t = Thread.currentThread();//step 1.1.2.1
ThreadLocalMap map = getMap(t);//step 1.1.2.2
if (map != null)
map.set(this, value);1.1.2.3
else
createMap(t, value);1.1.2.4
}
進入//step 1.1.2.1
/**
* Returns a reference to the currently executing thread object.
*
* @return the currently executing thread.
*/
@FastNative
public static native Thread currentThread();
這個是一個JNI方法,實現在C++,本文我們不去追蹤,這裏從註釋就能看出是返回一個當前執行線程對象的引用。
我們繼續看//step 1.1.2.2
/**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
獲取當前線程中的 threadlocalMap。看名字就知道是個map集合。是個容器用來存東西的。我們點進去稍微看下。它是ThreadLocal的一個靜態內部類。
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
/**
* The initial capacity -- MUST be a power of two.
*/
private static final int INITIAL_CAPACITY = 16;
/**
* The table, resized as necessary.
* table.length MUST always be a power of two.
*/
private Entry[] table;
/**
* The number of entries in the table.
*/
private int size = 0;
/**
* The next size value at which to resize.
*/
private int threshold; // Default to 0
/**
* Set the resize threshold to maintain at worst a 2/3 load factor.
*/
private void setThreshold(int len) {
threshold = len * 2 / 3;
}
/**
* Increment i modulo len.
*/
private static int nextIndex(int i, int len) {
return ((i + 1 < len) ? i + 1 : 0);
}
/**
* Decrement i modulo len.
*/
private static int prevIndex(int i, int len) {
return ((i - 1 >= 0) ? i - 1 : len - 1);
}
看到這些字段應該很熟悉了吧,它和hashmap非常的類似。
定義了一個數組Entry[] table,存儲是元素是Entry。繼承至弱引用。k-v的形式存儲內容。key是ThreadLocal<?>。
初始容量是16,每次增加必須是2的指數倍,當滿了2/3時擴容。
由於第一次進來獲取到的map肯定是空,所以執行//step 1.1.2.4
/**
* 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
*/
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);//step 1.1.2.4.1
}
這個就是創建一個ThreadLocalMap 然後設置到當前的線程中。
//step 1.1.2.4.1
/**
* 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);
}
這個就是創建一個ThreadlocalMap。並存入第一個元素Entry。這個的firstValue就是前面創建的Looper。fristKey就是在Looper裏面創建的靜態常量 sThreadLocal。我們計算一下,第一個entry的下標是存在哪。
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);計算這個I。
前面
int threadLocalHashCode = nextHashCode();
int INITIAL_CAPACITY = 16;
所以後面的是16-1=15。主要是看前面的數。
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
private static AtomicInteger nextHashCode =
new AtomicInteger();
private static final int HASH_INCREMENT = 0x61c88647;
AtomicInteger是隨着jdk5.0出來的,它位於java.util.concurrent.atomic包下,AtomicInteger,一個提供原子操作的Integer的類。也就是說在Java語言中,++i和i++操作並不是線程安全的,在使用的時候,不可避免的會用到synchronized關鍵字。而AtomicInteger則通過一種線程安全的加減操作接口,也就是說當有多個線程操作同一個變量時,使用AtomicInteger不會導致變量出現問題,而且比使用 synchronized效率高,
AtomicInteger的常用方法如下:
-
AtomicInteger(int initialValue):創建一個AtomicInteger實例,初始值由參數指定。不帶參的構造方法初始值爲0。
-
int addAndGet(int delta):以原子方式將輸入的數值與實例中的值(AtomicInteger裏的value)相加,並返回結果,與getAndAdd(int delta)相區分,從字面意思即可區分,前者返回相加後結果,後者先返回再相加。
-
boolean compareAndSet(int expect, int update) :如果當前值等於預期值,則以原子方式將該值設置爲輸入的值。
-
int getAndIncrement():以原子方式將當前值加1,注意:這裏返回的是自增前的值。
-
void lazySet(int newValue):最終會設置成newValue,使用lazySet設置值後,可能導致其他線程在之後的一小段時間內還是可以讀到舊的值。
-
int getAndSet(int newValue):以原子方式設置爲newValue的值,並返回舊值。
/**
* Atomically adds the given value to the current value.
*
* @param delta the value to add
* @return the previous value
*/
public final int getAndAdd(int delta) {
return U.getAndAddInt(this, VALUE, delta);
}
這裏返回的上一個值。上一個值由於我們沒設置初始值所以這裏返回的就是0.
那麼0&15還是0 。所以第一個元素的下標是0。
到這裏整個1.1 prepare()步驟就都走完了。
總結:就是創建了Looper對象,MessageQueue對象,再把創建的Looper存到了當前線程的ThreadLocalMap中。下標爲0,Key是Looper中的靜態常量sThreadLocal,value是前面創建的Looper對象。
繼續看//step1.2 myLooper()方法。
/**
* 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 sThreadLocal.get();//step 1.2.1
}
看註釋就知道是獲取前面我們存到ThreadLocalMap的looper。不過我還是要點進去看看。
/**
* 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);//step 1.2.1.1
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);//step 1.2.1.2
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
//step 1.2.1.1
/**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
就是取出當前線程的ThreadLocalMap。就是之前在//step 1.1.2.4.1 存進去的。
//step 1.2.1.2
/**
* 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;
else
return getEntryAfterMiss(key, i, e);//step 1.2.1.2.1
}
這裏就是取出最終的Entry,就是前面我們存進去的第一個元素。我們計算一下這個I
首先(table.length - 1)=15 沒什麼疑問。
關鍵是看key.threadLocalHashCode。這個這裏的Key就是sThreadLocal。
/**
* The difference between successively generated hash codes - turns
* implicit sequential thread-local IDs into near-optimally spread
* multiplicative hash values for power-of-two-sized tables.
*/
private static final int HASH_INCREMENT = 0x61c88647;
/**
* Returns the next hash code.
*/
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
這裏和我們前面存的流程是一樣的。之前我們返回的是0.但是這一次會返回。HASH_INCREMENT=0x61c88647。
實際就是0x61c88647&15=7 但是我們之前存的是0.所以走 //step 1.2.1.2.1
/**
* Version of getEntry method for use when key is not found in
* its direct hash slot.
*
* @param key the thread local object
* @param i the table index for key's hash code
* @param e the entry at table[i]
* @return the entry associated with key, or null if no such
*/
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) {
ThreadLocal<?> k = e.get();
if (k == key)
return e;
if (k == null)
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
return null;
}
這裏會通過循環一個一個的去找entry直到找到。expungeStaleEntry是擦除過期的元素。估計因爲和弱引用有關。這裏有點不是很明白。問題不大,繼續回到//step 1.2.1.2 往下走
就是取出value值返回。直接回到//step 1.2 也沒什麼東西,繼續回到 //step 1。 Looper.prepareMainLooper();方法執行完了。接着會去創建目標Activity。這個我們不管。看//step 2
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 //step 2.1
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
msg.target.dispatchMessage(msg);//step 2.2
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
}
}
這個方法比較簡單了。先進入死循環,//step 2.1就是從消息隊列中MessageQueue取出消息。如果返回消息是null則return 就意味着looper所在的線程 代碼都執行完了,這個線程結束。主線程肯定是不會出現這種情況的。
//step 2.2 中的dispatchMessage方法應該很熟悉了吧。msg.target 就是Handler。
總結一下Looper類:
1.創建了當前所在線程的Looper唯一對象,並通過ThreadLocalMap保存起來。
2.創建了MessageEqueue消息隊列
3.無限循環從消息隊列中取出消息,並回調dispatchMessage方法。這一步是一直運行在創建Looper對象的線程中的,這個是跨線程的關鍵。
接下來是Handler機制中最重要的類。MesssageQueue。
我們從之前跳過的//step 1.1.1.1 創建開始。
//step 1.1.1.1
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit(); //step 1
}
參數從意思看就是 是否允許退出,主線程是傳入false是不允許主動退出的。
這裏關鍵看 mPtr = nativeInit()
這個其實是個C++的指針地址
private long mPtr; // used by native code
//step 1
這裏是JNI調用,我們通過包名+類名+方法名的規律可以知道 其實現在 android_os_MessageQueue.cpp類中。JNI類一般位於framework\base\core\jni 包中,但是也有一些是例外。那就只能去源碼網站上去搜索了。
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();//step 1.1
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
nativeMessageQueue->incStrong(env);//step 1.2
return reinterpret_cast<jlong>(nativeMessageQueue);//step 1.3
}
這裏需要解釋下幾個類。
JNIEnv* env:代表的JNI環境,虛擬機會傳參數進來
jclass clazz:代表Java的對象Object,也是虛擬機傳入進來
//step 1.1
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);//step 1.1.1
Looper::setForThread(mLooper);
}
}
//step 1.1.1 創建native Looper
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);//step 1.1.1.1
LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd: %s",
strerror(errno));
AutoMutex _l(mLock);
rebuildEpollLocked();//step 1.1.1.2
}
//step 1.1.1.1 構造喚醒事件,這個已經是內核中的方法了。
加鎖,再執行 //step 1.1.1.2 重建eqooll事件
void Looper::rebuildEpollLocked() {
// Close old epoll instance if we have one.
關閉舊的 epoll實例
if (mEpollFd >= 0) {
close(mEpollFd);
}
// Allocate the new epoll instance and register the wake pipe.
//創建新的epoll實例,註冊喚醒管道
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeEventFd;
//添加喚醒事件的監聽
int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
for (size_t i = 0; i < mRequests.size(); i++) {
const Request& request = mRequests.valueAt(i);
struct epoll_event eventItem;
request.initEventItem(&eventItem);
//添加請求隊列事件的監聽
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, request.fd, & eventItem);
if (epollResult < 0) {
ALOGE("Error adding epoll events for fd %d while rebuilding epoll set: %s",
request.fd, strerror(errno));
}
}
}
這個方法主要是 使用epoll多路IO複用的機制,這套機制是Linux提供的。簡單的說就是 能監聽文件描述符 並且註冊要監聽的事件,當有事件產生時就會觸發 往後面執行,如果沒有事件則會阻塞。具體可以自行了解Eqoll機制。這個機制是非常重要的。很多地方都有用到。
Looper對象中的mWakeEventFd添加到epoll監控,以及mRequests也添加到epoll的監控範圍內。這樣我們可以用2種方式喚醒。
//step 1.2
nativeMessageQueue->incStrong(env);//step 1.2
這句意思是增加引用計數。incStrong方法在RefBase類中。
是C++中的智能指針。
想使用智能指針處理一個對象,這個對象必須要繼承RefBase類, 以便智能指針能操作類提供引用計數操作!
這裏主要是爲了回收機制。
Android C++層的內存收回主要是通過三個類來實現,分別是RefBase,sp,wp;
SP和WP是兩個智能指針模板類,sp是strong pointer,wp則是weak pointer,亦我們常說的強引用和弱引用;實例化sp和wp這兩個模板類的類型必須是派生自RefBase的類。
//step 1.3
return reinterpret_cast<jlong>(nativeMessageQueue)
將創建的nativeMessageQueue 指針地址通過reinterpret_cast強制轉化符 轉化爲long類型返回到java層。到這裏就知道了。java層中的mPtr就是一個指針地址。
接着看Lopper從MessageQueue中取消息的操作。
final MessageQueue queue = me.mQueue;
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);//step 2.1
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 == 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();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
這個方法比較關鍵,我們一步一步看。
首先判斷初始化創建的NativeMessageQueue是否成功。
定義了2個int 變量。其中 nextPollTimeoutMillis 是指阻塞時間。
然後開啓一個無限循環。這個循環退出只有2個返回,1是返回一個Messaged,2是返回null 前面分析Looper就知道如果獲取的Message是null Looper循環就會退出 結束了。
//step 2.1
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);//step 2.1.1
}
這裏傳入了2個參數,1個是之前創建的NativeMessageQueue指針,1個是timeoutMillis 延遲退出時間。
//step 2.1.1
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
mPollEnv = env;
mPollObj = pollObj;
mLooper->pollOnce(timeoutMillis);//step 2.1.1.1
mPollObj = NULL;
mPollEnv = NULL;
if (mExceptionObj) {
env->Throw(mExceptionObj);
env->DeleteLocalRef(mExceptionObj);
mExceptionObj = NULL;
}
}
這個就是賦值 然後就調用pollOnce(timeout) 輪詢一次。
//step 2.1.1.1
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
result = pollInner(timeoutMillis);//step 2.1.1.1.1
}
}
調用內部輪詢
//step 2.1.1.1.1
int Looper::pollInner(int timeoutMillis) {
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
// No longer idling.
這個方法很長,概括就是開始等待事件發生,timeoutMillis=0 則立即往下執行,timeoutMillis=-1則會一直阻塞 直到監聽的事件到來,我們在前面添加了2個事件,1是喚醒,1是請求隊列。
回到step 2.1進行往下走。就到了處理消息的時候了。
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());
}
消息Message是單鏈表結構存儲。異常查找 直到找到消息。
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();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
如果沒有消息則把超時設置爲-1 。就是一直等待。
如果找到了Message則判斷其when是否需要立刻值,還是要延時執行。
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
如果要退出,則返回NUll 。接着處理空閒Handler。的回調問題。如果都沒有則需要阻塞。
接下來。看Handler類。這個是我們用的最多的。首先看構造函數。
有很多構造函數。最常用的是 無參和1個Looper參數的。
public Handler(@Nullable Callback callback, boolean async) {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
獲取當前線程創建保存的Looper。再取出MessageQueue。這2個對象一個線程中只有一個。
在看最常用的。Handler的sendMessage方法和post方法。
public boolean sendMessageAtTime(@NonNull 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(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
關鍵是queue.enqueueMessage(msg, uptimeMillis);看它怎麼把消息插入隊列中。
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new 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();
return false;
}
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);
}
}
return true;
}
如果頭消息爲null,當前消息的時間延長爲0,當前消息的延遲時間 小於頭消息
則把當前消息設置爲頭消息。
否則把消息 插入到 鏈表的合適位置通過when。
when=systemClock.uptimeMillis() + delayMillis
這個延時是通過設置的時間加系統開機後的時間。
最後再看看分發方法。
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(@NonNull Message msg) {
}
/**
* Handle system messages here.
*/
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
如果消息的callback不爲空則直接回調run方法。這裏的callback其實就是runnable接口,它和線程每關係,只是作爲一個接口使用。
否則就執行handleMessage方法。子類Handler必須重寫這個方法來接收Message。
到此整個Handler機制就分析完了,設計到的知識點還是很多的。
首先畫一個UML圖:
再畫一個簡單的流程圖。
藍色和紅色代表2個不同的線程。
總結
Handler是消息處理機制也是線程間的通信機制,它是無法跨進程使用,利用的是進程中各線程能共享內存。是一種生產消費者模型。主要由 Looper,MessageQueue,Handler,Message 和ThreadLocal 組合而成。
調用Looper.prepare()時,會創建一個Looper對象 保存到當前線程的ThreadLocalMap中,每個線程只會創建一個Looper。Looper持有一個MessageQueue的引用。
調用Looper.loop()時,會開啓無線循環調用消息隊列的Next()方法去獲取消息,當取到消息時就會回調Handler的dispatchMessage()分發消息方法,然後繼續循環。
MessageQueue持有消息列表的頭結點消息,初始化時會調用nativeInit()方法。會調用到C++中的android_os_MessageQueue.cpp和Looper.cpp中的方法。主要是利用Linux聽的epoll機制。使用epoll_create()創建一個文件描述符。
再用epoll_ctl()往描述符中添加要監聽的事件。再用
epoll_wait()開始監聽。最後一個參數是timeout,
指定epoll的超時時間,單位是毫秒。當timeout爲-1是,epoll_wait調用將永遠阻塞,直到某個時間發生。當timeout爲0時,epoll_wait調用將立即返回。
消息隊列的next()方法中,開發無限循環然後調用nativePollOnce()輪詢一次,第一次的參數爲0會立即往下執行。由於目前沒消息,所以會把timeout參數設置爲-1。進入阻塞狀態 直到被喚醒。當有消息時則會判斷當前消息的when參數是否需要立即執行,是則返回 否則設置timeout值爲等待時間。
當我們new Handler對象時,會獲取當前線程中的Looper對象,所以主線的Hander一定要在主線程中初始化。
當調用sendMessage或者post時最終會調用到 消息隊列中的enqueueMessage(),把消息插入到消息單鏈表的合適位置,並根據需要喚醒之前阻塞的next()進行執行。
next()方法運行在之前創建looper的線程中。而enqueueMessage()方法是運行在被調用的各個線程中。這裏就實現了跨線程。
我覺得整個機制中最關鍵的是。linux的epoll多路IO複用機制。它保證了無限循環時不會出現CPU佔有率很高,有消息時喚醒處理,沒有消息時則阻塞。在android的很多其他地方都有用到。比如Zygota進程,binder機制。