Handler常見問題分析

  1. runOnUIThread(Runable run)和Handler有何關(guān)系其原理又是什么?
  2. handler.post(Runable run) 回調(diào)方法是運行在那個線程?
  3. 在UI Thread 創(chuàng)建Handler時,沒有手動去調(diào)用 Looper.preper()、Looper.loop()方法為何沒有拋出異常?
  4. Looper 在UI Thread中輪詢消息時,為何沒有造成阻塞?
  5. 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的理解

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容