Before writing this blog post, let me ask you a question: What is the entry point to an Android app? I think a lot of people might say, application’s onCreate method, it’s not, even application has a method that executes before onCreate, This method is the attachBaseContext(Context Context) method: In general, this method can be used to subcontract injection of multiple dex, as shown in the following code:

@Override protected void attachBaseContext(Context base) { MultiDex.install(base); super.attachBaseContext(base); try { HandlerInject.hookHandler(base); } catch (Exception e) { e.printStackTrace(); }}Copy the code

And of course I’m just giving you an example of what this method does, it’s the method that gets executed immediately after the application initializes, and then the onCreate method, and of course the Application is not the entry point for an Android application, an Android application is a control class, just like a Java application, Has an entry, the entry is ActivityThread, ActiviyThread there is also a main method, the main method android applications true entrance, below I will detail ActivityThread unconventional methods and some problems, the article is long, Please take a look, I believe you will have a new understanding of android main thread.

First of all, what does ActivityThread do? Activitythreads perform various functions, but the main function is to schedule and perform activities, broadcasts, and other operations according to the requirements of ActivityManagerService (AMS) through the interface of iApplicationThreads. In Android, all four components run on the main thread by default. You will see how these components are managed in the following code analysis.

First, the main code for the ActivityThread entry is as follows:

PrepareMainLooper (); // Initialize Looper looper.prepareMainLooper (); // Create an ActivityThread object and bind it to AMS ActivityThread thread = new ActivityThread(); // Common applications are not system applications, so set it to false, where AMS thread. Attach (false) is attached; if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); // Open looper.loop (); throw new RuntimeException("Main thread loop unexpectedly exited");Copy the code
None of the above code is difficult, but it makes two things clear :1. Handler =new Handler() binds to the main thread by default because the above code does the Looper binding for the main thread for us, 2. The main thread Looper cannot be called exit in the program. See the last line of code, if it is called, it will throw an exception. The exit thread loop is called by the framework layer when the call exits the application, as discussed below.Copy the code
Final ApplicationThread mAppThread = new ApplicationThread(); //IBinder object. AMS holds a proxy object for this object and tells ActivityThread to manage other things. final Looper mLooper = Looper.myLooper(); final H mH = new H(); Final ArrayMap mActivities = new ArrayMap<>(); // Store all activities with IBinder as key. IBinder is the only representation of activities in the framework layer. Service final ArrayMap mServices = new ArrayMap<>(); Private static ActivityThread sCurrentActivityThread; private Static ActivityThread scurrenTactiyThread;Copy the code

Activitythreads interact with AMS and manage activities and services. How do you know which pages a user has visited in an app? Of course, there are many methods, but through the above code, you might be smart enough to find the field mActivities. Yes, because this field stores all the Activity objects, and you can get the value of this field to know which activities there are, and those activities are the ones that the user has stayed. That’s one solution.


 static final class ActivityClientRecord {
         
        IBinder token;
        int ident;
        Intent intent;
        String referrer;
        IVoiceInteractor voiceInteractor;
        Bundle state;
        PersistableBundle persistentState;
        
        Activity activity;
        Window window;
        Activity parent;
        String embeddedID;
        Activity.NonConfigurationInstances lastNonConfigurationInstances;
        boolean paused;
        boolean stopped;
        boolean hideForNow;
        Configuration newConfig;
        Configuration createdConfig;
        Configuration overrideConfig;
        
        private Configuration tmpConfig = new Configuration();
        ActivityClientRecord nextIdle;

        ProfilerInfo profilerInfo;

        ActivityInfo activityInfo;
        CompatibilityInfo compatInfo;
        LoadedApk packageInfo;

        List pendingResults;
        List pendingIntents;

        boolean startsNotResumed;
        boolean isForward;
        int pendingConfigChanges;
        boolean onlyLocalRequest;

        View mPendingRemoveWindow;
        WindowManager mPendingRemoveWindowManager;
    }Copy the code

