Android 原始碼分析之旅3 1 訊息機制原始碼分析

小楠總發表於2017-12-21

本篇文章已授權微信公眾號 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,然後再去深入理解訊息機制裡面各個部件。

主執行緒與Looper的關係.png

舉個例子,廣播: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):

如圖:

跨程式啟動Service.png

最後是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指向下一個結點,最後返回剛剛取出來的結點,如下圖所示:

訊息池的概念.png

上面我們知道了訊息可以直接建立,也可以通過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、通過上面就完成了一次訊息的迴圈。

#####訊息的傳送

分析完訊息的分發與處理,最後我們來看看訊息的傳送:

訊息的傳送.png

訊息的傳送有這一系列方法,甚至我們的一系列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.png

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是跟主執行緒繫結在一起的。

#####主執行緒一直在迴圈,為什麼沒有卡死,還能響應我們的點選之類的呢?

  1. 通過子執行緒去訪問主執行緒的程式碼,有程式碼注入、回撥機制嘛。
  2. 切入到訊息佇列裡面的訊息去訪問主執行緒,例如傳訊息,然後回撥四大元件的生命週期等等。
  3. IPC跨程式的方式也可以實現。

雖然主執行緒一直在執行,但是我們可以通過外部條件、注入的方法來執行自己的程式碼,而不是一直死迴圈。

###總結

Android訊息機制.png

如圖所示,在主執行緒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的一些使用姿勢

如果覺得我的文字對你有所幫助的話,歡迎關注我的公眾號:

公眾號:Android開發進階

我的群歡迎大家進來探討各種技術與非技術的話題,有興趣的朋友們加我私人微信huannan88,我拉你進群交(♂)流(♀)

相關文章