消息機制存在的意義
- 為什么不能在非UI線程中操作UI控件停做?
因為Android的UI控件不是線程安全的邑跪,如果在多線程中并發(fā)訪問可能會導致UI控件處于不可預期的狀態(tài), - 為什么不對UI控件加上鎖機制汉规?
首先加上鎖會讓UI訪問的邏輯變得復雜恭朗;其次鎖機制會降低UI訪問的效率,因為鎖機制會阻塞某些線程的執(zhí)行 - Android是在哪兒校驗UI操作是否是在UI線程哥攘?
//ViewRootImpl.java
public ViewRootImpl(Context context, Display display) {
... ...
mThread = Thread.currentThread();
... ...
}
//該方法會多個方法的開頭被調(diào)用剖煌,例如requestLayout()、invalidateChildInParent()等等... ...
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException("Only the original thread that created a view hierarchy can touch its views.");
}
}
消息機制原理
Android系統(tǒng)主要通過Message逝淹、MessageQueue耕姊、Looper、Handler三個類來實現(xiàn)消息處理栅葡,其中Message代表消息茉兰,MessageQueue是用來描述消息隊列欣簇,Looper是用來創(chuàng)建消息隊列以及進入消息循環(huán)坯约,Handler 是用來發(fā)送消息和處理消息。(以下源碼均來自API 26 Android 8.0鬼店,源碼在線查看:http://androidxref.com),先看下整個事件的整體脈絡黔龟,建議根據(jù)圖然后去找對應的函數(shù)調(diào)用。大圖點擊
Handler
- Handler的成員變量
//可能造成內(nèi)存泄露
private static final boolean FIND_POTENTIAL_LEAKS = false;
//是否為異步巍棱,默認為false
final boolean mAsynchronous;
final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback;
- Handler的創(chuàng)建和獲取
public Handler() { this(null, false); }
public Handler(Callback callback, boolean async) {
//檢測內(nèi)存是否存在內(nèi)存泄露的可能
... ...
mLooper = Looper.myLooper();
if (mLooper == null) {
//必須先執(zhí)行Looper.prepare()的原因
throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
- 消息發(fā)送
//最基本的消息發(fā)送
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }
public final boolean sendMessage(Message msg){ return sendMessageDelayed(msg, 0); }
public final boolean sendEmptyMessage(int what){ return sendEmptyMessageDelayed(what, 0); }
//延時發(fā)送消息
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
//SystemClock.uptimeMisllis()是系統(tǒng)啟動至今的時間
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
//在未來某一確定的時間點發(fā)送消息(發(fā)送消息最終的調(diào)用在這)
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) {
//Message中保存的Handler 對象
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
- 消息處理
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
// 該Callback實在初始化的時候傳進來
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
//Handler 的內(nèi)部接口
public interface Callback {
public boolean handleMessage(Message msg);
}
Looper
- Looper的重要成員
//存儲不同線程的Looper
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
//主線程的Looper
private static Looper sMainLooper;
final MessageQueue mQueue;
final Thread mThread;
- Looper的創(chuàng)建和獲取
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
//Looper.prepare()只能執(zhí)行一次的原因
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
//準備主線程的Looper
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
//Looper構造器
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
//拿到當前線程的Looper對象
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
//拿到主線程的Looper對象
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
- UI線程中Looper的創(chuàng)建
//ActivityThread.java
public static void main(String[] args) {
... ...
Looper.prepareMainLooper();
... ...
Looper.loop();
... ...
}
- 消息循環(huán)
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next();
if (msg == null) {
return;
}
... ...
msg.target.dispatchMessage(msg);
}
}
MessageQueue
- MessageQueue的重要成員
//該MessageQueue是否可退出航徙,通過Looper.prepare()創(chuàng)建的都是可退出的,UI線程的MessageQueue是不可以退出的
private final boolean mQuitAllowed;
//當前消息隊列的頭部
Message mMessages;
//C++層NativeMessageQueue的地址
private long mPtr;
//是否處于阻塞狀態(tài)
private boolean mBlocked;
- MessageQueue的創(chuàng)建
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
//JNI調(diào)用初始化 C++ 層的 NativeMessageQueue.cpp
mPtr = nativeInit();
}
- 消息入隊
boolean enqueueMessage(Message msg, long when) {
... ...
synchronized (this) {
... ...
msg.when = when;
Message p = mMessages;
boolean needWake;
//頭部等于空到踏,或者入隊消息的時間小于當前時間就插到隊首
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//尋找隊尾或者在自己發(fā)送時間之后的位置,然后插入隊列中
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;
prev.next = msg;
}
if (needWake) {
//JNI調(diào)用尚猿,喚醒NativeMessageQueue
nativeWake(mPtr);
}
}
return true;
}
- 消息出隊
Message next() {
//執(zhí)行下一條消息時還需要等待的時間窝稿,當為-1時代表消息隊列中沒有消息,則無限等待
int nextPollTimeoutMillis = 0;
for (;;) {
... ...
//JNI調(diào)用凿掂,阻塞操作伴榔,當?shù)却齨extPollTimeoutMillis時長,或者消息隊列被喚醒庄萎,都會返回
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
... ...
if (msg != null) {
if (now < msg.when) {
//當異步消息觸發(fā)時間大于當前時間踪少,則設置下一次輪詢的超時時長
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 獲取一條消息,并返回
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
//成功地獲取MessageQueue中的下一條即將要執(zhí)行的消息
return msg;
}
} else {
//沒有消息
nextPollTimeoutMillis = -1;
}
//消息正在退出糠涛,返回null(調(diào)用了quit()方法)
if (mQuitting) {
dispose();
return null;
}
... ...
}
... ...
}
}
NativeMessageQueu
//Java層中MessageQueue的nativaInit()方法
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
... ...
//將NativeMessageQueue對象的指針強轉(zhuǎn)為Java中的long類型并返回
return reinterpret_cast<jlong>(nativeMessageQueue);
}
- NativeMessageQueue的構造器
//和Java層的MessageQueue的構造器很類似
NativeMessageQueue::NativeMessageQueue() : mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
//獲取當前線程的Looper援奢,類似Java層的myLooper()
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
Looper.cpp
- 構造器
Looper::Looper(bool allowNonCallbacks) {
//構造一個喚醒事件的文件描述符,能在用戶態(tài)用做事件wait/notify機制忍捡,通過內(nèi)核取喚醒用戶態(tài)的事件集漾。
//這個對象保存了一個內(nèi)核維護的uint64_t類型的整型counter。這個counter初始值被參數(shù)一指定锉罐,一般初值設置為0帆竹。
//read:如果計數(shù)值counter的值不為0,讀取成功脓规,獲得到該值栽连。如果counter的值為0,非阻塞模式,會直接返回失敗秒紧,并把errno的值指紋EINVAL绢陌。
//如果為阻塞模式,一直會阻塞到counter為非0位置熔恢。
//write:會增加8字節(jié)的整數(shù)在計數(shù)器counter上脐湾,如果counter的值達到0xfffffffffffffffe時,就會阻塞叙淌。直到counter的值被read秤掌。阻塞和非阻塞情況同上面read一樣。
mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
... ...
}
- 喚醒鎖
void Looper::wake() {
uint64_t inc = 1;
//向喚醒事件的文件中寫入數(shù)據(jù)
write(mWakeEventFd, &inc, sizeof(uint64_t));
}
- 輪詢
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for(;;){
... ...
if (result != 0) {
... ...
return result;
}
result = pollInner(timeoutMillis);
}
}
int Looper::pollInner(int timeoutMillis) {
... ...
//等待被喚醒鹰霍,喚醒時間為timeoutMillis闻鉴,如果timeoutMillis<0,則等待I/O事件喚醒
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
... ...
for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
if (fd == mWakeEventFd) {
if (epollEvents & EPOLLIN) {
awoken(); //已經(jīng)喚醒了茂洒,則讀取并清空管道數(shù)據(jù)
}
} else {
... ...
}
... ...
}
... ...
return result;
}
void Looper::awoken() {
uint64_t counter;
//不斷讀取管道數(shù)據(jù)孟岛,目的就是為了清空管道內(nèi)容
TEMP_FAILURE_RETRY(read(mWakeEventFd, &counter, sizeof(uint64_t)));
}
疑點
Looper.loop()已經(jīng)進入死循環(huán),那么主線程的其他操作是如何執(zhí)行的督勺?
- 首先從線程的角度分析:一個線程是會在什么時間結束渠羞?很明顯是該線程中的代碼執(zhí)行完成后就會結束次询,然后該線程就會被JVM回收盏触。那么如何保證一個線程永遠處于運行狀態(tài)?貌似只有死循環(huán)。所以這么設計肯定是合理的授艰,不然主線程早已被回收了,之所以一直存在就是因為Looper.loop()一直在執(zhí)行糟需。
- 這時候第二個問題就出現(xiàn)了谷朝,如果Looper.loop()一直在阻塞等待,那么UI的繪制是如何執(zhí)行杈帐?那么只能從MainLooper的源頭查起,沒錯就是ActivityThread:
final ApplicationThread mAppThread = new ApplicationThread();
final H mH = new H();
public static void main(String[] args) {
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
//這時候就是進入死循環(huán)累铅,一旦退出循環(huán)直接拋出異常
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
//ActivityThread的內(nèi)部類站叼,在這個類中有許多發(fā)送消息的方法,并且從方法中的參數(shù)可以看出都是與UI線程相關的操作投储,并且我們知道Binder的服務端都是運行在Binder線程池中
private class ApplicationThread extends IApplicationThread.Stub {
@Override
public final void scheduleLaunchActivity(... ...) {
... ...
sendMessage(H.LAUNCH_ACTIVITY, r);
}
public final void schedulePauseActivity(... ...) {
... ...
sendMessage(finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY, ... ...);
}
... ...
}
//ActivityThread的內(nèi)部類阔馋,該類繼承自Handler,并且處理的消息都是與UI線程相關
private class H extends Handler {
switch (code) {
case LAUNCH_ACTIVITY:
... ...
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
break;
case PAUSE_ACTIVITY:
handlePauseActivity(... ...);
break;
... ...
}
}
所以根據(jù)以上代碼冲泥,可以大致分析出凡恍,UI線程本質(zhì)的的所有操作都是有MainHanlder來進行處理怔球,而發(fā)送這些消息的操作就分別在不同的子線程中,例如與生命周期相關的都是在ApplicationThread的線程中闽巩,下面從網(wǎng)上盜了張圖:Android中為什么主線程不會因為Looper.loop里的死循環(huán)卡死担汤?
所以說主線程不能獨活,必須依賴于其他線程來給主線程發(fā)送消息隅很,而我們自己在主線程里面發(fā)送消息無疑是來自于另一個線程先發(fā)的消息率碾,例如我們經(jīng)常會在Activity的生命周期中進行發(fā)送消息,而這些生命周期方法的執(zhí)行也是來自與另一個線程的消息绒尊,就相當于我們在
handlerMessage
方法中再次發(fā)送消息下面的Demo可以說明上述結論是正確的仔粥。
public class MainActivity extends AppCompatActivity {
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
Log.d("TestHandler","Start Thread One");
Looper.prepare();
handler = new Handler(){
@Override
public void handleMessage(Message msg) {
Log.d("TestHandler",msg.what+" = "+System.currentTimeMillis());
if (msg.what == 3){
Log.d("TestHandler","send Message!");
sendEmptyMessageDelayed(4,1000);
sendEmptyMessage(5);
}
}
};
Looper.loop();
handler.sendEmptyMessageDelayed(1,500);
handler.sendEmptyMessage(2);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
Log.d("TestHandler","Start Thread Two");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendEmptyMessage(3);
}
}).start();
}
}
//輸出
03-21 22:21:31.132 21995-22032/? D/TestHandler: Start Thread One
03-21 22:21:31.133 21995-22033/? D/TestHandler: Start Thread Two
03-21 22:21:31.633 21995-22032/? D/TestHandler: 3 = 1521642091633
03-21 22:21:31.633 21995-22032/? D/TestHandler: send Message!
03-21 22:21:31.633 21995-22032/? D/TestHandler: 5 = 1521642091633
03-21 22:21:32.635 21995-22032/com.whf.test D/TestHandler: 4 = 1521642092635?
內(nèi)存泄露問題
- 為什么會發(fā)生內(nèi)存泄露矮湘?
- Java 回收機制是根據(jù)可達性來判別口糕,即一個對象如果是沒有被其他對象引用(即不可達的),這時候在GC的過程就會把這個對象列為可回收的十办,在下一次的GC的時候就會執(zhí)行回收。
- 一個非靜態(tài)內(nèi)部類會持有外部類的引用向族。
- Message對象的taget屬性引用了Handler對象棠绘。
基于以上三點可知,當一個Activity中有一個非靜態(tài)的Hanlder類夜矗,該Handler類就會持有該Activity的引用让虐,如果當該Activitv退出的時候,其內(nèi)部的Handler發(fā)送的Message還沒有被處理完对扶,而這些Message對象都會持有該Activity的引用惭缰,所以就會導致Activity無法被回收,從而造成內(nèi)存泄露逞泄。
- Handler如何避免內(nèi)存泄露拜效?
- 以靜態(tài)內(nèi)部類或外部類的形式存在紧憾,不要在Activity中寫非靜態(tài)的Handler類
- Handler的靜態(tài)內(nèi)部類訪問外部類時采用弱引用
- 在Activity回收前昌渤,清空MessageQueue中存儲的該Handler發(fā)送的Message,通過調(diào)用Handler的
removeCallbackAndMessages()
方法般眉,該方法原理如下:
//Handle
public final void removeCallbacksAndMessages(Object token) {
mQueue.removeCallbacksAndMessages(this, token);
}
//MessageQueue
void removeCallbacksAndMessages(Handler h, Object object) {
synchronized (this) {
Message p = mMessages;
while (p != null && p.target == h && (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}
while (p != null) {
Message n = p.next;
if (n != null) {
if (n.target == h && (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
HandlerThread
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
private @Nullable Handler mHandler;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
protected void onLooperPrepared() {
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
public Looper getLooper() {
if (!isAlive()) {
return null;
}
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
@NonNull
public Handler getThreadHandler() {
if (mHandler == null) {
mHandler = new Handler(getLooper());
}
return mHandler;
}
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
public int getThreadId() {
return mTid;
}
}
IntentService
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
//stopSelf(int)來保證你當前停止Service的請求是基于上一個請求的柿汛。也就是說络断,當你調(diào)用stopSelf(int)项玛,你把startID傳給了對應的要停止的Service,
//這個startID是上一個請求的StartID襟沮!!如果沒有第二個請求來,那么這個Service就會死掉膀跌,但是如果這個Service已經(jīng)又接受到一個新的啟動請求之后硅则,
//你才調(diào)用stopSelf(int),那么你傳遞給stopSelf()的ID是上一個請求的ID暑认,而當前Service的startID已經(jīng)更新為新的請求的ID大审,造成兩個ID不對應,stopSelf()失效粮彤,
//那么Service就不會停止姜骡。這樣就避免了將后面的請求終止
stopSelf(msg.arg1);
}
}
public IntentService(String name) {
super();
mName = name;
}
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onDestroy() {
mServiceLooper.quit();
}
@Override
@Nullable
public IBinder onBind(Intent intent) {
return null;
}
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}