本篇文章已授權微信公眾號 guolin_blog (郭霖)獨家釋出 本人小楠——一位勵志的Android開發者。
###前言
在分析Application Framework的時候,經常會看到Handler的使用,尤其見得最多的是“H”這個系統Handler的使用。因此有必要先學習Android中的訊息機制。
###應用程式的入口分析
應用程式的入口是在ActivityThread的main方法中的(當應用程式啟動的時候,會通過底層的C/C++去呼叫main方法),這個方法在ActivityThread類的最後一個函式裡面,核心程式碼如下:
public static void main(String[] args) {
Environment.initForCurrentUser();
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
}
複製程式碼
######在分析原始碼的時候,你可能會發現一些if(false){}之類的語句,這種寫法是方便除錯的,通過一個標誌就可以控制某些程式碼是否執行,比如說是否輸出一些系統的Log。
在main方法裡面,首先初始化了我們的Environment物件,然後建立了Looper,然後開啟訊息迴圈。根據我們的常識知道,如果程式沒有死迴圈的話,執行完main函式(比如構建檢視等等程式碼)以後就會立馬退出了。之所以我們的APP能夠一直執行著,就是因為Looper.loop()裡面是一個死迴圈:
public static void loop() {
for (;;) {
}
}
複製程式碼
######這裡有一個小小的知識,就是之所以用for (;;)而不是用while(true)是因為防止一些人通過黑科技去修改這個迴圈的標誌(比如通過反射的方式)
######在非主執行緒裡面我們也可以搞一個Handler,但是需要我們主動去為當前的子執行緒繫結一個Looper,並且啟動訊息迴圈。
Looper主要有兩個核心的方法,一是prepare,而是開始loop迴圈。 通過Looper、Handler、Message、MessageQueue等組成了Android的訊息處理機制,也叫事件、反饋機制。
###為什麼需要這樣一個訊息機制?
我們知道每一個應用程式都有一個主執行緒,主執行緒一直迴圈的話,那麼我們的自己的程式碼就無法執行了。而系統在主執行緒繫結一個Looper迴圈器以及訊息佇列,Looper就像是一個水泵一樣不斷把訊息傳送到主執行緒。如果沒有訊息機制,我們的程式碼需要直接與主執行緒進行訪問,操作,切換,訪問主執行緒的變數等等,這樣做會帶來不安全的問題,另外APP的開發的難度也會提高,同時也不利於整個Android系統的運作。有了訊息機制,我們可以簡單地通過傳送訊息,然後Looper把訊息傳送到主執行緒,然後就可以執行了。
訊息其中包括: 我們自己的操作訊息(客戶端的Handler) 系統的操作訊息(系統Handler):比如啟動Activity等四大元件(例如突然來電話的時候跳轉到電話介面) 我們的思路是先分析系統的Handler,然後再去深入理解訊息機制裡面各個部件。
舉個例子,廣播:AMS傳送訊息到MessageQueue,然後Looper迴圈,系統的Handler取出來以後才處理。(AMS是處理四大元件的生命週期的一個比較重要的類,在以後我們分析IPC機制以及Activity啟動流程的時候會提到)
###系統的Handler在哪裡?
在ActivityThread的成員變數裡面有一個這樣的大H(繼承Handler),這個就是系統的Handler:
final H mH = new H();
複製程式碼
回顧一下ActivityThread的main方法可以知道,在new ActivityThread的時候,系統的Handler就就初始化了,這是一種餓載入的方法,也就是在類被new的時候就初始化成員變數了。另外還有一種懶載入,就是在需要的時候才去初始化,這兩種方式在單例設計模式裡面比較常見。
public static void main(String[] args) {
Environment.initForCurrentUser();
Looper.prepareMainLooper();
//new 的時候已經把成員變數Handler初始化了
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
}
複製程式碼
下面看系統Handler的定義(看的時候可以跳過一些case,粗略地看即可):
private class H extends Handler {
public static final int LAUNCH_ACTIVITY = 100;
public static final int PAUSE_ACTIVITY = 101;
public static final int PAUSE_ACTIVITY_FINISHING= 102;
public static final int STOP_ACTIVITY_SHOW = 103;
public static final int STOP_ACTIVITY_HIDE = 104;
public static final int SHOW_WINDOW = 105;
public static final int HIDE_WINDOW = 106;
public static final int RESUME_ACTIVITY = 107;
public static final int SEND_RESULT = 108;
public static final int DESTROY_ACTIVITY = 109;
public static final int BIND_APPLICATION = 110;
public static final int EXIT_APPLICATION = 111;
public static final int NEW_INTENT = 112;
public static final int RECEIVER = 113;
public static final int CREATE_SERVICE = 114;
public static final int SERVICE_ARGS = 115;
public static final int STOP_SERVICE = 116;
public static final int CONFIGURATION_CHANGED = 118;
public static final int CLEAN_UP_CONTEXT = 119;
public static final int GC_WHEN_IDLE = 120;
public static final int BIND_SERVICE = 121;
public static final int UNBIND_SERVICE = 122;
public static final int DUMP_SERVICE = 123;
public static final int LOW_MEMORY = 124;
public static final int ACTIVITY_CONFIGURATION_CHANGED = 125;
public static final int RELAUNCH_ACTIVITY = 126;
public static final int PROFILER_CONTROL = 127;
public static final int CREATE_BACKUP_AGENT = 128;
public static final int DESTROY_BACKUP_AGENT = 129;
public static final int SUICIDE = 130;
public static final int REMOVE_PROVIDER = 131;
public static final int ENABLE_JIT = 132;
public static final int DISPATCH_PACKAGE_BROADCAST = 133;
public static final int SCHEDULE_CRASH = 134;
public static final int DUMP_HEAP = 135;
public static final int DUMP_ACTIVITY = 136;
public static final int SLEEPING = 137;
public static final int SET_CORE_SETTINGS = 138;
public static final int UPDATE_PACKAGE_COMPATIBILITY_INFO = 139;
public static final int TRIM_MEMORY = 140;
public static final int DUMP_PROVIDER = 141;
public static final int UNSTABLE_PROVIDER_DIED = 142;
public static final int REQUEST_ASSIST_CONTEXT_EXTRAS = 143;
public static final int TRANSLUCENT_CONVERSION_COMPLETE = 144;
public static final int INSTALL_PROVIDER = 145;
public static final int ON_NEW_ACTIVITY_OPTIONS = 146;
public static final int CANCEL_VISIBLE_BEHIND = 147;
public static final int BACKGROUND_VISIBLE_BEHIND_CHANGED = 148;
public static final int ENTER_ANIMATION_COMPLETE = 149;
public static final int START_BINDER_TRACKING = 150;
public static final int STOP_BINDER_TRACKING_AND_DUMP = 151;
public static final int MULTI_WINDOW_MODE_CHANGED = 152;
public static final int PICTURE_IN_PICTURE_MODE_CHANGED = 153;
public static final int LOCAL_VOICE_INTERACTION_STARTED = 154;
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
case RELAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
ActivityClientRecord r = (ActivityClientRecord)msg.obj;
handleRelaunchActivity(r);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
case PAUSE_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
SomeArgs args = (SomeArgs) msg.obj;
handlePauseActivity((IBinder) args.arg1, false,
(args.argi1 & USER_LEAVING) != 0, args.argi2,
(args.argi1 & DONT_REPORT) != 0, args.argi3);
maybeSnapshot();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
case PAUSE_ACTIVITY_FINISHING: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
SomeArgs args = (SomeArgs) msg.obj;
handlePauseActivity((IBinder) args.arg1, true, (args.argi1 & USER_LEAVING) != 0,
args.argi2, (args.argi1 & DONT_REPORT) != 0, args.argi3);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
case STOP_ACTIVITY_SHOW: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
SomeArgs args = (SomeArgs) msg.obj;
handleStopActivity((IBinder) args.arg1, true, args.argi2, args.argi3);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
case STOP_ACTIVITY_HIDE: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
SomeArgs args = (SomeArgs) msg.obj;
handleStopActivity((IBinder) args.arg1, false, args.argi2, args.argi3);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
case SHOW_WINDOW:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityShowWindow");
handleWindowVisibility((IBinder)msg.obj, true);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case HIDE_WINDOW:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityHideWindow");
handleWindowVisibility((IBinder)msg.obj, false);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case RESUME_ACTIVITY:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
SomeArgs args = (SomeArgs) msg.obj;
handleResumeActivity((IBinder) args.arg1, true, args.argi1 != 0, true,
args.argi3, "RESUME_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case SEND_RESULT:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityDeliverResult");
handleSendResult((ResultData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case DESTROY_ACTIVITY:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityDestroy");
handleDestroyActivity((IBinder)msg.obj, msg.arg1 != 0,
msg.arg2, false);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case BIND_APPLICATION:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case EXIT_APPLICATION:
if (mInitialApplication != null) {
mInitialApplication.onTerminate();
}
Looper.myLooper().quit();
break;
case NEW_INTENT:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityNewIntent");
handleNewIntent((NewIntentData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case RECEIVER:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastReceiveComp");
handleReceiver((ReceiverData)msg.obj);
maybeSnapshot();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case CREATE_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj)));
handleCreateService((CreateServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case BIND_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
handleBindService((BindServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case UNBIND_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceUnbind");
handleUnbindService((BindServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case SERVICE_ARGS:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceStart: " + String.valueOf(msg.obj)));
handleServiceArgs((ServiceArgsData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case STOP_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStop");
handleStopService((IBinder)msg.obj);
maybeSnapshot();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case CONFIGURATION_CHANGED:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "configChanged");
mCurDefaultDisplayDpi = ((Configuration)msg.obj).densityDpi;
handleConfigurationChanged((Configuration)msg.obj, null);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case CLEAN_UP_CONTEXT:
ContextCleanupInfo cci = (ContextCleanupInfo)msg.obj;
cci.context.performFinalCleanup(cci.who, cci.what);
break;
case GC_WHEN_IDLE:
scheduleGcIdler();
break;
case DUMP_SERVICE:
handleDumpService((DumpComponentInfo)msg.obj);
break;
case LOW_MEMORY:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "lowMemory");
handleLowMemory();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case ACTIVITY_CONFIGURATION_CHANGED:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityConfigChanged");
handleActivityConfigurationChanged((ActivityConfigChangeData) msg.obj,
msg.arg1 == 1 ? REPORT_TO_ACTIVITY : !REPORT_TO_ACTIVITY);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case PROFILER_CONTROL:
handleProfilerControl(msg.arg1 != 0, (ProfilerInfo)msg.obj, msg.arg2);
break;
case CREATE_BACKUP_AGENT:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backupCreateAgent");
handleCreateBackupAgent((CreateBackupAgentData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case DESTROY_BACKUP_AGENT:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backupDestroyAgent");
handleDestroyBackupAgent((CreateBackupAgentData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case SUICIDE:
Process.killProcess(Process.myPid());
break;
case REMOVE_PROVIDER:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "providerRemove");
completeRemoveProvider((ProviderRefCount)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case ENABLE_JIT:
ensureJitEnabled();
break;
case DISPATCH_PACKAGE_BROADCAST:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastPackage");
handleDispatchPackageBroadcast(msg.arg1, (String[])msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case SCHEDULE_CRASH:
throw new RemoteServiceException((String)msg.obj);
case DUMP_HEAP:
handleDumpHeap(msg.arg1 != 0, (DumpHeapData)msg.obj);
break;
case DUMP_ACTIVITY:
handleDumpActivity((DumpComponentInfo)msg.obj);
break;
case DUMP_PROVIDER:
handleDumpProvider((DumpComponentInfo)msg.obj);
break;
case SLEEPING:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "sleeping");
handleSleeping((IBinder)msg.obj, msg.arg1 != 0);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case SET_CORE_SETTINGS:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setCoreSettings");
handleSetCoreSettings((Bundle) msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case UPDATE_PACKAGE_COMPATIBILITY_INFO:
handleUpdatePackageCompatibilityInfo((UpdateCompatibilityData)msg.obj);
break;
case TRIM_MEMORY:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "trimMemory");
handleTrimMemory(msg.arg1);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case UNSTABLE_PROVIDER_DIED:
handleUnstableProviderDied((IBinder)msg.obj, false);
break;
case REQUEST_ASSIST_CONTEXT_EXTRAS:
handleRequestAssistContextExtras((RequestAssistContextExtras)msg.obj);
break;
case TRANSLUCENT_CONVERSION_COMPLETE:
handleTranslucentConversionComplete((IBinder)msg.obj, msg.arg1 == 1);
break;
case INSTALL_PROVIDER:
handleInstallProvider((ProviderInfo) msg.obj);
break;
case ON_NEW_ACTIVITY_OPTIONS:
Pair<IBinder, ActivityOptions> pair = (Pair<IBinder, ActivityOptions>) msg.obj;
onNewActivityOptions(pair.first, pair.second);
break;
case CANCEL_VISIBLE_BEHIND:
handleCancelVisibleBehind((IBinder) msg.obj);
break;
case BACKGROUND_VISIBLE_BEHIND_CHANGED:
handleOnBackgroundVisibleBehindChanged((IBinder) msg.obj, msg.arg1 > 0);
break;
case ENTER_ANIMATION_COMPLETE:
handleEnterAnimationComplete((IBinder) msg.obj);
break;
case START_BINDER_TRACKING:
handleStartBinderTracking();
break;
case STOP_BINDER_TRACKING_AND_DUMP:
handleStopBinderTrackingAndDump((ParcelFileDescriptor) msg.obj);
break;
case MULTI_WINDOW_MODE_CHANGED:
handleMultiWindowModeChanged((IBinder) msg.obj, msg.arg1 == 1);
break;
case PICTURE_IN_PICTURE_MODE_CHANGED:
handlePictureInPictureModeChanged((IBinder) msg.obj, msg.arg1 == 1);
break;
case LOCAL_VOICE_INTERACTION_STARTED:
handleLocalVoiceInteractionStarted((IBinder) ((SomeArgs) msg.obj).arg1,
(IVoiceInteractor) ((SomeArgs) msg.obj).arg2);
break;
}
Object obj = msg.obj;
if (obj instanceof SomeArgs) {
((SomeArgs) obj).recycle();
}
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}
}
複製程式碼
從系統的Handler中,在handleMessage我們可以看到很多關於四大元件的生命週期操作,比如建立、銷燬、切換、跨程式通訊,也包括了整個Application程式的銷燬等等。 比如說我們有一個應用程式A通過Binder去跨程式啟動另外一個應用程式B的Service(或者同一個應用程式中不同程式的Service):
如圖:
最後是AMS接收到訊息以後,傳送訊息到MessageQueue裡面,最後由系統的Handler處理啟動Service的操作:
case CREATE_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj)));
handleCreateService((CreateServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
複製程式碼
在handleCreateService裡通過反射的方式去newInstance(),並且回撥了Service的onCreate方法:
private void handleCreateService(CreateServiceData data) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
//通過反射的方式去建立Service
java.lang.ClassLoader cl = packageInfo.getClassLoader();
service = (Service) cl.loadClass(data.info.name).newInstance();
} catch (Exception e) {
if (!mInstrumentation.onException(service, e) {
throw new RuntimeE)xception(
"Unable to instantiate service " + data.info.name
+ ": " + e.toString(), e);
}
}
try {
if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(service);
Application app = packageInfo.makeApplication(false, mInstrumentation);
service.attach(context, this, data.info.name, data.token, app,
ActivityManagerNative.getDefault());
//回撥了Service的onCreate方法
service.onCreate();
mServices.put(data.token, service);
try {
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} catch (Exception e) {
if (!mInstrumentation.onException(service, e)) {
throw new RuntimeException(
"Unable to create service " + data.info.name
+ ": " + e.toString(), e);
}
}
}
複製程式碼
又例如我們可以通過發SUICIDE訊息可以自殺,這樣來退出應用程式。
case SUICIDE:
Process.killProcess(Process.myPid());
break;
複製程式碼
#####應用程式的退出過程
實際上我們要退出應用程式的話,就是讓主執行緒結束,換句話說就是要讓Looper的迴圈結束。這裡是直接結束Looper迴圈,因此我們四大元件的生命週期方法可能就不會執行了,因為四大元件的生命週期方法就是通過Handler去處理的,Looper迴圈都沒有了,四大元件還玩毛線!因此我們平常寫程式的時候就要注意了,onDestroy方法是不一定能夠回撥的。
case EXIT_APPLICATION:
if (mInitialApplication != null) {
mInitialApplication.onTerminate();
}
//退出Looper的迴圈
Looper.myLooper().quit();
break;
複製程式碼
這裡實際上是呼叫了MessageQueue的quit,清空所有Message。
public void quit() {
mQueue.quit(false);
}
複製程式碼
######tips:看原始碼一定不要慌,也不要一行一行看,要抓住核心的思路去看即可。
###訊息機制的分析
#####訊息物件Message的分析
提到訊息機制,在MessageQueue裡面存在的就是我們的Message物件:
public final class Message implements Parcelable {
public int what;
public int arg1;
public int arg2;
public Object obj;
long when;
Bundle data;
Handler target;
Runnable callback;Message next;
private static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
private static boolean gCheckRecycle = true;
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
}
複製程式碼
首先我們可以看到Message物件是實現了Parcelable介面的,因為Message訊息可能需要跨程式通訊,這時候就需要程式序列化以及反序列化操作了。
Message裡面有一些我們常見的引數,arg1 arg2 obj callback when等等。這裡要提一下的就是這個target物件,這個物件就是傳送這個訊息的Handler物件,最終這條訊息也是通過這個Handler去處理掉的。
#####Message Pool訊息池的概念——重複利用Message
Message裡面中一個非常重要的概念,就是訊息池Pool:
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
複製程式碼
我們通過obtain方法取出一條訊息的時候,如果發現當前的訊息池不為空,那就直接重複利用Message(已經被建立過和handle過的);如果為空就重新new 一個訊息。這就是一種享元設計模式的概念。例如在遊戲裡面,發子彈,如果一個子彈是一個物件,一按下按鍵就發很多個子彈,那麼這時候就需要利用享元模式去迴圈利用了。
這個訊息池是通過連結串列的實現的,通過上面的程式碼可以知道,sPool永遠指向這個訊息池的頭,取訊息的時候,先拿到當前的頭sPool,然後使得sPool指向下一個結點,最後返回剛剛取出來的結點,如下圖所示:
上面我們知道了訊息可以直接建立,也可以通過obtain方法迴圈利用。所以我們平常程式設計的時候就要養成好的習慣,迴圈利用。
#####訊息的回收機制
有訊息的建立,必然有回收利用,下面兩個是Message的回收相關的核心方法:
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
void recycleUnchecked() {
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
複製程式碼
recycleUnchecked中拿到訊息池,清空當前的訊息,next指向當前的頭指標,頭指標指向當前的Message物件,也就是在訊息池頭部插入當前的訊息。
關於訊息的回收還有一點需要注意的就是,我們平時寫Handler的時候不需要我們手動回收,因為谷歌的工程師已經有考慮到這方面的問題了。訊息是在Handler分發處理之後就會被自動回收的: 我們回到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;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
//處理訊息
try {
msg.target.dispatchMessage(msg);
} finally {
}
}
msg.recycleUnchecked();//回收訊息
}
}
複製程式碼
msg.target.dispatchMessage(msg)就是處理訊息,緊接著在loop方法的最後呼叫了msg.recycleUnchecked()這就是回收了Message。
#####訊息的迴圈過程分析
下面我們繼續分析這個死迴圈:
1、首先拿到Looper物件(me),如果當前的執行緒沒有Looper,那麼就會丟擲異常,這就是為什麼在子執行緒裡面建立Handler如果不手動建立和啟動Looper會報錯的原因。
2、然後拿到Looper的成員變數MessageQueue,在MessageQueue裡面不斷地去取訊息,關於MessageQueue的next方法如下:
這裡可以看到訊息的取出用到了一些native方法,這樣做是為了獲得更高的效率,訊息的去取出並不是直接就從佇列的頭部取出的,而是根據了訊息的when時間引數有關的,因為我們可以傳送延時訊息、也可以傳送一個指定時間點的訊息。因此這個函式有點複雜,我們點到為止即可。
Message next() {
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);
synchronized (this) {
// Try to retrieve the next message. Return if found.
//拿到當前的時間戳
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//判斷頭指標的Target(Handler是否為空(因為頭指標只是一個指標的作用))
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
//遍歷下一條Message
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
//還沒有到執行的時間
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;
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;
}
}
複製程式碼
3、繼續分析loop方法:如果已經沒有訊息了,那麼就可以退出迴圈,那麼整個應用程式就退出了。什麼情況下會發生呢?還記得我們分析應用退出嗎?
在系統Handler收到EXIT_APPLICATION訊息的時候,就會呼叫Looper的quit方法:
case EXIT_APPLICATION:
if (mInitialApplication != null) {
mInitialApplication.onTerminate();
}
Looper.myLooper().quit();
break;
複製程式碼
Looper的quit方法如下,實際上就是呼叫了訊息佇列的quit方法:
public void quit() {
mQueue.quit(false);
}
複製程式碼
而訊息佇列的quit方法實際上就是執行了訊息的清空操作,然後在Looper迴圈裡面如果取出訊息為空的時候,程式就退出了:
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
//置位正在退出的標誌
mQuitting = true;
//清空所有訊息
if (safe) {
//安全的(系統的),未來未處理的訊息都移除
removeAllFutureMessagesLocked();
} else {
//如果是不安全的,例如我們自己定義的訊息,就一次性全部移除掉
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
複製程式碼
removeAllFutureMessagesLocked方法如下:
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
if (p.when > now) {
//如果所有訊息都處理完了,就一次性把全部訊息移除掉
removeAllMessagesLocked();
} else {
//否則就通過for迴圈拿到還沒有把還沒有執行的Message,利用do迴圈
//把這些未處理的訊息通過recycleUnchecked方法回收,放回到訊息池裡面
Message n;
for (;;) {
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
複製程式碼
4、msg.target.dispatchMessage(msg)就是處理訊息,這裡就會呼叫Handler的dispatchMessage方法:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
複製程式碼
在這個方法裡面會先去判斷Message的callback是否為空,這個callback是在Message類裡面定義的:
Runnable callback;
複製程式碼
這是一個Runnable物件,handleCallback方法裡面做的事情就是拿到這個Runnable物件,然後在Handler所建立的執行緒(例如主執行緒)執行run方法:
private static void handleCallback(Message message) {
message.callback.run();
}
複製程式碼
######Handler(Looper)在哪個執行緒建立的,就在哪個執行緒回撥,沒毛病,哈哈!
這就是我們平常使用post系列的方法: post、postAtFrontOfQueue、postAtTime、postDelayed 其實最終也是通過Message包裝一個Runnable實現的,我們看其中一個即可:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
複製程式碼
通過post一個Runnable的方式我們可以很簡單地做一個迴圈,比如無限輪播的廣告條Banner:
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
}
};
Runnable run = new Runnable() {
@Override
public void run() {
//廣告條切換
mBanner.next();
//兩秒鐘之後繼續下一次的輪播,其中tihs代表自身,也就是Runnable物件
mHandler.postDelayed(this, 2000);
}
};
//在需要的地方開始廣播條的輪播
mHandler.postDelayed(run, 1000);
//在需要的地方停止廣播條的輪播
mHandler.removeCallbacks(run);
複製程式碼
當然,我們的Handler自己也可以有一個mCallback物件:
public interface Callback {
public boolean handleMessage(Message msg);
}
final Callback mCallback;
複製程式碼
如果自身的Callback不為空的話,就會回撥Callback的方法。例如我們建立Handler的時候可以帶上Callback:
Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
//處理些東西,這種一般用於一些預處理,每次有訊息來都需要執行的程式碼
//返回值代表是否攔截訊息的下面寫的handleMessage,從原始碼裡面可以看出來
return false;
}
}) {
@Override
public void handleMessage(Message msg) {
}
};
複製程式碼
如果自身的Callback執行之後沒有返回true(沒有攔截),那麼最後才會回撥我們經常需要複寫的handleMessage方法,這個方法的預設實現是空處理:
public void handleMessage(Message msg) {
}
複製程式碼
5、最後是回收訊息:msg.recycleUnchecked()。所以說:我們平時在處理完handleMessage之後並不需要我們程式設計師手動去進行回收哈!系統已經幫我們做了這一步操作了。
Message msg = Message.obtain();
//不需要我們程式設計師去回收,這樣反而會更加耗效能
msg.recycle();
複製程式碼
6、通過上面就完成了一次訊息的迴圈。
#####訊息的傳送
分析完訊息的分發與處理,最後我們來看看訊息的傳送:
訊息的傳送有這一系列方法,甚至我們的一系列post方法(封裝了帶Runnable的Message),最終都是呼叫sendMessageAtTime方法,把訊息放到訊息佇列裡面:
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);
}
複製程式碼
MessageQueue的進入佇列的方法如下,核心思想就是時間比較小的(越是需要馬上執行的訊息)就越防到越靠近頭指標的位置:
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;
}
複製程式碼
訊息並不是一直在佇列的尾部新增的,而是可以指定時間,如果是立馬需要執行的訊息,就會插到佇列的頭部,就會立馬處理,如此類推。
關於這一點這裡我們可以從MessageQueue的next方法知道,next是考慮訊息的時間when變數的,下面回顧一下MessageQueue的next方法裡面的一些核心程式碼:next方法並不是直接從頭部取出來的,而是會去遍歷所有訊息,根據時間戳引數等資訊來取訊息的。
Message next() {
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
if (msg != null) {
if (now < msg.when) {
//如果當前的時間還沒到達訊息指定的時間,先計算出下一次需要處理的時間戳,然後儲存起來
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;
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;
}
}
}
}
複製程式碼
#####執行緒與Looper的繫結
執行緒裡面預設情況下是沒有Looper迴圈器的,因此我們需要呼叫prepare方法來關聯執行緒和Looper:
//Looper的prepare方法,並且關聯到主執行緒
public static void prepareMainLooper() {
//false意思不允許我們程式設計師退出(面向我們開發者),因為這是在主執行緒裡面
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
//把Looper設定為主執行緒的Looper
sMainLooper = myLooper();
}
}
//Looper一般的prepare方法
private static void prepare(boolean quitAllowed) {
//一個執行緒只能繫結一個Looper,否則的話就會丟擲如下的異常
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
複製程式碼
此處呼叫了ThreadLocal的set方法,並且new了一個Looper放進去。
Looper的成員變數sThreadLocal
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
在prepare方法中new了一個Looper並且設定到sThreadLocal裡面
sThreadLocal.set(new Looper(quitAllowed));
複製程式碼
可以看到Looper與執行緒的關聯是通過ThreadLocal來進行的,如下圖所示:
ThreadLocal是JDK提供的一個解決執行緒不安全的類,執行緒不安全問題歸根結底主要涉及到變數的多執行緒訪問問題,例如變數的臨界問題、值錯誤、併發問題等。這裡利用ThreadLocal繫結了Looper以及執行緒,就可以避免其他執行緒去訪問當前執行緒的Looper了。
ThreadLocal通過get以及set方法就可以繫結執行緒和Looper了,這裡只需要傳入Value即可,因為線是可以通過Thread.currentThread()去拿到的:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
複製程式碼
為什麼可以繫結執行緒了呢?
map.set(this, value)通過把自身(ThreadLocal以及值(Looper)放到了一個Map裡面,如果再放一個的話,就會覆蓋,因為map不允許鍵值對中的鍵是重複的)
因此ThreadLocal繫結了執行緒以及Looper。
因為這裡實際上把變數(這裡是指Looper)放到了Thread一個成員變數Map裡面,關鍵的程式碼如下:
//這是ThreadLocal的getMap方法
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
//這是Thread類中定義的MAP
ThreadLocal.ThreadLocalMap threadLocals = null;
複製程式碼
ThreadLocal的getMap方法實際上是拿到執行緒的MAP,底層是通過陣列(實際上資料結構是一種雜湊列表)實現的,具體的實現就點到為止了。
######如果android系統主執行緒Looper可以隨隨便便被其他執行緒訪問到的話就會很麻煩了,啊哈哈,你懂的。
#####Handler、Looper是怎麼關聯起來的呢?
我們知道,Looper是與執行緒相關聯的(通過ThreadLocal),而我們平常使用的Handler是這樣的:
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
}
};
複製程式碼
其實Handler在構造的時候,有多個過載方法,根據呼叫關係鏈,所以最終會呼叫下面這個構造方法:
public Handler(Callback callback, boolean async) {
mLooper = Looper.myLooper();
//如果當前執行緒(子執行緒)沒有Looper,就需要我們程式要去手動prepare以及啟動loop方法了
//子執行緒裡面預設沒有Looper迴圈器
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
複製程式碼
這裡只給出了核心的程式碼,可以看到我們在構造Handler的時候,是通過Looper的靜態方法myLooper()去拿到一個Looper物件的:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
複製程式碼
看,我們的又出現了ThreadLocal,這裡就是通過ThreadLocal的get方法去拿到當前執行緒的Looper,因此Handler就跟執行緒繫結在一起了,在一起,在一起,啊哈哈。
######一般們是在Activity裡面使用Handler的,而Activity的生命週期是在主執行緒回撥的,因此我們一般使用的Handler是跟主執行緒繫結在一起的。
#####主執行緒一直在迴圈,為什麼沒有卡死,還能響應我們的點選之類的呢?
- 通過子執行緒去訪問主執行緒的程式碼,有程式碼注入、回撥機制嘛。
- 切入到訊息佇列裡面的訊息去訪問主執行緒,例如傳訊息,然後回撥四大元件的生命週期等等。
- IPC跨程式的方式也可以實現。
雖然主執行緒一直在執行,但是我們可以通過外部條件、注入的方法來執行自己的程式碼,而不是一直死迴圈。
###總結
如圖所示,在主執行緒ActivityThread中的main方法入口中,先是建立了系統的Handler(H),建立主執行緒的Looper,將Looper與主執行緒繫結,呼叫了Looper的loop方法之後開啟整個應用程式的主迴圈。Looper裡面有一個訊息佇列,通過Handler傳送訊息到訊息佇列裡面,然後通過Looper不斷去迴圈取出訊息,交給Handler去處理。通過系統的Handler,或者說Android的訊息處理機制就確保了整個Android系統有條不紊地運作,這是Android系統裡面的一個比較重要的機制。
我們的APP也可以建立自己的Handler,可以是在主執行緒裡面建立,也可以在子執行緒裡面建立,但是需要手動建立子執行緒的Looper並且手動啟動訊息迴圈。
花了一天的時間,整個Android訊息機制原始碼分析就到這裡結束了,今天的天氣真不錯,但是我選擇了在自己的房間學習Android的訊息機制,我永遠相信,付出總會有所收穫的!
###擴充套件閱讀:論Handler的正確使用姿勢
典型錯誤的使用示例:
public class LeakActivity extends AppCompatActivity {
private int a = 10;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_leak);
mHandler.sendEmptyMessageDelayed(0, 5000);
}
//也是匿名內部類,也會引用外部
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
a = 20;
break;
}
}
};
}
複製程式碼
分析:這是我們用得最多的用法,Handler隱式地引用了Activity(通過變數a)。Handler的生命週期有可能與Activity的生命週期不一致,比如栗子中的sendEmptyMessageDelayed,在5000毫秒之後才傳送訊息,但是很有可能這時候Activity被返回了,這樣會造成Handler比Activity還要長壽,這樣會導致Activity發生暫時性的記憶體洩漏。
姿勢一: 為了解決這個問題,我們可以把Handler改為static的,但是這樣會造成Handler無法訪問Activity的非靜態變數a,但是實際開發中我們
private static Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
//a = 20; //不能訪問得到
break;
}
}
};
複製程式碼
姿勢二: 通過把Activity作為Handler成員變數,在Handler構造的時候傳進來即可。這時候我們不能使用匿名內部類了,需要把Handler單獨抽取成一個類,這樣就可以訪問Activity的非靜態變數了。但是我們的問題又回來了,這時候Handler持有了Activity的強引用了,這樣不就是回到我們的原點了嗎?(記憶體洩漏問題依然沒有解決)
private static class UIHandler extends Handler {
private LeakActivity mActivity;//外部類的強引用
public UIHandler(LeakActivity activity) {
mActivity = activity;
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
mActivity.a = 20;
}
}
複製程式碼
姿勢三(最終版本):把Activity通過弱引用來作為成員變數。雖然我們把Activity作為弱引用,但是Activity不一定就是會在GC的時候被回收,因為可能還有其他物件引用了Activity。在處理訊息的時候就要注意了,當Activity回收或者正在finish的時候,就不能繼續處理訊息了,再說了,Activity都回收了,Handler還玩個屁!
private static class UIHandler extends Handler {
private WeakReference<LeakActivity> mActivityRef;//GC的時候會回收
public UIHandler(LeakActivity activity) {
mActivityRef = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//當使用弱引用的時候,會回收Activity嗎?
//雖然用的是弱引用,但是並不代表不存在其他的物件沒有引用Activity,因此不一定會被回收
//Activity都回收了,Handler還玩個屁!
LeakActivity activity = mActivityRef.get();
if (activity == null || activity.isFinishing()) {
return;
}
mActivityRef.get().a = 20;
}
}
複製程式碼
關於更多的Handler使用,請參考我朋友寫的文章: 說說Handler的一些使用姿勢
如果覺得我的文字對你有所幫助的話,歡迎關注我的公眾號:
我的群歡迎大家進來探討各種技術與非技術的話題,有興趣的朋友們加我私人微信huannan88,我拉你進群交(♂)流(♀)。