[Android] 插件化框架Virtual APK实现原理解析

1 . 前言Virtual APK是滴滴出行自研的一款优秀的插件化框架,其主要开发人员有任玉刚老师说到任玉刚老师,他可以说是我Android FrameWork层的启蒙老师。刚接触Android的时候,在拖了几年控件、写了一些CURD操作后,就得出了这样的结论:客户端太无聊了,现在已经完全精通安卓开发了。直到有一天看了一本叫做《Android开发艺术探索》的书,不禁感慨:原来Android开发竟然还能这么玩,之前的认知实在是浅薄言归正传,Virtual APK的特性和使用方法不是本文重点,如有需要了解
摘要由CSDN通过智能技术生成

1 . 前言

Virtual APK是滴滴出行自研的一款优秀的插件化框架,其主要开发人员有任玉刚老师

说到任玉刚老师,他可以说是我Android FrameWork层的启蒙老师。刚接触Android的时候,在拖了几年控件、写了一些CURD操作后,就得出了这样的结论:客户端太无聊了,现在已经完全精通安卓开发了。直到有一天看了一本叫做《Android开发艺术探索》的书,不禁感慨:原来Android开发竟然还能这么玩,之前的认知实在是浅薄

言归正传,Virtual APK的特性和使用方法不是本文重点,如有需要了解更多请移步VirtualAPK的特性和使用方法。本文主要针对Virtual APK的实现做讲解。

2 . 重要的知识点

  • Activity启动流程(AMS)
  • DexClassLoader
  • 动态代理
  • 反射
  • 广播的动态注册

3 . 宿主App的实现

中心思想:

  • 对插件APK进行解析,获取插件APK的信息
  • 在框架初始化时,对一系列系统组件和接口进行替换,从而对Activity、Service、ContentProvider的启动和生命周期进行修改和监控,达到欺瞒系统或者劫持系统的目的来启动插件Apk的对应组件。

3.1 插件Apk的解析和加载

插件Apk的加载在PluginManager#loadPlugin方法,在加载完成后,会生成一个LoadedPlugin对象并保存在Map中。LoadedPlugin里保存里插件Apk里绝大多数的重要信息和一个DexClassLoader,这个DexClassLoader是作为插件Apk的类加载器使用。

看下LoadedPlugin的具体实现,注释标明了各个属性的含义:

