写点什么

插件化库 VirtualAPK 详解,你头秃都没想到还能这样吧

用户头像
Android架构
关注
发布于: 2 小时前

1.1、宿主工程引入 VirtualApk

  • 在项目 Project 的 build.gradle 中添加依赖


dependencies {classpath 'com.didi.virtualapk:gradle:0.9.8.6'}


  • 在宿主 app 的 build.gradle 中引入 VirtualApk 的 host 插件


apply plugin: 'com.didi.virtualapk.host'


  • 在 app 中添加依赖


dependencies {implementation 'com.didi.virtualapk:core:0.9.8'}


  • 在 Application 中完成 PluginManager 的初始化


public class VirtualApplication extends Application {@Overrideprotected void attachBaseContext(Context base) {super.attachBaseCon


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


text(base);PluginManager.getInstance(base).init();}}

1.2 插件开发

插件 APK 的配置

  • 在插件 project 中配置


classpath 'com.didi.virtualapk:gradle:0.9.8.6'


  • 在插件 app 的 build.gradle 中引入 plugin 插件


apply plugin: 'com.didi.virtualapk.plugin'


  • 配置插件信息和版本


virtualApk{// 插件资源表中的 packageId,需要确保不同插件有不同的 packageId// 范围 0x1f - 0x7fpackageId = 0x6f// 宿主工程 application 模块的路径,插件的构建需要依赖这个路径// targetHost 可以设置绝对路径或相对路径 targetHost = '../../../VirtualAPkDemo/app'// 默认为 true,如果插件有引用宿主的类,那么这个选项可以使得插件和宿主保持混淆一致//这个标志会在加载插件时起作用 applyHostMapping = true}


  • 设置签名(Virtual 仅支持 Release,host 项目和 plugin 项目签名一致)


signingConfigs {release {storeFile file('/Users/wuliangliang/AndroidSubjectStudyProject/PluginProject/VirtualAPkDemo/keystore/keystore')storePassword '123456'keyAlias = 'key'keyPassword '123456'}}buildTypes {release {signingConfig signingConfigs.release}}

插件的开发

在 VirtualAPK 中,插件开发等同于原生 Android 开发,因此开发插件就和开发 APP 一样。

插件和宿主的交互

通过 compile 相同 aar 的方式来交互。 比如,宿主工程中 compile 了如下 aar:


compile 'com.didi.foundation:sdk:1.2.0'compile 'com.didi.virtualapk:core:[newest version]'compile 'com.android.support:appcompat-v7:22.2.0'


但是插件工程需要访问宿主 sdk 中的类和资源,那么可以在插件工程中同样 compile sdk 的 aar,如下:


compile 'com.didi.foundation:sdk:1.2.0'


这样一来,插件工程就可以正常地引用 sdk 了,类似宿主和插件共用了一个功能库来进行交流。并且,插件构建的时候会自动将这个 aar 从 apk 中剔除。上述就是 VirtualAPK 中插件和宿主通信的基本方式。

插件中四大组件的已知约束
  • 透明 Activity,不能有启动模式,并且主题中必须含有 android:windowIsTranslucent 属性;


<style name="AppTheme.Transparent"><item name="android:windowBackground">@android:color/transparent</item><item name="android:windowIsTranslucent">true</item></style>


  • 插件中调用宿主的四大组件,请注意 Intent 中的包名


VirtualAPK 对 Intent 的处理遵循 Android 规范,插件之间乃至插件和宿主之间,包名是区分它们的唯一标识。 为了兼容宿主与插件之间的 activity 互调的场景,我们弱化了插件的包名,在插件中通过 context.getPackageName()取到的仍然是宿主的包名。因此在下面的例子中,假如宿主的包名是"com.didi.virtualapk",然后在插件中启动一个宿主 Activity,仍然可正确的调用:


// 兼容方式 Intent intent = new Intent(this, HostActivity.class);startActivity(intent);


// 显式指定包名的方式 Intent intent = new Intent();intent.setClassName("com.didi.virtualapk", "com.didi.virtualapk.HostActivity");startActivity(intent);


如果想在插件中去访问插件的四大组件,那么就没有任何要求了,下面的代码会在插件 Activity 中尝试启动另一个插件 Activity:


// 正确的用法,因为此时 intent 中的包名是插件的包名 Intent intent = new Intent(this, PluginActivity.class);startActivity(intent);


BroadcastReceiver


  • 静态 Receiver 将被动态注册,当宿主停止运行时,外部广播将无法唤醒宿主;

  • 由于动态注册的缘故,插件中的 Receiver 必须通过隐式调用来唤起。


ContentProvider,支持跨进程访问 ContentProvider


  1. 分情况,插件调用自己的 ContentProvider,如果需要用到 call 方法,那么需要将 provider 的 uri 放到 bundle 中,否则调用不生效;


Uri bookUri = Uri.parse("content://com.didi.virtualapk.demo.book.provider/book");Bundle bundle = PluginContentResolver.getBundleForCall(bookUri);getContentResolver().call(bookUri, "testCall", null, bundle);


  1. 插件调用宿主和外部的 ContentProvider,无约束;

  2. 宿主调用插件的 ContentProvider,需要将 provider 的 uri 包装一下,通过 PluginContentResolver.wrapperUri 方法,如果涉及到 call 方法,参考 1)中所描述的;


String pkg = "com.didi.virtualapk.demo";


LoadedPlugin plugin = PluginManager.getInstance(this).getLoadedPlugin(pkg);


Uri bookUri = Uri.parse("content://com.didi.virtualapk.demo.book.provider/book");


bookUri = PluginContentResolver.wrapperUri(plugin, bookUri);


Cursor bookCursor = getContentResolver().query(bookUri, new String[]{"_id", "name"}, null, null, null);


Fragment


推荐大家在 Application 启动的时候去加载插件,不然的话,请注意插件的加载时机。 考虑一种情况,如果在一个较晚的时机去加载插件并且去访问插件中的资源,请注意当前的 Context。比如在宿主 Activity(MainActivity)中去加载插件,接着在 MainActivity 去访问插件中的资源(比如 Fragment),需要做一下显示的 hook,否则部分 4.x 的手机会出现资源找不到的情况。


String pkg = "com.didi.virtualapk.demo";PluginUtil.hookActivityResources(MainActivity.this, pkg);


so 文件的加载


为了提升性能,VirtualAPK 在加载一个插件时并不会主动去释放插件中的 so,除非你在插件 apk 的 manifest 中显式地指定 VA_IS_HAVE_LIB 为 true,如下所示:


<applicationandroid:name=".VAApplication"android:allowBackup="true"

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
插件化库VirtualAPK详解,你头秃都没想到还能这样吧