This article has authorized the official wechat account Guolin_blog (Guo Lin) to publish my own Xiaonan, an aspiring Android developer.

# # # introduction

When analyzing the Application Framework, we often see the use of Handler, especially the use of “H” system Handler. Therefore, it is necessary to learn the messaging mechanism in Android first.

### entry analysis of the application

The entry point for the application is in the ActivityThread main method (which is called from the underlying C/C++ method when the application starts). This method is in the last function of the ActivityThread class. The core code is as follows:

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();

}
Copy the code

###### When analyzing the source code, you may find statements such as if(false){}, which are easy to debug. A flag can control whether certain code is executed, such as whether to output some system logs.

In the main method, we first initialize our Environment object, then create Looper, and then start the message loop. As we all know, if the program does not have an infinite loop, it will exit immediately after executing main (building a view, etc.). The reason our APP keeps running is because looper.loop () is an infinite loop:

public static void loop() { for (;;) {}}Copy the code

###### here is a little bit of knowledge, which is why we use for (;;). Instead of using while(true) is to prevent someone from hacking into the loop flag (e.g. by reflection)

###### We can also have a Handler in the non-main thread, but we need to actively bind a Looper to the current child thread and start the message loop.

Looper has two core methods: prepare, but instead start the loop. Through Looper, Handler, Message, MessageQueue and other components of Android Message processing mechanism, also called event, feedback mechanism.

### Why do you need such a messaging mechanism?

We know that every application has a main thread, and if the main thread keeps looping, our own code won’t execute. The system binds a Looper loop and message queue to the main thread, and the Looper acts as a pump to continuously send messages to the main thread. If there is no message mechanism, our code needs to directly access, operate, switch, access variables of the main thread and so on, which will bring unsafe problems, in addition, the difficulty of APP development will also increase, and is not conducive to the operation of the entire Android system. With messaging, we can simply send a message, then Looper sends the message to the main thread, and then we can execute.

Messages include: our own operation messages (client Handler) System operation messages (system Handler) : For example, we start the Activity and other four components (for example, jump to the phone interface when there is a sudden call). Our idea is to analyze the Handler of the system first, and then to deeply understand the various components of the message mechanism.

For example, broadcast: AMS sends a message to MessageQueue, then Looper loops, and the system’s Handler picks it up before processing it. (AMS is an important class that deals with the lifecycle of the four major components, which will be covered later when we examine the IPC mechanism and the Activity startup process.)

Where is the Handler for the system?

In the ActivityThread member variable there is a large H (inherited Handler) that is the system Handler:

final H mH = new H();
Copy the code

As you can see from the main method of an ActivityThread, the system’s Handler is initialized when a new ActivityThread is created. This is a loading method that initializes a member variable when the class is new. There is also lazy loading, which is initialization only when needed, which is common in the singleton design pattern.

public static void main(String[] args) { Environment.initForCurrentUser(); Looper.prepareMainLooper(); ActivityThread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } Looper.loop(); }Copy the code

Here’s a look at the definition of a system Handler (skip some cases while looking at it) :

    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));
    }
}
Copy the code

From the Handler of the system, we can see many operations about the life cycle of the four components in handleMessage, such as creation, destruction, switching, cross-process communication, including the destruction of the entire Application process, and so on. Let’s say we have an application A that uses Binder to start A Service of another application B (or A Service of A different process in the same application) across processes:

As shown in figure:

Finally, after AMS receives the message, it sends the message to MessageQueue. Finally, the Handler of the system handles the operation of starting the 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;
Copy the code

In handleCreateService newInstance() is executed by reflection, and the onCreate method of the Service is called back:

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 {/ / to create a Service by means of reflection. Java lang. This 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()); // The onCreate method of the Service is called 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); }}}Copy the code

Another example is that we can exit the application by sending SUICIDE messages.

case SUICIDE:
	Process.killProcess(Process.myPid());
	break;
Copy the code

##### Application exit process

In fact, when we exit the application, we terminate the main thread, in other words, the Looper loop. The Looper loop is terminated, so the lifecycle methods of the four major components may not execute, because the lifecycle methods of the four major components are handled by the Handler, and the Looper loop is gone. The onDestroy method is not necessarily callable.

case EXIT_APPLICATION: if (mInitialApplication ! = null) { mInitialApplication.onTerminate(); } // exit the Looper loop looper.mylooper ().quit(); break;Copy the code

This actually calls MessageQueue’s quit, emptying all messages.

public void quit() {
    mQueue.quit(false);
}
Copy the code

######tips: look at the source code must not panic, do not look line by line, to seize the core ideas to see.

### analysis of message mechanism

##### Message analysis of Message objects

Speaking of messaging mechanisms, inside MessageQueue is our Message object:

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++;
            }
        }
    }   
}
Copy the code

