当前位置:   article > 正文

Android studio接入VirtualApk详细过程,对比Atlas、RePlugin_android studio 对接virtualapk

android studio 对接virtualapk

转载请标明出处:https://blog.csdn.net/u013254166/article/details/80431288
本文出自: rhino博客 

一、宿主工程接入

1.在宿主工程根目录的build.gradle添加依赖,注意:我这里用的gradle时3.0,其他版本可能会有问题。

dependencies {
	classpath 'com.android.tools.build:gradle:3.0.0'
	classpath 'com.didi.virtualapk:gradle:0.9.8.3'
}

2.在App的工程模块的build.gradle添加使用gradle插件

apply plugin: 'com.didi.virtualapk.host'
...
dependencies {
    ...
    implementation 'com.didi.virtualapk:core:0.9.5'
}

3.AppApplication.java类是继承了Application,覆写attachBaseContext函数,进行插件SDK初始化工作

  1. package com.rhino.virtualapkdemo;
  2. import android.app.Application;
  3. import android.content.Context;
  4. import com.didi.virtualapk.PluginManager;
  5. /**
  6. * @author LuoLin
  7. * @since Create on 2018/5/23.
  8. */
  9. public class AppApplication extends Application {
  10. @Override
  11. protected void attachBaseContext(Context base) {
  12. super.attachBaseContext(base);
  13. PluginManager.getInstance(base).init();
  14. }
  15. }

4.在使用插件之前加载插件,可以根据具体业务场景选择合适时机加载,demo在MainActivity的onCreate时加载,加载需要权限READ_EXTERNAL_STORAGE,这里直接贴我的MainActivity.java

  1. package com.rhino.virtualapkdemo;
  2. import android.Manifest;
  3. import android.content.Intent;
  4. import android.content.pm.PackageManager;
  5. import android.os.Environment;
  6. import android.support.annotation.NonNull;
  7. import android.support.v4.app.ActivityCompat;
  8. import android.support.v4.content.ContextCompat;
  9. import android.support.v7.app.AppCompatActivity;
  10. import android.os.Bundle;
  11. import android.view.View;
  12. import android.widget.Button;
  13. import android.widget.Toast;
  14. import com.didi.virtualapk.PluginManager;
  15. import com.didi.virtualapk.internal.LoadedPlugin;
  16. import java.io.File;
  17. /**
  18. * @author LuoLin
  19. * @since Create on 2018/5/24.
  20. */
  21. public class MainActivity extends AppCompatActivity {
  22. /** 插件文件路径 **/
  23. private static final String PLUGIN_FILE_PATH = Environment.getExternalStorageDirectory()
  24. + File.separator + "plugin_demo.apk";
  25. /** 插件apk包名 **/
  26. private static final String PLUGIN_PACKAGE_NAME = "com.rhino.virtualapkplugindemo";
  27. /** 插件apk入口activity **/
  28. private static final String PLUGIN_LAUNCHER_ACTIVITY = "com.rhino.virtualapkplugindemo.PluginMainActivity";
  29. /** 请求权限请求码 **/
  30. public static final int REQUEST_PERMISSION_CODE = 111;
  31. /** 权限 **/
  32. public static final String[] PERMISSIONS = new String[]{
  33. Manifest.permission.READ_EXTERNAL_STORAGE
  34. };
  35. @Override
  36. protected void onCreate(Bundle savedInstanceState) {
  37. super.onCreate(savedInstanceState);
  38. setContentView(R.layout.activity_main);
  39. if (lacksPermissions(PERMISSIONS)) {
  40. // 开始请求权限
  41. requestPermissions(PERMISSIONS);
  42. } else {
  43. loadPlugin();
  44. }
  45. findViewById(R.id.show_second_activity).setOnClickListener(new View.OnClickListener() {
  46. @Override
  47. public void onClick(View v) {
  48. Intent intent = new Intent();
  49. intent.setClass(getApplicationContext(), SecondActivity.class);
  50. startActivity(intent);
  51. }
  52. });
  53. findViewById(R.id.run_plugin).setOnClickListener(new View.OnClickListener() {
  54. @Override
  55. public void onClick(View v) {
  56. showPluginActivity();
  57. }
  58. });
  59. }
  60. @Override
  61. public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
  62. if (requestCode == REQUEST_PERMISSION_CODE) {
  63. loadPlugin();
  64. }
  65. }
  66. /** 判断是否缺少权限 **/
  67. private boolean lacksPermissions(String... permissions) {
  68. for (String permission : permissions) {
  69. if (ContextCompat.checkSelfPermission(getApplicationContext(), permission) == PackageManager.PERMISSION_DENIED) {
  70. return true;
  71. }
  72. }
  73. return false;
  74. }
  75. /** 请求权限 **/
  76. private void requestPermissions(String... permissions) {
  77. ActivityCompat.requestPermissions(this, permissions, REQUEST_PERMISSION_CODE);
  78. }
  79. /** 加载插件 **/
  80. private void loadPlugin() {
  81. PluginManager pluginManager = PluginManager.getInstance(this);
  82. //此处是当查看插件apk是否存在,如果存在就去加载(比如修改线上的bug,把插件apk下载,达到热修复)
  83. File pluginApk = new File(PLUGIN_FILE_PATH);
  84. if (pluginApk.exists()) {
  85. try {
  86. pluginManager.loadPlugin(pluginApk);
  87. } catch (Exception e) {
  88. e.printStackTrace();
  89. }
  90. }
  91. }
  92. /** 运行插件中activity **/
  93. private void showPluginActivity() {
  94. if (PluginManager.getInstance(this).getLoadedPlugin(PLUGIN_PACKAGE_NAME) == null) {
  95. Toast.makeText(getApplicationContext(),"插件未加载,请尝试重启APP", Toast.LENGTH_SHORT).show();
  96. return;
  97. }
  98. Intent intent = new Intent();
  99. intent.setClassName(PLUGIN_PACKAGE_NAME, PLUGIN_LAUNCHER_ACTIVITY);
  100. startActivity(intent);
  101. }
  102. }

