VirtualApp 中的进程

Posted ximsfei

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了VirtualApp 中的进程相关的知识,希望对你有一定的参考价值。

一. 什么是进程

进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。 – 来自百度百科

二. VA进程

VA在运行时,存在四种类型的进程:
* Server Process: 进程名hostPackageName:x,参照android system_server进程实现,用于管理VPackageManagerService,VActivityManagerService等服务。
* VAppClient Process: 进程名hostPackageName:p[0~…],VA容器中安装的第三方应用将运行在此类进程中。
* Main Process: 宿主应用主进程。
* Child Process: 宿主应用子进程。

com/lody/virtual/client/core/VirtualCore.java

/**
 * Process type
 */
private enum ProcessType 
    /**
     * Server process
     */
    Server,
    /**
     * Virtual app process
     */
    VAppClient,
    /**
     * Main process
     */
    Main,
    /**
     * Child process
     */
    CHILD

2.1 Server进程

Android通过system_server进程来管理整个Java framework层,包含ActivityManager,PackageManager等各种系统服务;在系统启动的时候就会启动该进程。

VA是一个开源的Android应用容器框架,允许用户在应用中创建虚拟空间,并且安装第三方应用到该虚拟空间中。为了保证应用可以无感知的运行在容器中,VA参照framework源码实现,构造了一个能够运行app的环境。
VA利用ContentProvider的同步特性构建了一套跨进程同步通信机制,把ContentProvider当作一个ServiceManager,所有的系统服务都放在这里管理,利用ContentProvider call的同步调用方式,把Binder对象透传到不同的进程;

com/lody/virtual/server/BinderProvider.java

在BinderProvider的onCreate中会初始化VPackageManagerService, VActivityManagerService, VAppManagerService等服务,这些服务均能在Android framework中找到缩影(PackageManagerService, ActivityManangerService等)。并把实例化的Service存储到ServiceCache的sCache Map中。

@Override
public boolean onCreate() 
    ...
    VPackageManagerService.systemReady();
    addService(ServiceManagerNative.PACKAGE, VPackageManagerService.get());
    VActivityManagerService.systemReady(context);
    addService(ServiceManagerNative.ACTIVITY, VActivityManagerService.get());
    addService(ServiceManagerNative.USER, VUserManagerService.get());
    VAppManagerService.systemReady();
    addService(ServiceManagerNative.APP, VAppManagerService.get());
    BroadcastSystem.attach(VActivityManagerService.get(), VAppManagerService.get());
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) 
        addService(ServiceManagerNative.JOB, VJobSchedulerService.get());
    
    VNotificationManagerService.systemReady(context);
    addService(ServiceManagerNative.NOTIFICATION, VNotificationManagerService.get());
    VAppManagerService.get().scanApps();
    VAccountManagerService.systemReady();
    addService(ServiceManagerNative.ACCOUNT, VAccountManagerService.get());
    addService(ServiceManagerNative.VS, VirtualStorageService.get());
    return true;



private void addService(String name, IBinder service) 
    ServiceCache.addService(name, service);

com/lody/virtual/server/ServiceCache.java

public class ServiceCache 

    private static final Map<String, IBinder> sCache = new ArrayMap<>(5);

    public static void addService(String name, IBinder service) 
        sCache.put(name, service);
    

    public static IBinder removeService(String name) 
        return sCache.remove(name);
    

    public static IBinder getService(String name) 
        return sCache.get(name);
    

了解源码的同学都知道,ActivityManagerService、PackageManagerService等系统服务,在每个进程中都对应一个Client(ActivityManager, PackageManager)。VA参照源码的实现,在Server进程中启动VPackageManagerService,VActivityManagerService等系统服务,在每个进程中也有对应的Client(VActivityManager,VPackageManager)。

2.2 Client与Server通信

以VActivityManager与VActivityManagerService通信为例,其他Service流程类似。

com/lody/virtual/client/ipc/VActivityManager.java

业务层调用VActivityManager的任意方法,会先调用getService()方法获取IActivityManager。

public IActivityManager getService() 
    if (mRemote == null || !mRemote.asBinder().isBinderAlive()) 
        synchronized (VActivityManager.class) 
            final Object remote = getRemoteInterface();
            mRemote = LocalProxyUtils.genProxy(IActivityManager.class, remote);
        
    
    return mRemote;


private Object getRemoteInterface() 
    return IActivityManager.Stub
            .asInterface(ServiceManagerNative.getService(ServiceManagerNative.ACTIVITY));

com/lody/virtual/client/ipc/ServiceManagerNative.java

ServiceManagerNative的getService方法中,先判断是否为Server进程:
* 是,直接从ServiceCache的Map中获取对应的Service。
* 不是,会通过authorities获取BinderProvider,并调用BinderProvider的call方法,参数method为”@”。

public static String SERVICE_CP_AUTH = "virtual.service.BinderProvider";

public static IBinder getService(String name) 
    if (VirtualCore.get().isServerProcess()) 
        return ServiceCache.getService(name);
    
    IServiceFetcher fetcher = getServiceFetcher();
    if (fetcher != null) 
        try 
            return fetcher.getService(name);
         catch (RemoteException e) 
            e.printStackTrace();
        
    
    VLog.e(TAG, "GetService(%s) return null.", name);
    return null;


