How many Application objects will an app create?

problem background

Recently, I discussed a technical issue with a group of friends:

exchange 1

一个应用开启了多进程,最终到底会创建几个application对象,执行几次onCreate()方法?

Some group of friends gave conjectures based on their own ideas

exchange 2

Some group friends even directly consulted ChatGPT

chatgpt1.jpg

But there is no final conclusion from beginning to end. So, in order to clarify this problem, I decided to write a demo test to draw a conclusion, and then analyze the reason from the source code

demo verification

First create an app project and start multi-process

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        android:name=".DemoApplication"
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Demo0307"
        tools:targetApi="31">
        <!--android:process 开启多进程并设置进程名-->
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:process=":remote">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

        </activity>
    </application>

</manifest>
复制代码

Then print the address of the application object and the name of the current process in the onCreate() method of DemoApplication

public class DemoApplication extends Application {
    private static final String TAG = "jasonwan";

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "Demo application onCreate: " + this + ", processName=" + getProcessName(this));
    }

    private String getProcessName(Application app) {
        int myPid = Process.myPid();
        ActivityManager am = (ActivityManager) app.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> runningAppProcesses = am.getRunningAppProcesses();
        for (ActivityManager.RunningAppProcessInfo runningAppProcess : runningAppProcesses) {
            if (runningAppProcess.pid == myPid) {
                return runningAppProcess.processName;
            }
        }
        return "null";
    }
}
复制代码

Run, the obtained log is as follows

2023-03-07 11:15:27.785 19563-19563/com.jason.demo0307 D/jasonwan: Demo application onCreate: com.jason.demo0307.DemoApplication@fb06c2d, processName=com.jason.demo0307:remote
复制代码

View all processes of the current application

View process 1

It means that the app has only one process at this time, and only one application object, and the object address is @fb06c2d

Now we increase the process to multiple and see how it goes

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        android:name=".DemoApplication"
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Demo0307"
        tools:targetApi="31">
        <!--android:process 开启多进程并设置进程名-->
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:process=":remote">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

        </activity>
        <activity
            android:name=".TwoActivity"
            android:process=":remote2" />
        <activity
            android:name=".ThreeActivity"
            android:process=":remote3" />
        <activity
            android:name=".FourActivity"
            android:process=":remote4" />
        <activity
            android:name=".FiveActivity"
            android:process=":remote5" />
    </application>

</manifest>
复制代码

The logic is click MainActivityto launch TwoActivity, click TwoActivityto launch ThreeActivity, and so on. Finally, we run and start all the activities to get the following logs

2023-03-07 11:25:35.433 19955-19955/com.jason.demo0307 D/jasonwan: Demo application onCreate: com.jason.demo0307.DemoApplication@fb06c2d, processName=com.jason.demo0307:remote
2023-03-07 11:25:43.795 20001-20001/com.jason.demo0307 D/jasonwan: Demo application onCreate: com.jason.demo0307.DemoApplication@fb06c2d, processName=com.jason.demo0307:remote2
2023-03-07 11:25:45.136 20046-20046/com.jason.demo0307 D/jasonwan: Demo application onCreate: com.jason.demo0307.DemoApplication@fb06c2d, processName=com.jason.demo0307:remote3
2023-03-07 11:25:45.993 20107-20107/com.jason.demo0307 D/jasonwan: Demo application onCreate: com.jason.demo0307.DemoApplication@fb06c2d, processName=com.jason.demo0307:remote4
2023-03-07 11:25:46.541 20148-20148/com.jason.demo0307 D/jasonwan: Demo application onCreate: com.jason.demo0307.DemoApplication@fb06c2d, processName=com.jason.demo0307:remote5
复制代码

View all processes of the current application

View process 2

At this time, the app has 5 processes, but the address of the application object is @fb06c2d, and the same address means that they are the same object.

Can it be concluded that no matter how many processes are started, only one application object will be created? It is not possible to jump to this conclusion. We will remove MainActivitythe processattribute and run it again. The log obtained is as follows

2023-03-07 11:32:10.156 20318-20318/com.jason.demo0307 D/jasonwan: Demo application onCreate: com.jason.demo0307.DemoApplication@5d49e29, processName=com.jason.demo0307
2023-03-07 11:32:15.143 20375-20375/com.jason.demo0307 D/jasonwan: Demo application onCreate: com.jason.demo0307.DemoApplication@fb06c2d, processName=com.jason.demo0307:remote2
2023-03-07 11:32:16.477 20417-20417/com.jason.demo0307 D/jasonwan: Demo application onCreate: com.jason.demo0307.DemoApplication@fb06c2d, processName=com.jason.demo0307:remote3
2023-03-07 11:32:17.582 20463-20463/com.jason.demo0307 D/jasonwan: Demo application onCreate: com.jason.demo0307.DemoApplication@fb06c2d, processName=com.jason.demo0307:remote4
2023-03-07 11:32:18.882 20506-20506/com.jason.demo0307 D/jasonwan: Demo application onCreate: com.jason.demo0307.DemoApplication@fb06c2d, processName=com.jason.demo0307:remote5
复制代码