public LoadedPlugin(PluginManager pluginManager, Context context, File apk) throws Exception {
        // PluginManager
        this.mPluginManager = pluginManager;
        // 宿主Context
        this.mHostContext = context;
        // 插件apk路径
        this.mLocation = apk.getAbsolutePath();
        this.mPackage = PackageParserCompat.parsePackage(context, apk, PackageParser.PARSE_MUST_BE_APK);
        // 插件apk metadata
        this.mPackage.applicationInfo.metaData = this.mPackage.mAppMetaData;
        // 插件apk package信息
        this.mPackageInfo = new PackageInfo();
        this.mPackageInfo.applicationInfo = this.mPackage.applicationInfo;
        this.mPackageInfo.applicationInfo.sourceDir = apk.getAbsolutePath();
        // 插件apk 签名信息
        if (Build.VERSION.SDK_INT >= 28
            || (Build.VERSION.SDK_INT == 27 && Build.VERSION.PREVIEW_SDK_INT != 0)) { // Android P Preview
            try {
                this.mPackageInfo.signatures = this.mPackage.mSigningDetails.signatures;
            } catch (Throwable e) {
                PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
                this.mPackageInfo.signatures = info.signatures;
            }
        } else {
            this.mPackageInfo.signatures = this.mPackage.mSignatures;
        }
        // 插件apk 包名
        this.mPackageInfo.packageName = this.mPackage.packageName;
        // 如果已经加载过相同的apk, 抛出异常
        if (pluginManager.getLoadedPlugin(mPackageInfo.packageName) != null) {
            throw new RuntimeException("plugin has already been loaded : " + mPackageInfo.packageName);
        }

        this.mPackageInfo.versionCode = this.mPackage.mVersionCode;
        this.mPackageInfo.versionName = this.mPackage.mVersionName;
        this.mPackageInfo.permissions = new PermissionInfo[0];
        this.mPackageManager = createPluginPackageManager();
        this.mPluginContext = createPluginContext(null);
        this.mNativeLibDir = getDir(context, Constants.NATIVE_DIR);
        this.mPackage.applicationInfo.nativeLibraryDir = this.mNativeLibDir.getAbsolutePath();
        // 创建插件的资源管理器
        this.mResources = createResources(context, getPackageName(), apk);
        // 创建 一个dexClassLoader
        this.mClassLoader = createClassLoader(context, apk, this.mNativeLibDir, context.getClassLoader());

        tryToCopyNativeLib(apk);

        // Cache instrumentations
        Map<ComponentName, InstrumentationInfo> instrumentations = new HashMap<ComponentName, InstrumentationInfo>();
        for (PackageParser.Instrumentation instrumentation : this.mPackage.instrumentation) {
            instrumentations.put(instrumentation.getComponentName(), instrumentation.info);
        }
        this.mInstrumentationInfos = Collections.unmodifiableMap(instrumentations);
        this.mPackageInfo.instrumentation = instrumentations.values().toArray(new InstrumentationInfo[instrumentations.size()]);

        // Cache activities
        // 保存插件apk的Activity信息
        Map<ComponentName, ActivityInfo> activityInfos = new HashMap<ComponentName, ActivityInfo>();
        for (PackageParser.Activity activity : this.mPackage.activities) {
            activity.info.metaData = activity.metaData;
            activityInfos.put(activity.getComponentName(), activity.info);
        }
        this.mActivityInfos = Collections.unmodifiableMap(activityInfos);
        this.mPackageInfo.activities = activityInfos.values().toArray(new ActivityInfo[activityInfos.size()]);

        // Cache services
        // 保存插件apk的Service信息
        Map<ComponentName, ServiceInfo> serviceInfos = new HashMap<ComponentName, ServiceInfo>();
        for (PackageParser.Service service : this.mPackage.services) {
            serviceInfos.put(service.getComponentName(), service.info);
        }
        this.mServiceInfos = Collections.unmodifiableMap(serviceInfos);
        this.mPackageInfo.services = serviceInfos.values().toArray(new ServiceInfo[serviceInfos.size()]);

        // Cache providers
        // 保存插件apk的ContentProvider信息
        Map<String, ProviderInfo> providers = new HashMap<String, ProviderInfo>();
        Map<ComponentName, ProviderInfo> providerInfos = new HashMap<ComponentName, ProviderInfo>();
        for (PackageParser.Provider provider : this.mPackage.providers) {
            providers.put(provider.info.authority, provider.info);
            providerInfos.put(provider.getComponentName(), provider.info);
        }
        this.mProviders = Collections.unmodifiableMap(providers);
        this.mProviderInfos = Collections.unmodifiableMap(providerInfos);
        this.mPackageInfo.providers = providerInfos.values().toArray(new ProviderInfo[providerInfos.size()]);

        // 将所有静态注册的广播全部改为动态注册
        Map<ComponentName, ActivityInfo> receivers = new HashMap<ComponentName, ActivityInfo>();
        for (PackageParser.Activity receiver : this.mPackage.receivers) {
            receivers.put(receiver.getComponentName(), receiver.info);

            BroadcastReceiver br = BroadcastReceiver.class.cast(getClassLoader().loadClass(receiver.getComponentName().getClassName()).newInstance());
            for (PackageParser.ActivityIntentInfo aii : receiver.intents) {
                this.mHostContext.registerReceiver(br, aii);
            }
        }
        this.mReceiverInfos = Collections.unmodifiableMap(receivers);
   
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值