为什么ActivityThread中有个mAllApplications?

一直想不通ActivityThread中为什么会有个 final ArrayList<Application> mAllApplications = new ArrayList<Application>();字段 ,mAllApplicationsmInitialApplication又是什么关系,翻了下源码,也没找到为什么还需要个mAllApplications字段。

一个应用程序虽然可以开启多个进程,每一个进程创建时都会创建一个Application对象,但一个进程中也只有一个Application对象,为什么需要一个mAllApplications保存Application对象呢

首先理一下应用的启动流程:

  • AMS通过socket告知Zygote fork进程,并让Zygote转达给新进程启动后执行ActivityThread.main方法
  • Zygote收到AMS发来的通知,然后开始fork进程
  • 进程fork完成后,新进程自动执行ActivityThread的main方法
  • ActivityThread的main方法中new了ActivityThread对象
  • ActivityThread构造的同时创建了一个用于AMS和自身通信的ApplicationThread binder对象
  • 应用进程调用AMS,并把自己刚创建的ApplicationThread对象作为参数告诉AMS自己已经启动了(此时应用进程还是个小白,啥都不知道,一脸懵逼,接下在要做什么,类从哪加载,屏幕大小,自己的进程名等等完全不知道)
  • AMS收到了刚创建的应用进程发来的通知,取出了ApplicationThread对象,然后把应用进程需要加载的类路径,屏幕参数,进程名称,是否需要调试还有一些其他的配置信息通过ApplicationThread这个binder对象发送给了应用进程
  • 应用进程的ApplicationThread对象收到了AMS发来的数据,并保存了起来,然后设置自己的进程名,创建Application对象,具体逻辑如下
AMS发送的消息最终调用了ActivityThread中的ApplicationThread的bindApplication方法,由于是在子线程当中的,所以需要handler转移到主线程去执行

        public final void bindApplication(String processName, ApplicationInfo appInfo,
                List<ProviderInfo> providers, ComponentName instrumentationName,
                ProfilerInfo profilerInfo, Bundle instrumentationArgs,
                IInstrumentationWatcher instrumentationWatcher,
                IUiAutomationConnection instrumentationUiConnection, int debugMode,
                boolean enableBinderTracking, boolean trackAllocation,
                boolean isRestrictedBackupMode, boolean persistent, Configuration config,
                CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
                String buildSerial, boolean autofillCompatibilityEnabled) {

            if (services != null) {
                if (false) {
                    // Test code to make sure the app could see the passed-in services.
                    for (Object oname : services.keySet()) {
                        if (services.get(oname) == null) {
                            continue; // AM just passed in a null service.
                        }
                        String name = (String) oname;

                        // See b/79378449 about the following exemption.
                        switch (name) {
                            case "package":
                            case Context.WINDOW_SERVICE:
                                continue;
                        }

                        if (ServiceManager.getService(name) == null) {
                            Log.wtf(TAG, "Service " + name + " should be accessible by this app");
                        }
                    }
                }

                // Setup the service cache in the ServiceManager
                ServiceManager.initServiceCache(services);
            }

            setCoreSettings(coreSettings);

            AppBindData data = new AppBindData();
            data.processName = processName;
            data.appInfo = appInfo;
            data.providers = providers;
            data.instrumentationName = instrumentationName;
            data.instrumentationArgs = instrumentationArgs;
            data.instrumentationWatcher = instrumentationWatcher;
            data.instrumentationUiAutomationConnection = instrumentationUiConnection;
            data.debugMode = debugMode;
            data.enableBinderTracking = enableBinderTracking;
            data.trackAllocation = trackAllocation;
            data.restrictedBackupMode = isRestrictedBackupMode;
            data.persistent = persistent;
            data.config = config;
            data.compatInfo = compatInfo;
            data.initProfilerInfo = profilerInfo;
            data.buildSerial = buildSerial;
            data.autofillCompatibilityEnabled = autofillCompatibilityEnabled;
            sendMessage(H.BIND_APPLICATION, data);
        }

