1 简介

Android大型项目中为了减小apk的体积,可以采用插件化的方法,即一些不常用的功能独立成插件,当用户需要的使用的时候再从服务器上下载回来,动态加载。这样就避免了为了满足所有用户需求而把功能全部打包到apk,导致apk体积的膨胀。所谓的插件,其实也是一个apk,但是一般都依赖正式对外发布的app,也叫宿主。本篇不讨论插件化的原理和实现难点,只介绍怎么使用以及优缺点。 
Android插件化常用实现方案有两种:

(1) DynamicLoadApk 
(2) DroidPlugin

这两个是目前比较主流的Android插件化实现方案,在Github的星星数很高,两者的Github地址如下:

DynamicLoadApk的Github地址:https://github.com/singwhatiwanna/dynamic-load-apk 
DroidPlugin的Github地址:https://github.com/Qihoo360/DroidPlugin

2 特点

(1) DynamicLoadApk是由团队维护的,但是目前已经很长时间没有更新了,途牛用的就是这个插件化框架 
优点:

  • 插件不依赖宿主,对宿主开发者透明,提供三种依赖方式
  • 宿主和插件可以频繁交互,启动时间短

缺点:

  • 插件apk必须实现DLBasePluginActivity,属于侵入式的,以及不支持service
  • 宿主调用插件和插件内部的相互调用都要使用DL提供的方法,而不能使用Android原生的api,例如:启动Activity
  • 插件开发有一套规定,因此造成插件开发门槛高,学习成本高

(2) DroidPlugin是360公司开源的一个框架,已经在360手机助手上使用 
优点:

  • 宿主和插件完全隔离,插件不依赖宿主,可以独立安装运行
  • 低入侵设计,插件不需要继承任何类
  • 插件apk和普通apk一样的,所以插件开发没有门槛
  • 有大公司维护,有360手机助手这样的商用app在使用

缺点:

  • 插件启动速度太慢,而且宿主只能调用插件的LaunchMode的Activity,不能调用其他Activity

3 使用方法

(1) 导入Dynamic-load-apk中的lib。 
下载Dynamic-load-apk后解压,在Android Studio中新建工程DLTest(自己命名) –> new –> import module –>选择lib所在的目录:dynamic-load-apk-master\DynamicLoadApk\lib

(2) 新建插件模块plugin,宿主模块host,这两个模块都是application, 最后都要生成apk的。项目目录如下:

编译lib模块,命令是build菜单–>make module lib,目的是为了获得生成的jar文件,jar文件所在位置是lib\build\intermediates\bundles\debug\class.jar,复制jar文件重命名为lib.jar

(3) 导入lib.jar到plugin项目的libs目录下,开发plugin项目,注意Activity要继承DLBasePluginActivity ,R.layout.activity_test上就一个TextView,显示”这个界面来自Plugin” 
插件项目Plugin的MainActivity:

public class MainActivity extends DLBasePluginActivity {@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_test);}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

注意:plugin模块的buidle.gradle需要修改为如下:

dependencies {provided fileTree(dir: 'libs', include: ['*.jar']).........
}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

provided 意思是编译时候使用,但不打包到APK中,这样做是因为我们的宿主项目host中已经包含了lib.jar,如果插件中也包含的话就会报找不到plugin中的Activity的错,原因是两个包重复,必须要用host中的DL框架来加载plugin,而不是plugin自带的DL框架

(4) 导入lib.jar到host项目的libs目录下,开发host项目 
host项目的MainActivity:

