Android 源碼分析之旅3.1--消息機制源碼分析

本篇文章已授權微信公眾號 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,我拉你進群交(♂)流(♀)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,732評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,214評論 3 426
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,781評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,588評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,315評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,699評論 1 327
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,698評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,882評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,441評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,189評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,388評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,933評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,613評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,023評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,310評論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,112評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,334評論 2 377

推薦閱讀更多精彩內容