发送到主线程后开始执行如下方法创建Application对象

 private void handleBindApplication(AppBindData data) {
        //根据AMS传过来的数据设置进程名
        Process.setArgV0(data.processName);
    
        //创建一个LoadedApk对象,其内部保存了AMS传过来的类文件的位置,并且会根据这些路径信息创建一个PathClassLoader对象用于加载我们应用apk包中的class
        data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
 
        //根据AMS传过来的数据设置图片密度
        if ((data.appInfo.flags&ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES)
                == 0) {
            mDensityCompatMode = true;
            Bitmap.setDefaultDensity(DisplayMetrics.DENSITY_DEFAULT);
        }
        
  
        //AMS控制是否需要调试,当你在系统设置的开发者选项中选择当前应用需要调试的话,当启动该应用时,AMS会通过data.debugMode告知应用进程需要调试,然后应用进程会调用AMS的showWaitingForDebugger(),这时你就看到了哪个wait deubg...弹窗
        if (data.debugMode != ApplicationThreadConstants.DEBUG_OFF) {
            // XXX should have option to change the port.
            Debug.changeDebugPort(8100);
            if 

                IActivityManager mgr = ActivityManager.getService();
                try {
                    mgr.showWaitingForDebugger(mAppThread, true);
                } catch (RemoteException ex) {
                    throw ex.rethrowFromSystemServer();
                }

                Debug.waitForDebugger();

                try {
                    mgr.showWaitingForDebugger(mAppThread, false);
                } catch (RemoteException ex) {
                    throw ex.rethrowFromSystemServer();
                }

            }  
        }

        //根据AMS传过来的数据设置是否开启大内存,由你在应用的manifest.xml配置的largeHeap决定
        if ((data.appInfo.flags&ApplicationInfo.FLAG_LARGE_HEAP) != 0) {
            dalvik.system.VMRuntime.getRuntime().clearGrowthLimit();
        } else {
            // Small heap, clamp to the current growth limit and let the heap release
            // pages after the growth limit to the non growth limit capacity. b/18387825
            dalvik.system.VMRuntime.getRuntime().clampGrowthLimit();
        }
 
        Application app;
            //用刚才提到的的LoadedApk对象中的classLoader加载应用自己的Application类,并反射创建Application对象
            app = data.info.makeApplication(data.restrictedBackupMode, null);

            
            mInitialApplication = app;

data.info.makeApplication(data.restrictedBackupMode, null);

 public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
        
        Application app = null;
        //获取AMS传过来的Application 类名称,也就是我们在manifest.xml的application节点配置的name值
        String appClass = mApplicationInfo.className;
        if (forceDefaultAppClass || (appClass == null)) {
            appClass = "android.app.Application";
        }

        try {
        //这个cl就是上面提到的根据AMS传过来的类路径所构造的PathClassLoader
            java.lang.ClassLoader cl = getClassLoader();
            //反射创建Application对象  即:(Application) cl.loadClass(appClass).newInstance()
           app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            
        }
        mActivityThread.mAllApplications.add(app);
        mApplication = app;

        return app;
    }        

整个启动流程理了一遍也没想通为什么需要一个list保存Application。

不过今天突然想到多个应用可以运行在同一个进程中!

为此我们按照如下逻辑测试一下:

定义应用1的Application

package com.wanjian.shareprocessapp1;
import android.app.Application;
public class ShareProApp1 extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
    }
}

配置应用1的manifest文件

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.wanjian.shareprocessapp1"
    android:sharedUserId="com.wanjian.share">

    <application
        android:name=".ShareProApp1"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity"
            android:process="com.wanjian.shareprocess">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>