View all processes of the current application

View process 3

At this time, the app has 5 processes, but there are 2 application objects, the object addresses are @5d49e29 and @fb06c2d, and the application objects of the child processes are the same.

The parent process ID of all the above processes is 678, and this process is the zygote process

zygote process

According to the above test results, we can draw the following conclusions:

  • Conclusion 1: A single process only creates one Applicationobject and executes the method once onCreate();
  • Conclusion 2: Multi-process creates at least 2 Applicationobjects, executes onCreate()the method multiple times, and executes several times for several processes;

结论2为什么说至少创建2个,因为我在集成了JPush的商业项目中测试发现,JPush创建的进程跟我自己创建的进程,Application地址是不同的。

jpush process

这里三个进程,分别创建了三个Application对象,对象地址分别是@f31ba9d,@2c586f3,@fb06c2d

源码分析

这里需要先了解App的启动流程,具体可以参考《App启动流程》

Application的创建位于frameworks/base/core/java/android/app/ActivityThread.javahandleBindApplication()方法中

	@UnsupportedAppUsage
    private void handleBindApplication(AppBindData data) {
        long st_bindApp = SystemClock.uptimeMillis();
        //省略部分代码

        // Note when this process has started.
        //设置进程启动时间
        Process.setStartTimes(SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());

        //省略部分代码

        // send up app name; do this *before* waiting for debugger
        //设置进程名称
        Process.setArgV0(data.processName);
        //省略部分代码
        
        // Allow disk access during application and provider setup. This could
        // block processing ordered broadcasts, but later processing would
        // probably end up doing the same disk access.
        Application app;
        final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();
        final StrictMode.ThreadPolicy writesAllowedPolicy = StrictMode.getThreadPolicy();
        try {
            // If the app is being launched for full backup or restore, bring it up in
            // a restricted environment with the base application class.
            //此处开始创建application对象,注意参数2为null
            app = data.info.makeApplication(data.restrictedBackupMode, null);

            //省略部分代码
            try {
                if ("com.jason.demo0307".equals(app.getPackageName())){
                    Log.d("jasonwan", "execute app onCreate(), app=:"+app+", processName="+getProcessName(app)+", pid="+Process.myPid());
                }
                //执行application的onCreate方法()
                mInstrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
                if (!mInstrumentation.onException(app, e)) {
                    throw new RuntimeException(
                      "Unable to create application " + app.getClass().getName()
                      + ": " + e.toString(), e);
                }
            }
        } finally {
            // If the app targets < O-MR1, or doesn't change the thread policy
            // during startup, clobber the policy to maintain behavior of b/36951662
            if (data.appInfo.targetSdkVersion < Build.VERSION_CODES.O_MR1
                    || StrictMode.getThreadPolicy().equals(writesAllowedPolicy)) {
                StrictMode.setThreadPolicy(savedPolicy);
            }
        }
        //省略部分代码
    }
复制代码

实际创建过程在frameworks/base/core/java/android/app/LoadedApk.java中的makeApplication()方法中,LoadedApk顾名思义就是加载好的Apk文件,里面包含Apk所有信息,像包名、Application对象,app所在的目录等,这里直接看application的创建过程

	@UnsupportedAppUsage
    public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
        if ("com.jason.demo0307".equals(mApplicationInfo.packageName)) {
            Log.d("jasonwan", "makeApplication: mApplication="+mApplication+", pid="+Process.myPid());
        }
        //如果已经创建过了就不再创建
        if (mApplication != null) {
            return mApplication;
        }

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");

        Application app = null;

        String appClass = mApplicationInfo.className;
        if (forceDefaultAppClass || (appClass == null)) {
            appClass = "android.app.Application";
        }

        try {
            java.lang.ClassLoader cl = getClassLoader();
            if (!mPackageName.equals("android")) {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                        "initializeJavaContextClassLoader");
                initializeJavaContextClassLoader();
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            //反射创建application对象
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            if ("com.jason.demo0307.DemoApplication".equals(appClass)){
                Log.d("jasonwan", "create application, app="+app+", processName="+mActivityThread.getProcessName()+", pid="+Process.myPid());
            }
            appContext.setOuterContext(app);
        } catch (Exception e) {
            Log.d("jasonwan", "fail to create application, "+e.getMessage());
            if (!mActivityThread.mInstrumentation.onException(app, e)) {
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                throw new RuntimeException(
                    "Unable to instantiate application " + appClass
                    + ": " + e.toString(), e);
            }
        }
        mActivityThread.mAllApplications.add(app);
        mApplication = app;

        if (instrumentation != null) {
            try {
                //第一次启动创建时,instrumentation为null,不会执行onCreate()方法
                instrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
                if (!instrumentation.onException(app, e)) {
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    throw new RuntimeException(
                        "Unable to create application " + app.getClass().getName()
                        + ": " + e.toString(), e);
                }
            }
        }

        // 省略部分代码
        return app;
    }
