- runOnUIThread(Runable run)和Handler有何關(guān)系其原理又是什么?
- handler.post(Runable run) 回調(diào)方法是運行在那個線程?
- 在UI Thread 創(chuàng)建Handler時,沒有手動去調(diào)用 Looper.preper()、Looper.loop()方法為何沒有拋出異常?
- Looper 在UI Thread中輪詢消息時,為何沒有造成阻塞?
- Toast為何在子線程調(diào)用會拋出異常?
上述問題都是和Handler機制相關(guān)的,在實際開發(fā)中有遇到過嗎?有深入了解過嗎?沒有關(guān)系,在這里我們再慢慢探討。還對Handler流程不熟悉的可以在這里找到答案 Handler原理剖析
runOnUIThread的工作原理
該方法是Activity的公共方法,下面是該方法的注釋:
在UI線程中運行執(zhí)行的action(手動指定的回調(diào)函數(shù)),如果當(dāng)前線程是UI線程,回調(diào)函數(shù)馬上執(zhí)行,如果當(dāng)前線程不是UI線程,那回調(diào)函數(shù)被加入到UI線程的事件隊列
從上面的注釋可以看出,該方法分為兩種邏輯執(zhí)行:1、直接運行在UI Thread;2、將該事件加入到UI Thread的消息隊列;
第一種還好理解,那第二種的消息隊列和我們一直講的Handler有何關(guān)系呢,要理清這些關(guān)系,從源碼開始:
//源碼
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
//如果當(dāng)前線程是UI線程,這將action加入到消息隊列
mHandler.post(action);
} else {
//直接調(diào)用run
action.run();
}
}
//Activity的UI Thread中直接使用
runOnUiThread(new Runnable() {
@Override public void run() {
// TODO: 你的代碼
}
});
該方法源碼還是挺簡潔的,先來action.run()直接調(diào)用的邏輯,結(jié)合源碼和在Activity的UI Thread中直接使用的代碼來看,action.run()是直接運行在UI Thread的,沒有任何異步操作,比較好理解,所有如果是在activity中直接調(diào)用的runOnUiThread,沒有開線程的情況下,最好不要在run方法體中做耗時操作,不然會造成UI Thread 的阻塞,接下來mHandler.post(action);才是我們要說的重點在實際代碼中我們?nèi)绻慈缦路椒ㄕ{(diào)用,會執(zhí)行mHandler.post(action);:
new Thread() {
@Override public void run() {
runOnUiThread(new Runnable() {
@Override public void run() {
// TODO: 你的代碼
}
});
}
}.start();
進入mHandler.post(action)的源碼:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
這樣的代碼和Handler發(fā)送消息一樣的流程了,還不知道的請看Handler原理剖析,
其實理解整個邏輯,關(guān)鍵是理解getPostMessage(r):
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
m.callback = r; 這行代碼就是將我們傳入的Runable對象賦值給message的callback成員變量,因為mHandler.post 實在主線程中執(zhí)行,所以loop()會運行在主線成中所以消息會在主線程中接收處理。我們通過源碼來驗證一下我們的邏輯是否正確,現(xiàn)在進入到Handler的消息分發(fā)方法中去:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
msg.callback != null 這段代碼肯定成立,因為上文中的getPostMessage方法中已經(jīng)將我們傳入的callback對象賦值給msg.callback,所以會直接執(zhí)行到 handleCallback(msg); 那么我們再來看handleCallback(msg):
private static void handleCallback(Message message) {
message.callback.run();
}
沒錯,只有一行代碼,message.callback.run(); 這行代碼是在UI線程中執(zhí)行的,那為什么呢?你怎么看出來的呢?好吧,我們要知其然也要知其所以然,那么我們再詳細解釋一下吧。上文的mHandler是Activity的成員變量,并且是在主線程中進行初始化,所以Looper.preper()、Looper.loop()等方法,就會在主線程中調(diào)用,還記得loop()做了那些事嗎?不記得的請看 Handler原理剖析,loop()輪詢messageQueue中的消息,如果沒有消息就退出輪詢,如果有,就執(zhí)行message.target.dispatchMessage進行消息分發(fā),源碼就不貼出來了,所以message.target.dispatchMessage就是在主線程中調(diào)用,由此推出上文的 message.callback.run();也會在主線程中執(zhí)行,所以 runOnUiThread的run回調(diào)方法也就是在子線程中執(zhí)行。對沒,錯,這就是一個Handler子線程發(fā)消息,主線程接收消息的一種封裝而已。
handler.post(Runable run) 回調(diào)方法是運行在那個線程
其實這個問題已經(jīng)在第一個問題中得到了解決,我們這里簡單分析一下就行;
new Handler().post(new Runnable() {
@Override public void run() {
// TODO: you code
}
});
我們在開發(fā)中時常寫出這樣的代碼,而且還在run()中處理一些耗時任務(wù),對,沒錯,你加班改的bug就是我寫的。
在主線程中創(chuàng)建Handler對象同時將Runable加入到主線程的消息隊列,所以run()會在主線程中調(diào)用,和第一個問題相類似的流程不在贅述。
在UI Thread 創(chuàng)建Handler時,沒有手動去調(diào)用 Looper.preper()、Looper.loop()方法為何沒有拋出異常?
要回答這樣的一系列問題,需要了解到Android程序的入口,那么Android程序真正的入口是在哪里呢?想想java程序的入口是某個類的main(),而Android程序的main函數(shù)實在類ActivityThread中,所以Android程序的入口就是ActivityThread的main(),這個我們直接拋出結(jié)論,至于其過程和原理這里不會探討。ActivityThread的源碼大概有5000多行,我們并不是要全部看完,只需要找到我們需要的代碼就行了。
public static void main(String[] args) {
....
//創(chuàng)建Looper對象,其實和preper()實現(xiàn)類似,只是該方法通常用于ActivityThread中
Looper.prepareMainLooper();
//創(chuàng)建ActivityThread對象
ActivityThread thread = new ActivityThread();
//建立Binder通道 (創(chuàng)建新線程)
thread.attach(false);
//消息循環(huán)運行
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
根據(jù)ActivityThread的源碼可以看出,在應(yīng)用程序啟動的時候在ActivityThread的main方法中手動調(diào)用了Looper.preper()、Looper.loop();
Looper 在UI Thread中輪詢消息時,為何沒有造成阻塞?
其實這個問題設(shè)計到的東西還是挺多的,需要明白Handler原理、Process/Thread、Android Binder IPC、Linux pipe/epoll機制、Activity的啟動及其生命周期等等一大堆東西,當(dāng)然這里我們就不會去深入了。
要了解這個問題,首先從ActivityThread的handlerMessage方法開始:
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);
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");
handlePauseActivity((IBinder)msg.obj, false, (msg.arg1&1) != 0, msg.arg2,
(msg.arg1&2) != 0);
maybeSnapshot();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case PAUSE_ACTIVITY_FINISHING:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
handlePauseActivity((IBinder)msg.obj, true, (msg.arg1&1) != 0, msg.arg2,
(msg.arg1&1) != 0);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case STOP_ACTIVITY_SHOW:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
handleStopActivity((IBinder)msg.obj, true, msg.arg2);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case STOP_ACTIVITY_HIDE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
handleStopActivity((IBinder)msg.obj, false, msg.arg2);
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");
handleResumeActivity((IBinder) msg.obj, true, msg.arg1 != 0, true);
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");
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");
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((IBinder)msg.obj);
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;
}
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}
ActivityThread的內(nèi)部類H繼承于Handler,通過handler消息機制,簡單說Handler機制用于同一個進程的線程間通信。Activity的生命周期都是依靠主線程的Looper.loop,當(dāng)收到不同Message時則采用相應(yīng)措施:在H.handleMessage(msg)方法中,根據(jù)接收到不同的msg,執(zhí)行相應(yīng)的生命周期。比如收到msg=H.LAUNCH_ACTIVITY,則調(diào)用ActivityThread.handleLaunchActivity()方法,最終會通過反射機制,創(chuàng)建Activity實例,然后再執(zhí)行Activity.onCreate()等方法; 再比如收到msg=H.PAUSE_ACTIVITY,則調(diào)用ActivityThread.handlePauseActivity()方法,最終會執(zhí)行Activity.onPause()等方法,主線程的消息又是哪來的呢?當(dāng)然是App進程中的其他線程通過Handler發(fā)送給主線程,epoll(請百度)當(dāng)沒有消息的時候會執(zhí)行epoll.wait,等待句柄寫的時候再喚醒,這個時候其實是阻塞的。所有的ui操作都通過handler來發(fā)消息操作。比如屏幕刷新16ms一個消息,各種點擊事件,Activity的生命周期函數(shù)執(zhí)行消息,所以就會有句柄寫操作,喚醒上文的wait操作,因此不會被造成主線程的阻塞??吹竭@是不是內(nèi)心在哦,原來如此!
Toast為何在子線程調(diào)用會拋出異常?
從Toast的源碼中可以看出,其內(nèi)部也有Handler發(fā)送消息的流程,這樣一來這個問題就解決了,其根本原因還是要歸類于Handler的原理,所以本文中的問題都是圍繞Handler來進行。只要把Handler搞明白,則萬變不離其宗;本文的主要目的也是通過這些問題來加深對Handler的理解