在应用1的MainActivity中点击按钮后获取系统类加载器,应用类加载器,主线程hash值,mAllApplications中对象


    private void getApplications() {
        System.out.println("系统类加载器:" + Activity.class.getClassLoader());
        System.out.println("应用类加载器:" + getClassLoader().hashCode() + ":" + getClassLoader());
        System.out.println("主线程:" + Thread.currentThread().hashCode());
        try {
            Class atClz = Class.forName("android.app.ActivityThread");

            Method method = atClz.getDeclaredMethod("currentActivityThread", null);
            Object at = method.invoke(null);

            Field field = atClz.getDeclaredField("mAllApplications");

            field.setAccessible(true);


            ArrayList list = (ArrayList) field.get(at);

            for (Object o : list) {
                System.out.println("shareprocessapp1:" + o);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

应用进程2类似

package com.wanjian.shareprocessapp2;

import android.app.Application;

public class ShareProApp2 extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

    }
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.wanjian.shareprocessapp2"
    android:sharedUserId="com.wanjian.share">

    <application
        android:name=".ShareProApp2"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity"
            android:process="com.wanjian.shareprocess">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>
private void getApplications() {
        System.out.println("系统类加载器:" + Activity.class.getClassLoader());
        System.out.println("应用类加载器:" + getClassLoader().hashCode() + ":" + getClassLoader());
        System.out.println("主线程:" + Thread.currentThread().hashCode());
        try {
            Class atClz = Class.forName("android.app.ActivityThread");

            Method method = atClz.getDeclaredMethod("currentActivityThread", null);
            Object at = method.invoke(null);

            Field field = atClz.getDeclaredField("mAllApplications");

            field.setAccessible(true);


            ArrayList list = (ArrayList) field.get(at);

            for (Object o : list) {
                System.out.println("shareprocessapp2:" + o);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

两个应用代码基本是一样的,唯一区别就是包名不同,Application类名不同,其中都指定了 android:sharedUserId="com.wanjian.share"android:process="com.wanjian.shareprocess",这样的话两个应用的MainActivity就可以运行在同一进程了(当然签名也要相同)。然后我们分别点击按钮输出如下

应用1输出
系统类加载器:java.lang.BootClassLoader@d785773
应用类加载器:264795041:dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.wanjian.shareprocessapp1-phr0dw65OS5vg61BueISiQ==/base.apk"],nativeLibraryDirectories=[/data/app/com.wanjian.shareprocessapp1-phr0dw65OS5vg61BueISiQ==/lib/x86_64, /system/lib64]]]
主线程:186284486
com.wanjian.shareprocessapp1.ShareProApp1@845d87


应用2输出
系统类加载器:java.lang.BootClassLoader@d785773
应用类加载器:165133074:dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.wanjian.shareprocessapp2-9DIcaaxVhaD0-poddlFFMQ==/base.apk"],nativeLibraryDirectories=[/data/app/com.wanjian.shareprocessapp2-9DIcaaxVhaD0-poddlFFMQ==/lib/x86_64, /system/lib64]]]
主线程:186284486
com.wanjian.shareprocessapp1.ShareProApp1@845d87
com.wanjian.shareprocessapp2.ShareProApp2@dc652e3

可以看到两个应用的系统类加载器是同一个对象

应用类加载器是不同的,因为应用类加载器是通过AMS传过的类路径构造的,两个应用类路径不同,所以应用类加载器也就不同了

主线程也是同一个,这是必然的

应用1先启动,所以获取到的mAllApplications中只有一个对象,而且是自己的Application

应用2后启动,所以获取到的mAllApplications中只有两个对象,
一个是应用1的一个是应用2的

到此也就分析完了为什么会有一个mAllApplications还有一个mInitialApplication

如果应用1和应用2都有一个MyClass类,其中都有一个value静态字段,那是不是在应用1中修改了value的值,应用2中获取的就是应用1修改后的值呢?肯定不是的,因为这两个应用有各自的类加载器,互不干扰,所以应用1中修改的值不会影响到应用2

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值