插件化之VirtualApk

VirtualApk是滴滴开源的一套插件化方案,其支持四大组件,支持插件宿主之间的交互,兼容性强,在滴滴出行APP中有应用。下面是官方文档中与其他主流插件化框架的对比(查看原文):

特性 DynamicLoadApk DynamicAPK Small DroidPlugin VirtualAPK
支持四大组件 只支持Activity 只支持Activity 只支持Activity 全支持 全支持
组件无需在宿主manifest中预注册 ×
插件可以依赖宿主 ×
支持PendingIntent × × ×
Android特性支持 大部分 大部分 大部分 几乎全部 几乎全部
兼容性适配 一般 一般 中等
插件构建 部署aapt Gradle插件 Gradle插件
  1. 添加gradle依赖
    在根目录build.gradle中添加插件
 buildscript {
     dependencies {
         ...
         classpath 'com.didi.virtualapk:gradle:0.9.8.6'
         ...
     }
 }
  1. 引入插件
    在app模块的build.gradle中添加
    apply plugin: 'com.didi.virtualapk.host'

  2. 添加依赖
    在app模块的build.gradle中的dependencies中加入
    implementation 'com.didi.virtualapk:core:0.9.8'

  3. 初始化SDK
    选择一个合适的时机初始化SDK,一般是在项目的Application类的attachBaseContext方法中完成。

   override fun attachBaseContext(base: Context?) {
       super.attachBaseContext(base)
       PluginManager.getInstance(base).init()
   }

1.2 接入插件模块

  1. 添加gradle依赖
    同上面接入主程序环节第一步配置,如果插件模块和主程序在同一个项目中则可以忽略

  2. 引入插件
    在插件模块的build.gradle中添加apply plugin: 'com.didi.virtualapk.plugin'
    注意的是:插件模块也是一个应用项目而非库项目,即apply plugin: 'com.android.application'而不是apply plugin: 'com.android.library'

  3. 声明插件配置
    在插件模块的build.gradle底部声明virtualApk配置

    virtualApk {
        packageId = 0x6f // 资源前缀.
        targetHost = '../app' // 宿主模块的文件路径,生成插件会检查依赖项,分析和排除与宿主APP的共同依赖.
        applyHostMapping = true //optional, default value: true.
    }
    

    其中packageId是资源id的前缀,用来区分插件资源,所以插件之间要使用不同的前缀。
    这个前缀不一定要0x6f,正常我们的APP编译出来的R文件一般像下面这种,可以看出前缀是0x7f,理论上这个packageId的取值范围应为[0x00,0x7f),然而0x010x02等等已经被系统应用占用,具体占用多少不得而知,因此尽量选择偏大且足够分配给所有插件使用的数字。

    public final class R {
        public static final class anim {
            public static final int abc_fade_in=0x7f010000;
            public static final int abc_fade_out=0x7f010001;
            public static final int abc_grow_fade_in_from_bottom=0x7f010002;
        }
    }
    

关于packageId的官方说明

到这里就已经完成了VirtualApk的宿主以及插件模块的配置,非常简单,可以看出对我们现有的工程完全几乎不需要修改,我们依然可以用我们习惯的模块化的开发方式。

截止发稿时的最新版本是0.9.8.6,建议大家尽量使用最新版本,毕竟安卓的碎片化这么严重,而且hook方案多少会有些不完美的地方,相信滴滴以及gayhub的基友们会在新版本不停的完善它,而且老版本很可能不会维护。
一般从官方GitHub项目的releases可以找到当前最新版本。

  • 在项目Application中初始化插件:
public class VirtualAPKHostApplication extends Application {

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        // 初始化VirtualAPK
        PluginManager.getInstance(base).init();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        // 加载存储根目录的插件apk,实际项目中按需保存
        String pluginPath = Environment.getExternalStorageDirectory().getAbsolutePath().concat("/plugin.apk");
        File plugin = new File(pluginPath);
        if (plugin.exists()) {
            try {
                PluginManager.getInstance(this).loadPlugin(plugin);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

不要忘了在清单文件中配置Application:

<application
    android:name=".VirtualAPKHostApplication">
</application>
  • 5.调用插件

com.yl.plugin是插件工程的包名,com.yl.plugin.PluginActivity是插件工程中的类,插件工程的包名可以和宿主工程相同,但是相同包名下的类名不能相同,资源名称也不能相同。

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.btn_start_plugin_activity).setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        if (PluginManager.getInstance(this).getLoadedPlugin("com.yl.plugin") == null) {
            Toast.makeText(this, "Plugin is not loaded!", Toast.LENGTH_SHORT).show();
        } else {
            Intent intent = new Intent();
            intent.setClassName("com.yl.plugin", "com.yl.plugin.PluginActivity");
            startActivity(intent);
        }
    }
}
  • 6.权限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  • 7.混淆配置
-keep class com.didi.virtualapk.internal.VAInstrumentation { *; }
-keep class com.didi.virtualapk.internal.PluginContentResolver { *; }

-dontwarn com.didi.virtualapk.**
-dontwarn android.**
-keep class android.** { *; }

插件

gradlew clean assemblePlugin

因为assemblePlugin依赖于assembleRelease,所以插件包均是Release包,不支持debug模式的插件包。

https://blog.csdn.net/yyh352091626/article/details/74852390

https://www.jianshu.com/p/013510c19391

https://www.cnblogs.com/tgltt/p/9542193.html

https://github.com/didi/VirtualAPK

https://mp.weixin.qq.com/s?__biz=MzAxMTI4MTkwNQ==&mid=2650823488&idx=1&sn=2976c8ddc0c206149b14c527260f7766&chksm=80b78fdeb7c006c8a9585db794c51e799049ec50d23c4d738c78d77454f6b0291227a00e2def&mpshare=1&scene=1&srcid=0712oTUswGWi172UK0Azpg4i&key=8652b956ca1971a47b1e263b435230c7469d30646ddbe6ce2fb781033d6eba5215c9fd7e5eaf0bd73dd5da279b32dd901261d5e55bf32997bc333ad8a059e095e2193a5baa805447fc49cd315fca4404&ascene=0&uin=MTI0NjM4NTEyMA%3D%3D&devicetype=iMac+MacBookPro11%2C4+OSX+OSX+10.11.1+build(15B42)&version=12010110&nettype=WIFI&fontScale=100&pass_ticket=mswE9bS3QeCxTOoepaUWh9VXHxeYMosdkHkAydyR09JHQkVe%2BAJHCCnPQrRpBfQN

发布了74 篇原创文章 · 获赞 36 · 访问量 25万+

猜你喜欢

转载自blog.csdn.net/kdsde/article/details/104002305