private static IServiceFetcher getServiceFetcher() 
    if (sFetcher == null || !sFetcher.asBinder().isBinderAlive()) 
        synchronized (ServiceManagerNative.class) 
            Context context = VirtualCore.get().getContext();
            Bundle response = new ProviderCall.Builder(context, SERVICE_CP_AUTH).methodName("@").call();
            if (response != null) 
                IBinder binder = BundleCompat.getBinder(response, "_VA_|_binder_");
                linkBinderDied(binder);
                sFetcher = IServiceFetcher.Stub.asInterface(binder);
            
        
    
    return sFetcher;
<provider
    android:name="com.lody.virtual.server.BinderProvider"
    android:authorities="$applicationId.virtual.service.BinderProvider"
    android:exported="false"
    android:process=":x" />

com/lody/virtual/client/ipc/ProviderCall.java

通过传入的authority,查找对应的ContentProvider,并调用ContentProvider的call方法。

public class ProviderCall 

    public static Bundle call(String authority, String methodName, String arg, Bundle bundle) 
        return call(authority, VirtualCore.get().getContext(), methodName, arg, bundle);
    

    public static Bundle call(String authority, Context context, String method, String arg, Bundle bundle) 
        Uri uri = Uri.parse("content://" + authority);
        return ContentProviderCompat.call(context, uri, method, arg, bundle);
    

    public static final class Builder 

        private Context context;

        private Bundle bundle = new Bundle();

        private String method;
        private String auth;
        private String arg;

        public Builder(Context context, String auth) 
            this.context = context;
            this.auth = auth;
        

        public Builder methodName(String name) 
            this.method = name;
            return this;
        

        public Builder arg(String arg) 
            this.arg = arg;
            return this;
        

        public Builder addArg(String key, Object value) 
            if (value != null) 
                 if (value instanceof Boolean) 
                    bundle.putBoolean(key, (Boolean) value);
                 else if (value instanceof Integer) 
                    bundle.putInt(key, (Integer) value);
                 else if (value instanceof String) 
                    bundle.putString(key, (String) value);
                 else if (value instanceof Serializable) 
                    bundle.putSerializable(key, (Serializable) value);
                 else if (value instanceof Bundle) 
                    bundle.putBundle(key, (Bundle) value);
                 else if (value instanceof Parcelable) 
                    bundle.putParcelable(key, (Parcelable) value);
                 else 
                    throw new IllegalArgumentException("Unknown type " + value.getClass() + " in Bundle.");
                
            
            return this;
        

        public Bundle call() 
            return ProviderCall.call(auth, context, method, arg, bundle);
        

    

com/lody/virtual/helper/compat/ContentProviderCompat.java

public static Bundle call(Context context, Uri uri, String method, String arg, Bundle extras) 
    if (VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) 
        return context.getContentResolver().call(uri, method, arg, extras);
    
    ContentProviderClient client = crazyAcquireContentProvider(context, uri);
    Bundle res = null;
    try 
        res = client.call(method, arg, extras);
     catch (RemoteException e) 
        e.printStackTrace();
     finally 
        releaseQuietly(client);
    
    return res;

com/lody/virtual/server/BinderProvider.java

BinderProvider的call方法会将mServiceFetcher封装到Bundle中返回给Client。Client端可以通过ServiceFetcher的getService()方法获取Server端启动时存储在ServiceCache中的Service。至此,Client与Server可以通过getService进行方法调用,建立通信了。

@Override
private final ServiceFetcher mServiceFetcher = new ServiceFetcher();

public Bundle call(String method, String arg, Bundle extras) 
    if ("@".equals(method)) 
        Bundle bundle = new Bundle();
        BundleCompat.putBinder(bundle, "_VA_|_binder_", mServiceFetcher);
        return bundle;
    
    return null;


private class ServiceFetcher extends IServiceFetcher.Stub 
    @Override
    public IBinder getService(String name) throws RemoteException 
        if (name != null) 
            return ServiceCache.getService(name);
        
        return null;
    

    @Override
    public void addService(String name, IBinder service) throws RemoteException 
        if (name != null && service != null) 
            ServiceCache.addService(name, service);
        
    

    @Override
    public void removeService(String name) throws RemoteException 
        if (name != null) 
            ServiceCache.removeService(name);
        
    

三. 总结

至此,我们已经打通了VA构建在ContentProvider机制上的同步通信方式。利用ContentProvider来模拟framework层的ServiceManager,使整个框架的核心得以摆脱AIDL Service异步过程的苦恼,代码看起来比较整洁,用起来也非常方便;同时,这个机制也解决了插件之间跨进程通信的难题;类似DroidPlugin这样的多进程机制的插件系统,如果插件之间需要通信,必须使用IPC机制:广播,AIDL等等,这些通信都是异步的,写起来非常麻烦;而构建在ContentProvider机制上的同步AIDL通信使得插件的跨进程通信就像普通函数调用一样简单。

四. 参考

以上是关于VirtualApp 中的进程的主要内容,如果未能解决你的问题,请参考以下文章

VirtualApp 进程分配与管理

VirtualApp 进程分配与管理

Android 插件化VirtualApp 编译运行 ( VirtualApp 简介 | 配置 VirtualApp 编译环境 | 编译运行 VirtualApp 官方示例 )

Android 插件化VirtualApp 编译运行 ( VirtualApp 简介 | 配置 VirtualApp 编译环境 | 编译运行 VirtualApp 官方示例 )

Android 插件化VirtualApp 接入 ( 在 VirtualApp 工程下创建 Module | 添加依赖 | 启动 VirtualApp 插件引擎 )

Android 插件化VirtualApp 接入 ( 在 VirtualApp 工程下创建 Module | 添加依赖 | 启动 VirtualApp 插件引擎 )