The beginning of a question

Have you ever wondered if you can open a web page in your browser, and there’s a button on the page that invokes your App?

How does this effect work? The browser is an app; Why can one app call up another app’s page?

Speaking of cross-app page calls, can you think of a mechanism: implicit calls for activities?

First, implicit priming principle

Implicit invocation is the API we use when we need to call up another app’s page.

For example, we have an app that declares an Activity like this:

<activity android:name=".OtherActivity"
    android:screenOrientation="portrait">
    <intent-filter>
        <action android:name="mdove"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</activity>
Copy the code

Other apps want to start this Activity with a call like this:

val intent = Intent()
intent.action = "mdove"
startActivity(intent)
Copy the code

We don’t actively declare an Activity class, so how does the system find the corresponding Activity for us? In fact, this is the same as the normal Activity start process, but the if/else implementation is different.

Let’s review the Activity startup process. To avoid getting bogged down in detail, we’ll just expand the familiar class and call stack, focusing on the serial process.

1.1. Cross-process

First of all, we must be clear: whether it is an implicit boot or a display boot; Both in-app and out-of-app activities are cross-process. Take our example above, where one App wants to launch the page of another App.

Note that the process incubated by the system cannot be seen on a mobile phone without root. That’s why some code doesn’t break.

Those of you who have followed startActivity() will be familiar with the following call flow, and after following a few methods you find yourself in a class called ActivityTread.

What are the features of the ActivityTread class? We have main, which is our main thread.

Soon we can see a call to a more common class: Instrumentation:

// Activity.java
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) {
    mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options);
    / / to omit
}
Copy the code

Note that mInstrumentation#execStartActivity() has a yellow entry parameter, which is the inner class ApplicationThread in ActivityThread.

The ApplicationThread class implements iApplicationThread. Stub, which is aiDL’s “client-side callback for cross-process calls.”

Another famous call to mInstrumentation#execStartActivity() :

public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) {
    / / to omit...ActivityManager.getService() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target ! =null ? target.mEmbeddedID : null,
                requestCode, 0.null, options);
    return null;
}
Copy the code

We click on getService() to see the IActivityManager class marked in red.

It’s not a.Java file, it’s an AIDL file.

So ActivityManager. GetService () essentially returns an instance of the “server side of the process” interface, namely:

1.2, ActivityManagerService

public class ActivityManagerService extends IActivityManager.Stub

So execution at this point goes to the system process (system_process process). To omit the code details, take a look at the call stack:

From the above debug screenshot, you can see that relevant information of our target Activitiy has been obtained at this time.

Here is a simplification of the source code to get the target class, directly into the conclusion:

1.3, PackageManagerService

This class is equivalent to parsing all apKs in the phone and constructing their information into memory, as shown in the following figure:

Small tips: phone directory/data/system/packages. The XML, you can see all the apk path information, process name, permissions, etc.

1.4. Start a new process

The prerequisite for starting the target Activity is that the process of the target Activity is started. So the first time you want to start the target Activity, you start the process.

The code to start the process is in the method that starts the Activity:

ResumeTopActivityInnerLocked – > startProcessLocked.

Here comes another class that’s another big name: ZygoteInit. So basically, we’re going to start the App using ZygoteInit.

1.5, ApplicationThread

After the process starts, continue back to the startup process of the target Activity. There is still a series of system_processes going around and around before the IApplicationThread enters the target process.

Notice here that the callback to ActivityThread is again via IApplicationThread.

class H extends Handler {
    / / to omit
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case EXECUTE_TRANSACTION:
                final ClientTransaction transaction = (ClientTransaction) msg.obj;
                mTransactionExecutor.execute(transaction);
                / / to omit
                break;
            case RELAUNCH_ACTIVITY:
                handleRelaunchActivityLocally((IBinder) msg.obj);
                break;
        }
        / / to omit...}}/ / Callback execution
public void execute(ClientTransaction transaction) {
    final IBinder token = transaction.getActivityToken();
    executeCallbacks(transaction);
}
Copy the code

The so-called CallBack implementation is LaunchActivityItem#execute(), which corresponds to:

public void execute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) {
    ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
            mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
            mPendingResults, mPendingNewIntents, mIsForward,
            mProfilerInfo, client);
    client.handleLaunchActivity(r, pendingActions, null);
}
Copy the code

ActivityThread#handleLaunchActivity() now goes to our daily life cycle and the call stack looks like this:

The call chain in the screenshot above implies the Activity instantiation process (reflection) :

public @NonNull Activity instantiateActivity(@NonNull ClassLoader cl, @NonNull String className, @Nullable Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {

    return (Activity) cl.loadClass(className).newInstance();

}
Copy the code

Two, browser startup principle

A backflow page in a Helo site is a standard, browser-evoked example of another App.

2.1 Interactive process

HTML tags have an attribute href, for example: .

That is to jump to Baidu after the click.

Since this is a front-end tag, depending on the browser and its kernel implementation, jumping to a web page seems “natural”.

Of course, the interaction process here is basically the same as android: declare the Activity to be started in the way of implicit invocation; Then pass in the corresponding scheme. Such as:

Front page:

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
</head>
<body>
<a href="mdove1://haha">Start the OtherActivity</a>
</body>
Copy the code

Android statement:

<activity
    android:name=".OtherActivity"
    android:screenOrientation="portrait">
    <intent-filter>
        <data
            android:host="haha"
            android:scheme="mdove1" />
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.BROWSABLE" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>
Copy the code

2.2 Inference realization

The browser can load Scheme, which can be interpreted as the browser kernel encapsulation. Is it up to the browser kernel to make Scheme parsing available on Android?

Obviously, it’s impossible to build a mobile operating system and then let the browser implement it.

So you can probably guess that the browser app on the phone is doing the processing. Let’s take a look at the implementation of browser. Apk based on this conjecture.

2.3 browser implementation

Based on the above said/data/system/packages. The XML files, we can pull out of the browser. Apk.

Then jADX decompile browser.apk WebView related source code:

We can see that the href processing comes from an implicit jump, so everything is strung together with the above flow.

The end of the

A quick question at the end: If I write a WebView to load a front-end page, can I jump implicitly?

Finally, I am a color pen, all kinds of weird learning analysis in: