近期由于项目过大 造成id 65536的问题,导致编译通不过(Android对一个应用中的方法个数有限制);
先看一个开源框架:
DynamicLoadApk :
1. 功能介绍
1.1 简介
DynamicLoadApk 是一个开源的 Android 插件化框架。
插件化的优点包括:(1) 模块解耦,(2) 动态升级,(3) 高效并行开发(编译速度更快) (4) 按需加载,内存占用更低等等。
DynamicLoadApk 提供了 3 种开发方式,让开发者在无需理解其工作原理的情况下快速的集成插件化功能。
- 宿主程序与插件完全独立
- 宿主程序开放部分接口供插件与之通信
- 宿主程序耦合插件的部分业务逻辑
三种开发模式都可以在 demo 中看到。
1.2 核心概念
(1) 宿主:主 App,可以加载插件,也称 Host。
(2) 插件:插件 App,被宿主加载的 App,也称 Plugin,可以是跟普通 App 一样的 Apk 文件。
(3) 组件:指 Android 中的Activity
、Service
、BroadcastReceiver
、ContentProvider
,目前 DL 支持Activity
、Service
以及动态的BroadcastReceiver
。
(4) 插件组件:插件中的组件。
(5) 代理组件:在宿主的 Manifest 中注册,启动插件组件时首先被启动的组件。目前包括 DLProxyActivity(代理 Activity)、DLProxyFragmentActivity(代理 FragmentActivity)、DLProxyService(代理 Service)。
(6) Base 组件:插件组件的基类,目前包括 DLBasePluginActivity(插件 Activity 的基类)、DLBasePluginFragmentActivity(插件 FragmentActivity 的基类)、DLBasePluginService(插件 Service 的基类)。
DynamicLoadApk 原理的核心思想可以总结为两个字:代理。通过在 Manifest 中注册代理组件,当启动插件组件时首先启动一个代理组件,然后通过这个代理组件来构建、启动插件组件。
2. 总体设计
上面是 DynamicLoadApk 的总体设计图,DynamicLoadApk 主要分为四大模块:
(1) DLPluginManager
插件管理模块,负责插件的加载、管理以及启动插件组件。
(2) Proxy
代理组件模块,目前包括 DLProxyActivity(代理 Activity)、DLProxyFragmentActivity(代理 FragmentActivity)、DLProxyService(代理 Service)。
(3) Proxy Impl
代理组件公用逻辑模块,与(2)中的 Proxy 不同的是,这部分并不是一个组件,而是负责构建、加载插件组件的管理器。这些 Proxy Impl 通过反射得到插件组件,然后将插件与 Proxy 组件建立关联,最后调用插件组件的 onCreate 函数进行启动。
(4) Base Plugin
插件组件的基类模块,目前包括 DLBasePluginActivity(插件 Activity 的基类)、DLBasePluginFragmentActivity(插件 FragmentActivity 的基类)、DLBasePluginService(插件 Service 的基类)。
3. 流程图
上面是调用插件 Activity 的流程图,其他组件调用流程类似。
(1) 首先通过 DLPluginManager 的 loadApk 函数加载插件,这步每个插件只需调用一次。
(2) 通过 DLPluginManager 的 startPluginActivity 函数启动代理 Activity。
(3) 代理 Activity 启动过程中构建、启动插件 Activity。
4. 详细设计
4.1 类关系图
以上是 DynamicLoadApk 主要类的关系图,跟总体设计中介绍的一样大致分为三部分。
(1) 对于 Proxy 部分,每个组件都存在 DLAttachable 接口,方便统一该组件不同类,如 Activity、FragmentActivity。每个组件的公共实现部分都统一放到了对应的 DLProxyImpl 中。
(2) 对于 Base Plugin 部分,每个组件都存在 DLPlugin 接口,同样是方便统一该组件不同类。
4.2 类功能介绍
4.2.1 DLPluginManager.java
DynamicLoadApk 框架的核心类,主要功能包括:
(1) 插件的加载和管理;
(2) 启动插件的组件,目前包括 Activity、Service。
主要属性:
mNativeLibDir
为插件 Native Library 拷贝到宿主中后的存放目录路径。
mPackagesHolder
HashMap,key 为包名,value 为表示插件信息的DLPluginPackage
,存储已经加载过的插件信息。
主要函数:
(1) getInstance(Context context)
获取 DLPluginManager 对象的单例。
在私有构造函数中将mNativeLibDir
变量赋值为宿主 App 应用程序数据目录下名为pluginlib
子目录的全路径。
(2) loadApk(String dexPath)
加载插件。参数 dexPath 为插件的文件路径。
这个函数直接调用 loadApk(final String dexPath, boolean hasSoLib)。
(3) loadApk(final String dexPath, boolean hasSoLib)
加载插件 Apk。参数 dexPath 为插件的文件路径,hasSoLib 表示插件是否含有 so 库。
注意:在启动插件的组件前,必须先调用上面两个函数之一加载插件,并且只能在宿主中调用。
流程图如下:
loadApk 函数调用 preparePluginEnv 函数加载插件,图中虚线框为 preparePluginEnv 的流程图。
(4) preparePluginEnv(PackageInfo packageInfo, String dexPath)
加载插件及其资源。流程图如上图。
调用createDexClassLoader(…)
、createAssetManager(…)
、createResources(…)
函数完成相应初始化部分。
(5) createDexClassLoader(String dexPath)
利用DexClassLoader
加载插件,DexClassLoader 初始化函数如下:
public DexClassLoader (String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent)
其中dexPath
为插件的路径。
optimizedDirectory
优化后的dex
存放路径。这里将路径设置为当前 App 应用程序数据目录下名为dex
的子目录中。
libraryPath
为 Native Library 存放的路径。这里将路径设置为mNativeLibDir
属性,其在getInstance(Context)
函数中已经初始化。
parent
父 ClassLoader,ClassLoader 采用双亲委托模式查找类,具体加载方式可见