ActivityClientRecord is an internal class of ActivtityThread. This ActivityClientRecord is a flag passed into AMS and carries a lot of information. As you can see from the code above, there is an Activity object in it. The Activity inside is a real Activity instance, and you can use it to know which pages the user is going to. Of course, it can be tedious, but this method is feasible.

ApplicationThread is not a thread. It is a Binder object.

private class ApplicationThread extends ApplicationThreadNative { public final void schedulePauseActivity(IBinder token,  boolean finished, boolean userLeaving, int configChanges, boolean dontReport) { sendMessage( finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY, token, (userLeaving ? 1 : 0) | (dontReport ? 2 : 0), configChanges); } public final void scheduleStopActivity(IBinder token, boolean showWindow, int configChanges) { sendMessage( showWindow ? H.STOP_ACTIVITY_SHOW : H.STOP_ACTIVITY_HIDE, token, 0, configChanges); } public abstract class ApplicationThreadNative extends Binder implements IApplicationThread /** * Cast a Binder object into an application thread interface, generating * a proxy if needed. */ static public IApplicationThread asInterface(IBinder obj) { if (obj == null) { return  null; } IApplicationThread in = (IApplicationThread)obj.queryLocalInterface(descriptor); if (in ! = null) { return in; } return new ApplicationThreadProxy(obj); }Copy the code

Binder is the Binder that wraps ApplicationThread into a proxy object, ApplicationThreadProxy. You might say, “How do you know it’s ApplicationThreadProxy?” In fact, this is a debug to know, below is the picture



See the red box? IApplictionThread is an ApplicationThreadProxy object in AMS

The schedulePauseActivity method is an Activity that is paused and executed. The schedulePauseActivity method is an IPC call. To be clear, AMS calls ActivityThread from the ApplicationThreadProxy object, while ActivityThread calls AMS from ActivityManagerProxy, which is also an IPC procedure call. There’s more code at the end. Okay, now you know that AMS calls ActivityThread from the ApplicationThreadProxy object, which usually starts with a schedule. For example, scheduleDestroyActivity scheduleReceiver, etc., a problem, The first method to execute after an Activity is paused is schedulePauseActivity. Then handlePauseActivity->performPauseActivity->callActivityOnPause-> handlePauseActivity->performPauseActivity->callActivityOnPause The activity.onpause () method is called late in the Activity lifecycle, scheduled by AMS, executed by the ActivityThread, and then called by the Activity itself.

The message distribution mechanism plays a very important role in the entire Android system. The Android system also relies on the message distribution mechanism to implement the operation of the system. In ActivityThread, H inherits the Handler class. Here are some variables:

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

As you can see, the constant represents the what value of the specific operation. Of course, there are other operations as well, which are listed below. Then we rewrite the handMessage method.

  public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what))
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart")
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo)
                    handleLaunchActivity(r, null)
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER)
                }Copy the code

Of course, the others are treated this way, not to list. See, LAUNCH_ACTIVITY handles the launching of an Activity, and then calls the handleLaunchActivity method, HandleLaunchActivity calls performLaunchActivity, the method that created the Activity,

Activity activity = null try { java.lang.ClassLoader cl = r.packageInfo.getClassLoader() activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent) StrictMode.incrementExpectedActivityCount(activity.getClass()) r.intent.setExtrasClassLoader(cl) r.intent.prepareToEnterProcess() if (r.state ! = null) { r.state.setClassLoader(cl) } }Copy the code

See, an Activity is just an ordinary Java object, created by reflection, loaded by the ClassLoader, and then called by the framework layer, which has a life cycle and becomes a component, so you can also see that in plug-ins, you can’t just load an Activity. It has to be called by the framework layer for it to be alive, otherwise it doesn’t make sense, and of course, not only activities, but services,BroadCase, etc. are created by reflection and then loaded by the framework layer, without exception,

activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor) if (customIntent ! = null) { activity.mIntent = customIntent } r.lastNonConfigurationInstances = null activity.mStartedActivity = false int  theme = r.activityInfo.getThemeResource() if (theme ! SetTheme (theme)} activity.mcalled = false if (r.isspersistable ()) {// Call the onCreate method mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState) } else { mInstrumentation.callActivityOnCreate(activity, r.state) }Copy the code

See, once you’ve created the Activity, you’re going to call attach, and you’re going to decide if you want to set the theme, If so, set the theme, and then call the mInstrumentation callActivityOnCreate, which is actually calling the onCreate method of the Activity, the Activity is created by Instrumentation, For an explanation of Instrumentation, see my last article:

Blog.csdn.net/shifuhetudi…

So now onCreate is called,

if (! R.activity. MFinished); // Use the onStart method activity.performStart() r.tivity = false} if (! r.activity.mFinished) { if (r.isPersistable()) { if (r.state ! = null || r.persistentState ! = null) { mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state, r.persistentState) } } else if (r.state ! = null) { mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state) } } if (! R.activity. MFinished) {activity.mcalled = false if (r.isspersistable ()) {// the OnPostCreate method is also a periodic method, Just don't general development mInstrumentation. CallActivityOnPostCreate (activity, r.s Tate. R.p ersistentState)} else {mInstrumentation. CallActivityOnPostCreate (activity, r.s Tate) / / store the activity here, You can know which activities there are when you get mActivities, which is the problem I mentioned above. How to count how many pages a user has been to? This method can be realized. mActivities.put(r.token, r)Copy the code

As you can see above, after executing the onCreate method, execute the onStart method, and then execute the onPostOnCreate method

Back to calling the handleLaunchActivity method, after executing the Activity’s onCreate,onStart life cycle methods, we come to the handleResumeActivity method,

if (r.window == null && ! a.mFinished && willBeVisible) { r.window = r.activity.getWindow() View decor = r.window.getDecorView() // Decor.setvisibility (view.invisible) ViewManager wm = A.getwindowManager () WindowManager.LayoutParams l = r.window.getAttributes() a.mDecor = decor l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION l.softInputMode |= forwardBit if (a.mVisibleFromClient) { A.windowadded = true // Very important here, add top devorView to wm.addView(decor, L)}....... If (state Richard armitage ctivity. MVisibleFromClient) {/ / execution here. R.activity. MakeVisible ()} // Tell the Activity manager we have resumed. If (reallyResume) {try { / / here to notify the AMS, the Activity has been Resume ActivityManagerNative. GetDefault () activityResumed (token)} catch (RemoteException ex) {}}Copy the code

See, at the time of execution onResume, actually it was not visible, until the execution of the state Richard armitage ctivity. MVisibleFromClient when the condition is really really visible, and we can also see from the code, in which the Activity is a View added to the window, Therefore, the Activity is essentially just a window, so that the Activity is really visible to the user, you can see that the above call stack is actually implemented by the Handler message distribution mechanism, step by step, through IPC to COMMUNICATE with AMS, do you feel that the Android system is extensive and profound? Binder allows Android to call remote processes as freely as it calls local processes. I have to admire Google’s god-like programmers, don’t you think? Of course, other Handler calls to distribute messages are the same, you can analyze other message calls, in fact, all Activity lifecycle, including other component calls are responsible for the distribution of calls by H.

public static ActivityThread currentActivityThread() { return sCurrentActivityThread; } public static String currentPackageName() { ActivityThread am = currentActivityThread(); return (am ! = null && am.mBoundApplication ! = null) ? am.mBoundApplication.appInfo.packageName : null; } public static String currentProcessName() { ActivityThread am = currentActivityThread(); return (am ! = null && am.mBoundApplication ! = null) ? am.mBoundApplication.processName : null; } public static Application currentApplication() { ActivityThread am = currentActivityThread(); return am ! = null ? am.mInitialApplication : null; } public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo, int flags) { return getPackageInfo(packageName, compatInfo, flags, UserHandle.myUserId()); }Copy the code

The above four methods are useful in some special situations and will be called in the following questions.

ActivityThread (IPC, AMS, ActivityThread); ActivityThread (IPC, AMS, ActivityThread)

private void handleReceiver(ReceiverData data) { LoadedApk packageInfo = getPackageInfoNoCheck( data.info.applicationInfo, data.compatInfo) IActivityManager mgr = ActivityManagerNative.getDefault() BroadcastReceiver receiver try { java.lang.ClassLoader cl = packageInfo.getClassLoader() data.intent.setExtrasClassLoader(cl) Data. The intent. PrepareToEnterProcess () data. SetExtrasClassLoader (cl) / / see, radio is founded by reflection, Receiver = (BroadcastReceiver) Cl.loadClass (Component).newinstance ()} ContextImpl context = (ContextImpl)app.getBaseContext() sCurrentBroadcastIntent.set(data.intent) receiver.setPendingResult(data) / / everyone know radio receiver. The callback method onReceive (context. GetReceiverRestrictedContext (), the data. The intent)}Copy the code

The only difference is that an Activity needs to be called by the framework layer and has a lifecycle method. Broadcast does not have the concept of a lifecycle method. It just calls back an onReceive method. Broadcast is easier to understand, but there is a question here first. Why can broadcast be called across processes, while EventBus, OTTO and other event buses cannot? In fact, the fundamental reason is that broadcast is uniformly managed by AMS system process, and our local process is only responsible for execution. However, EventBus and OTTO, whose EventBus processing is built between the same process, cannot send messages across processes, while broadcast can. Of course, AMS processing broadcast is also complicated, which will not be discussed today.

ActivityThread (ActivityThread)

private void handleCreateService(CreateServiceData data) { LoadedApk packageInfo = getPackageInfoNoCheck( data.info.applicationInfo, Data.com patInfo) Service Service = null try {Java. Lang. This cl = packageInfo. GetClassLoader () / / see, Service = (Service) cl.loadClass(data.info.name).newinstance ()}} 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 ()) / / execution service onCreate method. The onCreate () / / store service up mServices put (data) token, service) } }Copy the code