复制代码

为了看清application到底被创建了几次,我在关键地方埋下了log,TAG为jasonwan的log是我自己加的,编译验证,得到如下log

启动app,进入MainActivity
03-08 17:20:29.965  4069  4069 D jasonwan: makeApplication: mApplication=null, pid=4069
//创建application对象,地址为@c2f8311,当前进程id为4069
03-08 17:20:29.967  4069  4069 D jasonwan: create application, app=com.jason.demo0307.DemoApplication@c2f8311, processName=com.jason.demo0307, pid=4069
03-08 17:20:29.988  4069  4069 D jasonwan: execute app onCreate(), app=:com.jason.demo0307.DemoApplication@c2f8311, processName=com.jason.demo0307, pid=4069
03-08 17:20:29.989  4069  4069 D jasonwan: DemoApplication=com.jason.demo0307.DemoApplication@c2f8311, processName=com.jason.demo0307, pid=4069
03-08 17:20:36.614  4069  4069 D jasonwan: makeApplication: mApplication=com.jason.demo0307.DemoApplication@c2f8311, pid=4069

点击MainActivity,跳转到TwoActivity
03-08 17:20:39.686  4116  4116 D jasonwan: makeApplication: mApplication=null, pid=4116
//创建application对象,地址为@c2f8311,当前进程id为4116
03-08 17:20:39.687  4116  4116 D jasonwan: create application, app=com.jason.demo0307.DemoApplication@c2f8311, processName=com.jason.demo0307:remote2, pid=4116
03-08 17:20:39.688  4116  4116 D jasonwan: execute app onCreate(), app=:com.jason.demo0307.DemoApplication@c2f8311, processName=com.jason.demo0307:remote2, pid=4116
03-08 17:20:39.688  4116  4116 D jasonwan: DemoApplication=com.jason.demo0307.DemoApplication@c2f8311, processName=com.jason.demo0307:remote2, pid=4116
03-08 17:20:39.733  4116  4116 D jasonwan: makeApplication: mApplication=com.jason.demo0307.DemoApplication@c2f8311, pid=4116

点击TwoActivity,跳转到ThreeActivity
03-08 17:20:41.473  4147  4147 D jasonwan: makeApplication: mApplication=null, pid=4147
//创建application对象,地址为@c2f8311,当前进程id为4147
03-08 17:20:41.475  4147  4147 D jasonwan: create application, app=com.jason.demo0307.DemoApplication@c2f8311, processName=com.jason.demo0307:remote3, pid=4147
03-08 17:20:41.475  4147  4147 D jasonwan: execute app onCreate(), app=:com.jason.demo0307.DemoApplication@c2f8311, processName=com.jason.demo0307:remote3, pid=4147
03-08 17:20:41.476  4147  4147 D jasonwan: DemoApplication=com.jason.demo0307.DemoApplication@c2f8311, processName=com.jason.demo0307:remote3, pid=4147
03-08 17:20:41.519  4147  4147 D jasonwan: makeApplication: mApplication=com.jason.demo0307.DemoApplication@c2f8311, pid=4147

点击ThreeActivity,跳转到FourActivity
03-08 17:20:42.966  4174  4174 D jasonwan: makeApplication: mApplication=null, pid=4174
//创建application对象,地址为@c2f8311,当前进程id为4174
03-08 17:20:42.968  4174  4174 D jasonwan: create application, app=com.jason.demo0307.DemoApplication@c2f8311, processName=com.jason.demo0307:remote4, pid=4174
03-08 17:20:42.969  4174  4174 D jasonwan: execute app onCreate(), app=:com.jason.demo0307.DemoApplication@c2f8311, processName=com.jason.demo0307:remote4, pid=4174
03-08 17:20:42.969  4174  4174 D jasonwan: DemoApplication=com.jason.demo0307.DemoApplication@c2f8311, processName=com.jason.demo0307:remote4, pid=4174
03-08 17:20:43.015  4174  4174 D jasonwan: makeApplication: mApplication=com.jason.demo0307.DemoApplication@c2f8311, pid=4174