public class MainActivity extends Activity {private Button btnTest;private TextView tvTip;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);this.btnTest = (Button) findViewById(R.id.btn_test);this.tvTip = (TextView) findViewById(R.id.tv_tip);this.init();}//初始化private void init() {//获取插件String pluginFolder = "/mnt/sdcard/DynamicLoadHost";File file = new File(pluginFolder);File[] plugins = file.listFiles();//判断有没有插件if (plugins == null || plugins.length == 0) {this.tvTip.setVisibility(View.VISIBLE);return;}//调用第一个插件File plugin = plugins[0];final PluginItem item = new PluginItem();item.pluginPath = plugin.getAbsolutePath();item.packageInfo = DLUtils.getPackageInfo(this, item.pluginPath);//获取插件的启动Activity的名称if (item.packageInfo.activities != null && item.packageInfo.activities.length > 0) {item.launcherActivityName = item.packageInfo.activities[0].name;}//获取插件启动Service的名称if (item.packageInfo.services != null && item.packageInfo.services.length > 0) {item.launcherServiceName = item.packageInfo.services[0].name;}//显示插件tvTip.setText("检测到一个插件:" + item.pluginPath);//加载插件DLPluginManager.getInstance(this).loadApk(item.pluginPath);//添加监听器this.btnTest.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//提示Toast.makeText(getApplicationContext(), "开始调用插件", Toast.LENGTH_SHORT).show();//调用插件usePlugin(item);}});}//调用插件private void usePlugin(PluginItem pluginItem) {DLPluginManager pluginManager = DLPluginManager.getInstance(this);pluginManager.startPluginActivity(this, new DLIntent(pluginItem.packageInfo.packageName, pluginItem.launcherActivityName));}//插件Beanpublic static class PluginItem {public PackageInfo packageInfo;public String pluginPath;public String launcherActivityName;public String launcherServiceName;public PluginItem() {}}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71

host的activity_main.xml:

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><TextView
        android:id="@+id/tv_tip"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:text="没有检测到插件"/><Button
        android:id="@+id/btn_test"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_below="@id/tv_tip"android:layout_marginTop="10dp"android:text="测试调用插件"/></RelativeLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

注意:Host的AndroidManifest.xml中需要额外的声明几个DL框架中的类,否则运行时候找不到Activity.

Host的AndroidManifest.xml:

<manifest
    package="com.host"xmlns:android="http://schemas.android.com/apk/res/android"><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/><application
        android:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:supportsRtl="true"android:theme="@style/AppTheme"><activity
            android:name=".MainActivity"android:label="@string/app_name"android:theme="@style/AppTheme.NoActionBar"><intent-filter><action android:name="android.intent.action.MAIN"/><category android:name="android.intent.category.LAUNCHER"/></intent-filter></activity><activity
            android:name="com.ryg.dynamicload.DLProxyActivity"android:label="@string/app_name"><intent-filter><action android:name="com.ryg.dynamicload.proxy.activity.VIEW"/><category android:name="android.intent.category.DEFAULT"/></intent-filter></activity><activity
            android:name="com.ryg.dynamicload.DLProxyFragmentActivity"android:label="@string/app_name"><intent-filter><action android:name="com.ryg.dynamicload.proxy.fragmentactivity.VIEW"/><category android:name="android.intent.category.DEFAULT"/></intent-filter></activity><service android:name="com.ryg.dynamicload.DLProxyService"/></application></manifest>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

(5) 编译plugin项目,将生成的plugin-debug.apk文件放入/mnt/sdcard/DynaminLoadHost目录下,然后运行Host,运行结果如下: 

4 总结

  • 1 宿主和插件没有任何联系,但是插件需要继承DLBasePluginActivity,这个不太友好
  • 2 侵入式的,对插件apk的开发限制太多,例如:必须继承DLBasePluginActivity,启动时候必须调用startPluginActivity(new 
    DLIntent(getPackageName(),TestActivity.class))
  • 3 这个框架学习成本高,限制多,联调不方便,不建议使用
  • 4 目前比较好的插件有360公司的DroidPlugin, 以及类似友盟的第三方解决方案ApkPlug

5 扩展

1 Android Studio中可以不用jar文件吗?

Host可以,但plugin不可以。宿主项目可以依赖于lib项目,但是plugin必须使用jar文件,原因参见下面第3条

2 Host项目如何直接依赖lib项目,而不用jar文件?

修改host文件的build.gradle文件

dependencies {compile project(':lib').....
}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

修改lib的build.gradle文件,不修改的话就会和host项目中的support-v4包冲突

dependencies {provided fileTree(dir: 'libs', include: ['*.jar'])
}
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

3 为什么plugin必须使用jar文件?

Plugin不能将lib模块打包到apk中,所以不能使用compile,只能使用provided,所以如果不用jar则plugin模块的build.gradle只能如下:

dependencies {provided project(':lib').....
}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

呵呵,可惜这样是不行的,project只能使用compile ,不能使用provided ,百度了半天没有解决这个问题,如果你有办法欢迎留言

6 参考博客:

(1) http://blog.csdn.net/singwhatiwanna/article/details/40283117

7 转载请注明来自“梧桐那时雨”的博客:http://blog.csdn.net/fuchaosz/article/details/51056947

[Android 插件化(一)] DynamicLoadApk的用法相关推荐

  1. [Android 插件化(二)] DroidPlugin 用法

    1 简介 关于Android插件化可以查看我的前一篇博客:  [Android 插件化(一)] DynamicLoadApk的用法 本篇介绍第二种实现插件化的框架,360公司出品的DroidPlugi ...

  2. 《Android插件化技术——原理篇》

    | 导语 插件化技术最早从2012年诞生至今,已经走过了5个年头.从最初只支持Activity的动态加载发展到可以完全模拟app运行时的沙箱系统,各种开源项目层出不穷,在此挑选了几个代表性的框架,总结 ...

  3. Android 插件化原理 完胜360插件框架 技术实战

    性能优化 Android 性能优化 (一)APK高效瘦身 http://blog.csdn.net/whb20081815/article/details/70140063 Android 性能优化 ...

  4. Android 插件化原理学习 —— Hook 机制之动态代理

    前言 为了实现 App 的快速迭代更新,基于 H5 Hybrid 的解决方案有很多,由于 webview 本身的性能问题,也随之出现了很多基于 JS 引擎实现的原生渲染的方案,例如 React Nat ...

  5. 【Android 插件化】Hook 插件化框架 ( Hook 技术 | 代理模式 | 静态代理 | 动态代理 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  6. VirtualAPK:滴滴 Android 插件化的实践之路

    一.前言 在 Android 插件化技术日新月异的今天,开发并落地一款插件化框架到底是简单还是困难,这个问题不同人会有不同的答案.但是我相信,完成一个插件化框架的 Demo 并不是多难的事儿,然而要开 ...

  7. Android插件化技术调研

    一.技术背景 Android的插件化技术,目前已经比较成熟,微信.淘宝.携程.360手机助手中都应用到了插件化.插件化技术的特点是无需单独安装apk,即可运行,即插即用,无需升级宿主应用,减少app的 ...

  8. Android插件化开发之用DexClassLoader加载未安装的APK资源文件来实现app切换背景皮肤

    第一步.先制做一个有我们需要的图片资源的APK 如下图,这里有个about_log.png,我们需要生成apk文件. 生成的apk文件如果你不到项目的文件夹里面去取apk,想通过命令放到手机里面去可以 ...

  9. Android插件化开发之动态加载三个关键问题详解

    本文摘选自任玉刚著<Android开发艺术探索>,介绍了Android插件化技术的原理和三个关键问题,并给出了作者自己发起的开源插件化框架. 动态加载技术(也叫插件化技术)在技术驱动型的公 ...

最新文章

  1. 稀疏自编码器及TensorFlow实现
  2. 公司成立两周年感言_对我的副项目成立一周年的一些反思
  3. re模块与正则表达式
  4. 系统安装,重装与优化:chapter1 安装操作系统前的准备
  5. VScode 1.4新版功能
  6. 微服务中集成分布式配置中心 Apollo
  7. springboot2.0 多数据源整合问题 At least one JPA metamodel must be present!   at
  8. 前端学习(1308):URl
  9. 升级macOS Big Sur 无法开机/死机怎么办?
  10. 读《Java编程思想第五版》心得体会
  11. PS、PR素材资源网站
  12. 华为鸿蒙os logo,华为鸿蒙OS Logo曝光:Powered by HarmonyOS
  13. Android进阶——动画家族之视图动画、属性动画和过渡(转场)动画总结(一)
  14. della计算机驱动检测,Installazione dei driver della stampante per una versione precedente di Windows...
  15. 使用@media screen解决分web不同分辨率问题
  16. windows的cmd常用命令
  17. 主要视频压缩技术在中国内地市场发展分析
  18. IDEA 2020免费下载(附安装教程)
  19. 【无人机】基于fmincon实现无人机二维路径规划附matlab代码
  20. Java JMF 多媒体框架

热门文章

  1. Qt入门(10)——调试技术
  2. Android与Javascript交互示例(二)
  3. 如何利用ESP8266模块实现远程控制
  4. Python学习笔记:进程和线程(承)
  5. CUDA程序编写具体参数设置
  6. Android自定义控件(特效二) 点击屏幕,根据所点击的位置绘制桃心
  7. 类的初始化(构造函数)
  8. vtk读取文件并显示的几种方法
  9. extern C的用法解析
  10. 【Leetcode】创建链表