First, we can see that the Message object implements the Parcelable interface, because messages may need to communicate across processes, which requires process serialization and deserialization operations.

Message has some common arguments, arg1, arg2, obj callback when, etc. This is the target object, which is the Handler object that sends the message, and ultimately the message is processed by this Handler.

#####Message Pool The concept of a Message Pool — reusing messages

Message Pool Message Pool 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(); }Copy the code

When we obtain a Message and find that the current Message pool is not empty, we simply reuse messages (already created and handled). If empty, new a message. This is the concept of a meta-design pattern. For example, in a game, if a bullet is an object and a button is pressed to fire many bullets, then it is necessary to use the share mode to recycle.

As you can see from the above code, sPool always points to the header of the message pool. When retrieving a message, take the current header sPool, make sPool point to the next node, and return the node that was just removed, as shown in the following figure:

We learned above that messages can be created directly or recycled using the obtain method. So we need to get into the habit of recycling when we’re programming.

##### Message recycling mechanism

There is no Message creation without recycling, and there are two core methods related to Message recycling:

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++; }}}Copy the code

Discard the Message pool, empty the current Message, and next points to the current header pointer that points to the current Message object, inserting the current Message into the header of the Message pool.

Another thing to note about message recycling is that we don’t need to manually recycle messages when we write Handler, because Google engineers have already taken this into account. Messages are automatically recycled after being distributed by Handler: let’s go back to Looper’s loop method:

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; } / / process the message try {MSG. Target. DispatchMessage (MSG); } finally { } } msg.recycleUnchecked(); // Retrieve message}}Copy the code

MSG. Target. DispatchMessage (MSG) is to process the Message, and then at the end of the loop method calls the MSG. RecycleUnchecked recycle () this is the Message.

##### Message loop analysis

Let’s continue the loop:

If the current thread does not have a Looper, it will throw an exception. This is why creating a Handler in a child thread without manually creating and starting a Looper will result in an error.

2, Then get the Looper member variable MessageQueue, in MessageQueue continuously fetch messages, about MessageQueue next method is as follows:

