Android插件化动态加载apk

 

什么是插件化动态加载apk?

支付宝是万能的,既可以淘票票看电影,又可以买车票,还可以开共享单车,这些都是支付宝的开发人员开发维护的么?显然不是,那么他是怎么做到的呢?是使用了动态加载apk的解决方案

怎么动态加载apk呢?

支付宝作为一个宿主apk提前将要集成的apk作为一个插件(plugin)下载到本地,然后当使用该plugin(apk)的时候再去加载对应plugin(apk)的资源文件以及对应的native页面。就是不去安装plugin(apk)就可以直接运行该plugin(apk)中的页面。

动态加载plugin(apk)分析

怎么调用一个apk中的页面呢?我们可以动态加载plugin中的文件资源使其以伪宿主身份运行在宿主apk中。以加载一个Activity页面来作为例子。

要让插件中的Activity运行起来,我们可以在宿主中创建一个Activity,然后去手动创建插件中的Acitivity的实例,然后使用宿主apk中Activity的生命周期去调用插件Activity的生命周期,这样就可以让Plugin中的Activity运行起来。

<li>Plugin中Activity生命周期的处理

我们可以在宿主中使用一个特殊的Activity,这个Activity是一个空壳,没有任何页面。但是它有实际的Activity的生命周期,这样我们可以通过这个Activity的生命周期去调用我们自己创建的Plugin中的Activity中的生命周期,实现了Plugin中的Activity的伪生命周期。这个宿主Activity命名为ProxyActivity。

<li>Plugin中资源文件的获取

使用AssetManager去得到Plugin包中的资源文件。

加载Plugin实现

第一步 PluginInterface

我们的宿主要提供一套标准,这套标准用来规范宿主与Plugin之间的上下文以及生命周期关系的标准。我们称之为:PluginInterface。这个标准涉及到Activity生命周期以及上下文,定义如下:

public interface PluginInterface {
    void onCreate(Bundle saveInstance);
    void attachContext(FragmentActivity context);
​
    void onStart();
​
    void onResume();
​
    void onRestart();
​
    void onDestroy();
​
    void onStop();
​
    void onPause();
}

我们新建一个android依赖库plugin,依赖库中只有一个PluginInterface接口,这个interface作为一个依赖库的形式存在于宿主与Plugin中。宿主gradle与plugin gradle都引用这个库。

compile project(':plugin')

为了使得编译起来更方便,我这里将宿主apk,插件plugin(项目中称之为otherapk)与依赖库plugin放在同一个项目下,只不过这个项目有两个module。

第二步 PluginManager

宿主需要一套工具,来管理加载PluginApk以及获取PluginApk中资源文件,就叫PluginManager。

获取PluginApk的字节码文件对象

DexClassLoader是一个类加载器,可以用来从.jar和.apk文件中加载class。可以用来加载执行没用和应用程序一起安装的那部分代码。

我们要拿到Plugin中的字节码文件对象,需要拿到Plugin对应的DexClassLoader可以使用DexClassLoaderDexClassLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent)方法。

dexPath:被解压的apk路径,不能为空。

optimizedDirectory:解压后的.dex文件的存储路径,不能为空。这个路径强烈建议使用应用程序的私有路径,不要放到sdcard上,否则代码容易被注入攻击。

libraryPath:os库的存放路径,可以为空,若有os库,必须填写。

parent:父亲加载器,一般为context.getClassLoader(),使用当前上下文的类加载器。

获取PluginApk中的Resource

我们可以使用Resource提供的下面的构造:

public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) {
        this(assets, metrics, config, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO);
    }

由于要获取PluginApk中的资源,所以这个assets对象应当是PluginApk中的资源对象;而对于一款手机的DisplayMetrics和Configuration来说,无论是宿主还是PluginApk获取的值都是一样的,所以可以使用宿主的值。

获取AssetManager对象

public final int addAssetPath(String path) {
        synchronized (this) {
            int res = addAssetPathNative(path);
            makeStringBlocks(mStringBlocks);
            return res;
        }
    }

这个path也就是PluginApk包在手机中的位置,由于这个方法被hide 了,我们需要使用反射。

AssetManag
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值