点击FourActivity,跳转到FiveActivity
03-08 17:20:44.426  4202  4202 D jasonwan: makeApplication: mApplication=null, pid=4202
//创建application对象,地址为@c2f8311,当前进程id为4202
03-08 17:20:44.428  4202  4202 D jasonwan: create application, app=com.jason.demo0307.DemoApplication@c2f8311, processName=com.jason.demo0307:remote5, pid=4202
03-08 17:20:44.429  4202  4202 D jasonwan: execute app onCreate(), app=:com.jason.demo0307.DemoApplication@c2f8311, processName=com.jason.demo0307:remote5, pid=4202
03-08 17:20:44.430  4202  4202 D jasonwan: DemoApplication=com.jason.demo0307.DemoApplication@c2f8311, processName=com.jason.demo0307:remote5, pid=4202
03-08 17:20:44.473  4202  4202 D jasonwan: makeApplication: mApplication=com.jason.demo0307.DemoApplication@c2f8311, pid=4202
复制代码

结果很震惊,我们在5个进程中创建的application对象,地址均为@c2f8311,也就是至始至终创建的都是同一个Application对象,那么上面的结论2显然并不成立,只是测试的偶然性导致的。

可真的是这样子的吗,这也太颠覆我的三观了,为此我跟群友讨论了这个问题:

不同进程中的多个对象,内存地址相同,是否代表这些对象都是同一个对象?

The idea of ​​group friends is that all the virtual memory addresses are obtained in java. The same virtual memory address does not mean the same object. The same physical memory address must be the same to represent the same memory space, which means the same object. , there is a mapping relationship between the physical memory address and the virtual memory address. At the same time, the method for obtaining the physical memory address in java is given. Android obtains the object address , mainly using Unsafethis class to operate. One function of this class is to directly access system memory resources. For a detailed description, see the magic class in Java-Unsafe . Because this operation is unsafe, it is marked as private, but we can call this API through reflection, and then I went to ask the boss of the department who engages in registers. I affirmed the idea of ​​the group friends, so I added the code and tried to get the physical memory address of the object to see if it was the same

public class DemoApplication extends Application {
    public static final String TAG = "jasonwan";

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "DemoApplication=" + this + ", address=" + addressOf(this) + ", pid=" + Process.myPid());
    }

    //获取对象的真实物理地址
    public static long addressOf(Object o) {
        Object[] array = new Object[]{o};
        long objectAddress = -1;
        try {
            Class cls = Class.forName("sun.misc.Unsafe");
            Field field = cls.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            Object unsafe = field.get(null);
            Class unsafeCls = unsafe.getClass();
            Method arrayBaseOffset = unsafeCls.getMethod("arrayBaseOffset", Object.class.getClass());
            int baseOffset = (int) arrayBaseOffset.invoke(unsafe, Object[].class);
            Method size = unsafeCls.getMethod("addressSize");
            int addressSize = (int) size.invoke(unsafe);
            switch (addressSize) {
                case 4:
                    Method getInt = unsafeCls.getMethod("getInt", Object.class, long.class);
                    objectAddress = (int) getInt.invoke(unsafe, array, baseOffset);
                    break;
                case 8:
                    Method getLong = unsafeCls.getMethod("getLong", Object.class, long.class);
                    objectAddress = (long) getLong.invoke(unsafe, array, baseOffset);
                    break;
                default:
                    throw new Error("unsupported address size: " + addressSize);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return objectAddress;
    }
}
复制代码

The following log is obtained after running

2023-03-10 11:01:54.043 6535-6535/com.jason.demo0307 D/jasonwan: DemoApplication=com.jason.demo0307.DemoApplication@930d275, address=8050489105119022792, pid=6535
2023-03-10 11:02:22.610 6579-6579/com.jason.demo0307 D/jasonwan: DemoApplication=com.jason.demo0307.DemoApplication@331b3b9, address=8050489105119027136, pid=6579
2023-03-10 11:02:36.369 6617-6617/com.jason.demo0307 D/jasonwan: DemoApplication=com.jason.demo0307.DemoApplication@331b3b9, address=8050489105119029912, pid=6617
2023-03-10 11:02:39.244 6654-6654/com.jason.demo0307 D/jasonwan: DemoApplication=com.jason.demo0307.DemoApplication@331b3b9, address=8050489105119032760, pid=6654
2023-03-10 11:02:40.841 6692-6692/com.jason.demo0307 D/jasonwan: DemoApplication=com.jason.demo0307.DemoApplication@331b3b9, address=8050489105119036016, pid=6692
2023-03-10 11:02:52.429 6729-6729/com.jason.demo0307 D/jasonwan: DemoApplication=com.jason.demo0307.DemoApplication@331b3b9, address=8050489105119038720, pid=6729
复制代码

It can be seen that although the virtual memory addresses of Application are the same, both are 331b3b9, their real physical addresses are different. So far, we can draw the final conclusion :

  • Single process, create 1 application object, execute onCreate()method once
  • Multi-process (N), create N application objects, and execute onCreate()methods N times

Guess you like

Origin juejin.im/post/7208345469658415159