As you can see, the Service is not much different from the previous broadcast, it’s all reflection creation, and then the callback method, and then of course the last thing left is ContentProcider, which is actually reflection creation, and of course the ContentProcider process is a little bit more complicated,

private IActivityManager.ContentProviderHolder installProvider(Context context, IActivityManager.ContentProviderHolder holder, ProviderInfo info, boolean noisy, boolean noReleaseNeeded, boolean stable) { ContentProvider localProvider = null; IContentProvider provider; if (holder == null || holder.provider == null) { if (DEBUG_PROVIDER || noisy) { Slog.d(TAG, "Loading provider " + info.authority + ": " + info.name); } Context c = null; ApplicationInfo ai = info.applicationInfo; if (context.getPackageName().equals(ai.packageName)) { c = context; } else if (mInitialApplication ! = null && mInitialApplication.getPackageName().equals(ai.packageName)) { c = mInitialApplication; } else { try { c = context.createPackageContext(ai.packageName, Context.CONTEXT_INCLUDE_CODE); } catch (PackageManager.NameNotFoundException e) { } } if (c == null) { Slog.w(TAG, "Unable to get context for package " + ai.packageName + " while loading content provider " + info.name); return null; } try { final java.lang.ClassLoader cl = c.getClassLoader(); localProvider = (ContentProvider)cl. loadClass(info.name).newInstance(); provider = localProvider.getIContentProvider(); localProvider.attachInfo(c, info); }Copy the code

As you can see, all four components are created by reflection and execute the lifecycle methods, and AMS is responsible for scheduling, and ActivityThread is responsible for executing, so AMS is the scheduler, and the main thread is the executor, just like in a company, AMS is the board of directors, While ActivityThread is the CEO, responsible for performing specific things while reporting to AMS,AMS backup, for record keeping.

OK, that’s the ActivityThread. Of course, stopping and destroying an Activity is the same process as starting an Activity. It’s an IPC procedure call like AMS. Here is the ActivityThread workflow (AMS calls the ActivityThread method):

This is how AMS calls ActivityThread. Of course, ActivityThread calls AMS in the same way, except that the object is an ActivityManagerProxy object.

Finally, some problems in special cases of unconventional methods, interested can have a look:

1. How do you get global Application objects anytime, anywhere, any logic? A lot of people might say, well, you define a method in your custom Application, assign it to onCreate, and you get it, yeah, most people do, And seemed to never miss, yes, this method is the most people will use, however, this approach is actually made limited, of course in the normal development or no problem, but if can not contact the program logic itself (in reverse and other special cases), then the above methods are invalid, so is no way out? No, the code above says there is a method:

public static Application currentApplication() { ActivityThread am = currentActivityThread(); return am ! = null ? am.mInitialApplication : null; }Copy the code

ActivityThread is a hidden class.

private void getGlobalApplication()throws Exception{ Class clazz=Class.forName("android.app.ActivityThread"); Method method=clazz.getDeclaredMethod("currentApplication"); Application application= (Application) method.invoke(null); Log.d("[app]"," Pplication is :"+application); }Copy the code

The results are as follows:



You can see success, problem solved!

GetApplication () : getApplication () : getApplication () : getApplication () : getApplication () : getApplication () : getApplication () : getApplication () : getApplication () : getApplication () : getApplication () : getApplication (); An Activity variable is first defined in the base class and then assigned to a specific subclass in onCreate so that the subclass gets the Activity instance. This method is the simplest and is used in general.

The second method: using the method of application lifecycle to monitor ActivityLifecycleCallbacks, below is the specific code:







OK, the current ActivityThread instance has also been successfully obtained.

The third method:



When we were looking at the source code for ActivityThread, we found this method:

 public final Activity getActivity(IBinder token) {
        return mActivities.get(token).activity;
    }Copy the code

The IBinder parameter must be of type IBinder, which is the unique identifier of the Activity. Well, let’s see, when we started an Activity, we said that when we finished executing onResume, we would report it to AMS with a method called ActivityResume, and that method happens to have a parameter of type IBinder, which is the toke of the current Activity, If you have an Activity token, you can call the code above to get an Activity. AMS can get all sorts of code from an Activity process. Bind AMS to ActivityThread with Binder when ActivityThread calls AMS methods Binder is called with ActivitymanagerProxy, which is a subclass of ActivityManagerNative ActivityManagerService. Binder is called with ActivitymanagerProxy, which is a subclass of ActivityManagerService The VA layer is easy to understand, just remember that different processes work with each other’s proxy objects.

public abstract class ActivityManagerNative extends Binder implements IActivityManager { /** * Cast a Binder object into  an activity manager interface, generating * a proxy if needed. */ static public IActivityManager asInterface(IBinder obj) { if (obj == null) { return null; } IActivityManager in = (IActivityManager)obj.queryLocalInterface(descriptor); if (in ! = null) { return in; } return new ActivityManagerProxy(obj); } private static final Singleton gDefault = new Singleton() { protected IActivityManager create() { IBinder b = ServiceManager.getService("activity"); if (false) { Log.v("ActivityManager", "default service binder = " + b); } IActivityManager am = asInterface(b); if (false) { Log.v("ActivityManager", "default service = " + am); } return am; }}; }Copy the code

All we need to do is replace it with our own delegate object, and then we can intercept it in the activityResumed method, okay, let’s go,

public static void hookActivityManagerService() throws Throwable { Class activityManagerNativeClass=Class.forName("android.app.ActivityManagerNative"); Field gDefaultField=activityManagerNativeClass.getDeclaredField("gDefault"); gDefaultField.setAccessible(true); Object gDefault=gDefaultField.get(null); Class singleton=Class.forName("android.util.Singleton"); Field mInstance=singleton.getDeclaredField("mInstance"); mInstance.setAccessible(true); Object originalIActivityManager=mInstance.get(gDefault); Log.d("[app]","originalIActivityManager="+originalIActivityManager); Class iActivityManagerInterface=Class.forName("android.app.IActivityManager"); Object object= Proxy.newProxyInstance(iActivityManagerInterface.getClassLoader(), new Class[]{iActivityManagerInterface},new IActivityManagerServiceHandler(originalIActivityManager)); mInstance.set(gDefault,object); Log.d("[app]","Hook AMS successfully "); }Copy the code

IActivityManagerServiceHandler implementation class dynamic proxy interface InvocationHandler, inside intercepted activityResumed method, get a Token after intercept, Then call the reflection method to get an instance of the Activity.

public class IActivityManagerServiceHandler implements InvocationHandler { private Object base; public IActivityManagerServiceHandler(Object base) { this.base = base; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().equals("activityResumed")){ IBinder iBinder= (IBinder) args[0]; Log.d("[app]"," Toke = "+iBinder "); Class clazz=Class.forName("android.app.ActivityThread"); Method method1=clazz.getDeclaredMethod("currentActivityThread"); Object object=method1.invoke(null); Method getActivity=clazz.getDeclaredMethod("getActivity",IBinder.class); Activity mActivity= (Activity) getActivity.invoke(object,iBinder); Log.d("[app]","Hook AMS: current Activity :"+mActivity); } return method.invoke(base,args); }}Copy the code

And then I’ll inject it into the Application, okay, so let’s print it out,



By the way, the core idea of 360 plug-in is to deceive the system and deceive the system, so as to achieve a seamless plug-in system. Of course, some compatibility problems need to be dealt with, OK, the above problems have been solved.

3. Is there any way to interfere with the start of an Activity or other processes? Normally, you can’t, because this is the scheduling of the framework layer. You can dispatch in the framework layer, halfway out of the way to bite gold, see my last article for details:

Blog.csdn.net/shifuhetudi…

4. Is there a way to pop up toast when you start the Activity, or do something else, like pop toast or something else when you launch the LaunchActivity method? Well, normally I can’t do that, someone will say I pop up in onCreate, heh heh, that violates my condition, when I’m LaunchActivity, when AMS sends a message to ActivityThread to start Activiy and do something, Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler

/** * Handle system messages here. */ 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

See, when the handler processes a message, it checks to see if the callback interface is implemented, and if it is, it executes the interface method directly, then the handleMessage method, and finally the overridden handleMessage method. We override handleMessage most of the time, and the ActivityThread main thread uses the override method. This method has the lowest priority, and we can implement the interface instead of the system Handler. Here is the code:

public static void hookHandler(Context context) throws Exception {
        Class activityThreadClass = Class.forName("android.app.ActivityThread");
        Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
        currentActivityThreadMethod.setAccessible(true);
        
        Object activityThread = currentActivityThreadMethod.invoke(null);
        
        Field mH = activityThreadClass.getDeclaredField("mH");
        mH.setAccessible(true);
        
        Handler handler = (Handler) mH.get(activityThread);
        
        Field mCallBack = Handler.class.getDeclaredField("mCallback");
        mCallBack.setAccessible(true);
        
        mCallBack.set(handler, new CustomHandler(context, handler));
    }Copy the code

There is also the CustomHandler class, which implements the Callback interface and also intercepts methods

public class CustomHandler implements Callback { public static final int LAUNCH_ACTIVITY = 100; private Handler origin; private Context context; public CustomHandler(Context origin, Handler context) { this.context = origin; this.origin = context; } @Override public boolean handleMessage(Message msg) { if (msg.what == LAUNCH_ACTIVITY) { Toast.makeText( context.getApplicationContext(), "hello,I am going launch", Toast.LENGTH_SHORT).show(); } origin.handleMessage(msg); return false; }}Copy the code

Once you’re done, inject it into your application. OK, you can test to see if the string hello,I am going Launch pops up every time you start your Activity.

, of course, in addition to these special problems, some time, especially the pluggable, often encountered this kind of question, these problems are often at the application layer is unable to intercept, because when arrived at the application layer, the invocation chain is invoked, don’t have a chance to step in, this time you can consider these special methods, of course, development, Use whichever is the quickest way to solve the problem.

Today write here, little brother level is limited, the deficiency please point out, thank you for reading.