至此,VirtualAPK插件功能就集成到宿主中了,接下来,我们看下插件工程是如何集成的。

二、插件工程接入

1.在插件工程根目录的build.gradle添加依赖,注意:我这里用的gradle时3.0,其他版本可能会有问题。

dependencies {
	classpath 'com.android.tools.build:gradle:3.0.0'
	classpath 'com.didi.virtualapk:gradle:0.9.8.3'
}

2.在App的工程模块的build.gradle添加使用gradle插件和插件配置信息,信息需要放在文件最下面。

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

...

// 插件配置信息,放在文件最下面
virtualApk {
    packageId = 0x6f // 插件资源id,避免资源id冲突
    targetHost='../../GitHub/VirtualApkDemo/app' // 宿主工程的路径
    applyHostMapping = true // 插件编译时是否启用应用宿主的apply mapping
}

       解释一下上面3个参数的作用

  • packageId用于定义每个插件的资源id,多个插件间的资源Id前缀要不同,避免资源合并时产生冲突
  • targetHost指明宿主工程的应用模块,插件编译时需要获取宿主的一些信息,比如mapping文件、依赖的SDK版本信息、R资源文件,一定不能填错,否则在编译插件时会提示找不到宿主工程。
  • applyHostMapping表示插件是否开启apply mapping功能。当宿主开启混淆时,一般情况下插件就要开启applyHostMapping功能。因为宿主混淆后函数名可能有fun()变为a(),插件使用宿主混淆后的mapping映射来编译插件包,这样插件调用fun()时实际调用的是a(),才能找到正确的函数调用。

3.生成插件,需要使用Gradle命令

gradle clean assemblePlugin 或者 gradlew clean assemblePlugin 

       执行成功后,会生成如下apk文件,即插件apk。

       

       注意:这里的apk不能通过其他方式生成,如:run。

三、运行插件

1.将插件apk重命名为plugin_demo.apk(与宿主MainActiivty中加载apk名称保持一致),然后拷贝到sdcard根目录。

2.运行宿主apk,点击“进入插件中activity”,会发现成功启动了插件的activity。

四、测试实验

实验内容
现象
结论
宿主app加载插件apk文件,并启动插件中的Activity,以及activity是否需要在宿主manifest预注册?
插件apk不需要安装,宿主app可以直接在代码中加载指定的插件apk文件,可以成功启动插件中activity,该activity没有在宿主中预注册。
插件不需要安装,动态加载后可以启动插件activity,插件activity不需要在宿主中预注册。(这里只测试了activity,四大组件也支持)
修改插件ui界面,替换原来的apk文件,重启宿主app,观察修改能否及时生效?
重启宿主app后,显示了修改后的界面。
支持插件apk文件动态替换。
宿主和插件中创建相同activity,观察启动的时候会加载哪一个activity?
启动的是宿主中的activity。
插件中的class不能与宿主中class相同,加载的是宿主中class,不能做到替换。
宿主和插件中创建相同layout,观察宿主和插件分别会加载哪一个layout?
宿主和插件都加载宿主中layout。
Layout文件名称不能和宿主中重名,加载的是宿主中layout,不能做到替换,对于其他资源文件也是一样。

五、与其他插件化

Atlas:https://alibaba.github.io/atlas/

VirtualAPK:https://github.com/didi/VirtualAPK/wiki

RePlugin:https://github.com/Qihoo360/RePlugin/wiki

框架AtlasRePluginVirtualAPK
定义Atlas是伴随着手机淘宝的不断发展而衍生出来的一个运行于Android系统上的一个容器化框架,我们也叫动态组件化(Dynamic Bundle)框架。RePlugin是一套完整的、稳定的、适合全面使用的,占坑类插件化方案。VirtualAPK是滴滴出行自研的一款优秀的插件化框架。
功能

1.远程bundle

2.插件以so形式,放在sdcard,加载后会自动删除。

3.热修复,可以不升级apk就实现宿主和组件的更新。

1.远程bundle

2.插件以apk形式放在sdcard,加载后还在会自动备份apk到缓存目录。

1.远程bundle

2.插件以apk形式放在sdcard,加载后还在,删除会打不开。
更新插件方式必须要和宿主一起,打差异补丁才能更新。直接通过下载一个新的插件apk,然后调安装方法就能实现插件的更新。直接通过下载一个新的插件apk,然后调安装方法就能实现插件的更新。
插件独立性和宿主的依赖还是挺多,毕竟官方也强调是组件化,不是插件化。插件不用声明和宿主的联系,所以你生成一个插件后,这个插件可以给其他宿主调用。插件是一个独立的apk,但插件里面也定义和宿主的关联,就是说这个插件apk并不能给其他宿主用,只能给插件里面声明的那个宿主使用。

六、结论

       VirtualAPK不支持对宿主进行热修复。如果app需要热更新和插件的功能,推荐使用Atlas;如果插件无法很好地从宿主中拆出来,或者插件的运行和宿主有较多的交互,推荐使用VirtualAPK;如果同一个插件希望其他宿主也能用的话,那就只能RePlugin了,RePlugin就像一个应用市场,宿主仅仅是一个壳,然后把需要的插件下载加载使用就行,更新的话也无需更新宿主,直接更新插件就行。


最后附上我测试的demo源码下载链接,点击下载。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/正经夜光杯/article/detail/861104
推荐阅读
相关标签
  

闽ICP备14008679号