Here you can see the message out to a few native methods, this is done in order to obtain higher efficiency, the message to remove is not straight out from the head of the queue, but the when time parameters according to the news, because we can send delay message, also can send the news of a specified point in time. So this function is a little bit complicated, so let’s just leave it at that.

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 SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; If (MSG!) {// if (MSG!) {if (MSG! = null && msg.target == null) { // Stalled by a barrier. Find the next asynchronous message in the queue. do { // prevMsg = MSG; msg = msg.next; } while (msg ! = null && ! msg.isAsynchronous()); } if (msg ! NextPollTimeoutMillis = (int) math.min (MSG. When - now, integer.max_value); } else {// At execution time, return mBlocked = false; if (prevMsg ! 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; }}Copy the code

3. Continue analyzing the loop method: If there are no more messages, you can exit the loop, and the entire application exits. When does that happen? Remember when we quit analytics?

When the system Handler receives the EXIT_APPLICATION message, Looper’s quit method is called:

case EXIT_APPLICATION: if (mInitialApplication ! = null) { mInitialApplication.onTerminate(); } Looper.myLooper().quit(); break;Copy the code

Looper’s quit method looks like this, which essentially calls the quit method of the message queue:

public void quit() {
    mQueue.quit(false);
}
Copy the code

The quit method actually emptying the message queue and exits the program if the message is empty in the Looper loop:

void quit(boolean safe) { if (! mQuitAllowed) { throw new IllegalStateException("Main thread not allowed to quit."); } synchronized (this) { if (mQuitting) { return; } // Set the exiting flag mstandard = true; / / to empty all messages if (safe) {/ / security (system), the future unhandled messages are removed removeAllFutureMessagesLocked (); } else {removeAllMessagesLocked();} else {// If the messages are not secure, such as those we define ourselves, remove them all at once; } // We can assume mPtr ! = 0 because mQuitting was previously false. nativeWake(mPtr); }}Copy the code

RemoveAllFutureMessagesLocked method is as follows:

private void removeAllFutureMessagesLocked() { final long now = SystemClock.uptimeMillis(); Message p = mMessages; if (p ! = null) {if (p.hen > now) {removeAllMessagesLocked(); } else {/ / through the for loop or get haven't haven't perform Message, use the do loop / / these unhandled messages through recycleUnchecked method recovery, back to the Message pool inside the 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); }}}Copy the code

4, MSG. Target. DispatchMessage (MSG) is to process the message, it will call the Handler dispatchMessage method:

public void dispatchMessage(Message msg) { if (msg.callback ! = null) { handleCallback(msg); } else { if (mCallback ! = null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); }}Copy the code

This method checks whether the Message callback is null, as defined in the Message class:

Runnable callback;
Copy the code

This is a Runnable object, and what the handleCallback method does is take the Runnable object and execute the run method on the thread created by the Handler (for example, the main thread) :

private static void handleCallback(Message message) {
    message.callback.run();
}
Copy the code

######Handler (Looper) is created on the same thread as the callback.

This is how we normally use the post series: Post, postAtFrontOfQueue, postAtTime, postDelayed and ultimately a Runnable wrapped by Message. Let’s look at one of them:

public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}
Copy the code

We can easily create a loop by Posting a Runnable, such as an AD Banner with unlimited rotation:

Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { } }; Runnable run = new Runnable() {@override public void run() {// // Continue the next rotation two seconds later, where tihs represents itself, i.e., the Runnable object mHandler.postDelayed(this, 2000); }}; Mhandler. postDelayed(run, 1000); Mhandler. removeCallbacks(run);Copy the code

Of course, our Handler could have an mCallback object of its own:

public interface Callback {
    public boolean handleMessage(Message msg);
}

final Callback mCallback;
Copy the code

If its own Callback is not empty, the Callback method is called. For example, we can create a Handler with a Callback:

Handler mHandler = new Handler(new Handler.Callback() {@override public Boolean handleMessage(Message MSG) { // The return value indicates whether to intercept the handleMessage written below the message. } }) { @Override public void handleMessage(Message msg) { } };Copy the code

If its own Callback does not return true after execution (no interception), then the handleMessage method that we often need to override is finally called, which by default is null:

public void handleMessage(Message msg) {

}
Copy the code

5, Finally, the recycle message: msg.recycleunchecked (). So we don’t need to recycle handleMessage manually after processing it. The system has already done that for us.

Message msg = Message.obtain(); Msg.recycle (); msg.recycle();Copy the code

This completes a message loop.

##### Message sending

After analyzing the distribution and processing of messages, we finally look at the sending of messages:

There are a series of methods for sending a Message, and even our series of POST methods (which encapsulate a Message with a Runnable) end up calling sendMessageAtTime to put the Message in a Message queue:

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);
}
Copy the code

MessageQueue’s queue entry method is as follows, the core idea is that the shorter the time (the more immediate the message needs to be executed), the closer to the position of the header pointer:

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; }Copy the code

Messages are not always added at the end of the queue, but can be timed, and if they need to be executed immediately, they are inserted at the head of the queue, processed immediately, and so on.

MessageQueue’s next method takes into account the time when variable of the message. Let’s review some of the core code in MessageQueue’s next method: The next method does not fetch the message directly from the header. Instead, it iterates through the message, retrieving the message based on the timestamp parameters and so on.

Message next() { for (;;) { if (nextPollTimeoutMillis ! = 0) { Binder.flushPendingCommands(); } nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { if (msg ! = null) {if (now < msg.when) {// If (now < msg.when) {// If (now < msg.when) {// If (now < msg.when) { NextPollTimeoutMillis = (int) math.min (msg.when - now, integer.max_value); } else {// Otherwise, pull a message mBlocked = false from the header of the message queue; 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; }}}}Copy the code

##### Thread binding to Looper

There is no Looper loop in a thread by default, so we need to call prepare to associate the thread with Looper:

Public static void prepareMainLooper() {public static void prepareMainLooper() { Because this is inside the main thread prepare(false); synchronized (Looper.class) { if (sMainLooper ! = null) { throw new IllegalStateException("The main Looper has already been prepared."); } // Set Looper to main thread Looper sMainLooper = myLooper(); }} private static void prepare(Boolean quitAllowed) {// Only one Looper can be bound to a thread. If (sthreadlocal.get ()! = null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }Copy the code

ThreadLocal’s set method is called and a new Looper is placed in it.

SThreadLocal static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); Set (new Looper(quitAllowed)) sthreadLocal. set(new Looper(quitAllowed));Copy the code

You can see that Looper is associated with a thread using a ThreadLocal, as shown below:

ThreadLocal is a class provided by the JDK to solve the problem of thread insecurity. The problem of thread insecurity is mainly related to the multithreaded access of variables, such as critical problems of variables, value errors, concurrency problems, etc. Using ThreadLocal to bind the Looper to the thread prevents other threads from accessing the current thread’s Looper.

A ThreadLocal can bind a Thread to a Looper with a get or set method, passing in a Value because thread.currentThread () is available:

public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map ! = null) map.set(this, value); else createMap(t, value); }Copy the code

Why can we bind threads now?

Map.set (this, value) puts itself (ThreadLocal and value (Looper) into a map, overwriting it if it puts another one, because the map does not allow duplicate keys in key-value pairs.

So ThreadLocal binds threads and Looper.

Because we actually put the variable (Looper in this case) into the Thread member variable Map, the key code is as follows:

ThreadLocalMap getMap(Thread t) {return t.htreadlocals; } / / this is a Thread class defined in the MAP ThreadLocal. ThreadLocalMap threadLocals = null;Copy the code

ThreadLocal’s getMap method actually gets the MAP of the thread. The underlying implementation is an array (in fact, the data structure is a hash list).

###### If the Android main thread Looper could be accessed by other threads, it would be very troublesome, haha, you know.

How are #####Handler and Looper related?

We know that Looper is associated with a thread (via ThreadLocal), and we normally use handlers like this:

Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {

    }
};
Copy the code

When the Handler builds, it has multiple overloaded methods, according to the chain of call relationships, so it will call the following constructor:

public Handler(Callback callback, boolean async) { mLooper = Looper.myLooper(); // If the current thread (child thread) has no 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; }Copy the code

When we construct a Handler, we use the Looper static method myLooper() to get a Looper object:

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
Copy the code

ThreadLocal gets the Looper of the current thread, so the Handler is tied to the thread, together, together.

###### We typically use handlers in activities, and the Activity lifecycle is called back to the main thread, so we typically use handlers tied to the main thread.

##### main thread is always looping, why is it not stuck, still can respond to our click and so on?

  1. Access the main thread through the child thread code, code injection, callback mechanism.
  2. Access the main thread by accessing the messages in the message queue, e.g. passing messages, and then calling back the lifecycle of the four major components.
  3. The IPC cross-process approach can also be implemented.

Although the main thread is always executing, we can execute our own code through external conditions, injection methods, rather than an endless loop.

# # #

As shown in the figure, in the main method entry of the main thread ActivityThread, we first create the system Handler (H), create the main thread Looper, bind the Looper to the main thread, call the loop method of Looper and start the main loop of the entire application. The Looper contains a message queue, which sends messages to the message queue through the Handler, and then circulates the messages to the Handler for processing. The system’s Handler, or Android message processing mechanism, ensures that the entire Android system works in an orderly manner, which is an important mechanism in the Android system.

Our APP can also create its own Handler, either in the main thread or in the child thread, but we need to manually create the child thread’s Looper and manually start the message loop.

Spent a day, the entire Android message mechanism source analysis is over here, today’s weather is really good, but I chose to learn Android message mechanism in my room, I always believe that there will be a harvest to pay!

### extension reading: On the proper use of Handler posture

Examples of typical use of errors:

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); } // Also anonymous inner class, MHandler = new Handler() {@override public void handleMessage(Message MSG) {switch (MSG. What) {  case 0: a = 20; break; }}}; }Copy the code

Analysis: This is our most common usage, where Handler implicitly refers to the Activity (via variable a). The Handler lifecycle may be different from the Activity lifecycle, such as sendEmptyMessageDelayed, which sends a message after 5000 milliseconds, but it is very likely that the Activity is returned at this point, This causes the Handler to outlive the Activity, which can cause the Activity to experience a temporary memory leak.

To solve this problem, we can change the Handler to static, but this will prevent the Handler from accessing the Activity’s non-static variable A

private static Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) {  case 0: //a = 20; // Can't access break; }}};Copy the code

Pose 2: Pass the Activity in as a Handler member variable during Handler construction. Instead of using anonymous inner classes, we need to extract Handler into a separate class that can access the Activity’s non-static variables. The Handler holds a strong reference to the Activity, so we’re back to where we started. (Memory leak still unresolved)

private static class UIHandler extends Handler { private LeakActivity mActivity; // A strong reference to the external class public UIHandler(LeakActivity Activity) {mActivity = activity; } @Override public void handleMessage(Message msg) { super.handleMessage(msg); mActivity.a = 20; }}Copy the code

Pose 3 (final version) : Use weak references to activities as member variables. Although we refer to the Activity as a weak reference, the Activity is not necessarily recycled during GC because there may be other objects that reference the Activity. When handling messages, be careful. When the Activity reclaims or is finishing, it can no longer process messages.

private static class UIHandler extends Handler { private WeakReference<LeakActivity> mActivityRef; Public UIHandler(LeakActivity Activity) {mActivityRef = new WeakReference<>(activity); // Public UIHandler(LeakActivity activity) {mActivityRef = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); // Will activities be recycled when weak references are used? // Just because there are no other objects that do not reference the Activity, it does not mean that it will not be recycled. LeakActivity activity = mActivityRef.get(); if (activity == null || activity.isFinishing()) { return; } mActivityRef.get().a = 20; }}Copy the code

For more information about using Handler, please refer to my friend’s post: Some postures for using Handler

If you feel that my words are helpful to you, welcome to pay attention to my public number:

My group welcomes everyone to come in and discuss all kinds of technical and non-technical topics. If you are interested, please add my wechat huannan88 and I will take you into our group.