本篇博客说一下我们的宿主APP怎样加载别的APK文件。
首先需要说一些知识点,我们的Java文件要想在Android环境运行,需要将.java文件通过转为class文件,然后为了能在DVM上面运行,再转为dex文件。同理反过来,我们在代码中要操作的基本都是class文件,但是class文件怎么来呢? 从DexClassLoader加载获取。
DexClassLoader和PathClassLoader什么区别呢?

  1. DexClassLoader可以加载jar/apk/dex,可以从SD卡中加载未安装的apk
  2. PathClassLoader只能加载系统中已经安装过的apk

至于具体源码区别:建议读一下DVM源码。本篇不再赘述,之后专门写一篇博客讲述DexClassLoader和PathClassLoader的区别。

**

加载外部APK

**
其实这个场景是这样的:

  1. 从服务器下载APK,保存在我们的手机储存卡内
  2. 读取APK文件,然后生成对应的DexClassLoader
  3. 通过DexClassLoader的loadClass方法读取插件APK dex中的任何一个类。

说干就干,首先我们创建一个项目MyPluginProject,在这个项目中创建一个Java类:TestModel

/*** author: liumengqiang* Date : 2019/7/27* Description :*/
public class TestModel {private String name;public void setName(String name) {this.name = name;}public String getName() {return name;}
}

然后打包此项目生成:app-debug.apk。
由于插件APK基本都是从服务器下载,为了模拟这个场景,我们需要在宿主项目中新建一个assets文件,将插件APK复制进去,然后在复制到宿主APP的data/data/files文件夹下。
注意:这里之前钻牛角尖了,就是为什么我不直接手动将插件APK直接复制到data/data/files,文件夹下呢? 说干就干,但是问题来了,我在文件管理器的当前Android/data/<包名>下找不到此包名,也就是说没有生成包路径。 说实话卡了很长时间,我一直以为是不是版本问题,最后求助朋友,折腾了一番,最终手动调用:getExternalCacheDir即可解决。生成路径是生成了,那么接下来就是复制APK了吧,当我复制到里面之后,我发现,尼玛,死活获取不到复制进去的插件APK,真的是活见鬼,最后猛然发现那个包路径是系统路径!!!这个需要系统签名权限才能有权限访问! 而我们代码中assets中的APK是复制到内存中。。。
然后在我们的宿主项目中,新建assets,然后将app-debug.apk复制进去。接下来就是将app-debug.apk加载到内存中。

    /*** 把Assets里面得文件复制到 /data/data/files 目录下** @param context* @param sourceName*/public static void extractAssets(Context context, String sourceName) {AssetManager am = context.getAssets();InputStream is = null;FileOutputStream fos = null;try {is = am.open(sourceName);File extractFile = context.getFileStreamPath(sourceName);fos = new FileOutputStream(extractFile);byte[] buffer = new byte[1024];int count = 0;while ((count = is.read(buffer)) > 0) {fos.write(buffer, 0, count);}fos.flush();} catch (IOException e) {e.printStackTrace();} finally {closeSilently(is);closeSilently(fos);}}

然后在APP启动的时候调用:

extractAssets(this, "app-debug.apk");

接下来创建DexClassLoader ,加载插件app-debug.apk中的dex:

        try {File extractFile = this.getFileStreamPath("app-debug.apk");String dexpath = extractFile.getPath();File fileRelease = getDir("dex", 0); //0 表示Context.MODE_PRIVATEclassLoader = new DexClassLoader(dexpath,fileRelease.getAbsolutePath(), null, getClassLoader());} catch (Throwable e) {e.printStackTrace();}

然后就是我们通过反射获取TestModel类,并且设置setName,获取getName:

   private void modelTestClick() {try {Class<?> loadClass = classLoader.loadClass("com.liumengqiang.mypluginproject.TestModel");Object newInstance = loadClass.newInstance();Class[] paramClass = new Class[]{String.class};Object[] valueObj = new Object[]{"Hello world"};Method setName = loadClass.getMethod("setName", paramClass);setName.setAccessible(true);setName.invoke(newInstance, valueObj);Method getName = loadClass.getMethod("getName");getName.setAccessible(true);String value = (String) getName.invoke(newInstance);Toast.makeText(this, "Value:" + value, Toast.LENGTH_SHORT).show();} catch (Throwable e) {e.printStackTrace();}}

至此就调用到了插件中的类。

细看上面的代码丑得一逼,每次都要通过反射获取类里面的方法,如果这个类很多方法,那代码量可想而知。可以升级一下:面向接口编程

**

面向接口编程

**
面向接口编程是面向对象编程的一种实现方式,它的核心思想是将抽象与实现分离,从组件的级别来设计代码,达到高内聚低耦合的目的。最简单的面向接口编程方法是,先定义底层接口模块,再定义高层实现模块。具体的之后在单独开博客讲述。
上述代码优化的思路:

  1. 创建一个interfaceLibrary, 是个Library,里面定义一个接口:IBaseInterface :
/**1. author: liumengqiang2. Date : 2019/8/203. Description :*/
public interface IBaseInterface {void setName(String name);String getName();
}
  1. 然后插件MyPluginProject以及宿主项目分别依赖此Library
  2. 对于插件MyPluginProject中TestModel类,实现IBaseInterface接口:
/*** author: liumengqiang* Date : 2019/7/27* Description :*/
public class TestModel implements IBaseInterface {private String name;@Overridepublic void setName(String name) {this.name = name;}@Overridepublic String getName() {return name;}
}
  1. 然后打包APK,按照上面的流程。
  2. 最后在宿主反射获取TestModel类的时候,就可以通过接口接收创建的TestModel类了。
        try {Class<?> loadClass = classLoader.loadClass("com.liumengqiang.mypluginproject.TestModel");IBaseInterface newInstance = (IBaseInterface) loadClass.newInstance();newInstance.setName("hello world");Toast.makeText(this, "" + newInstance.getName(), Toast.LENGTH_SHORT).show();} catch (Throwable e) {e.printStackTrace();}

是不是精简了看着。 这是简单的面向接口编程,也就是先定义接口:然后再实现。

Android开发交流:

Android 插件化开发——宿主APP加载APK插件相关推荐

  1. Android插件化开发之动态加载本地皮肤包进行换肤

    Android插件化开发之动态加载本地皮肤包进行换肤 前言: 本文主要讲解如何用开源换肤框架 android-skin-loader-lib来实现加载本地皮肤包文件进行换肤,具体可自行参考框架原理进行 ...

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

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

  3. Android插件化开发之动态加载的类型

    https://segmentfault.com/a/1190000005113493 基本信息 Author:kaedea GitHub:android-dynamical-loading 现在网络 ...

  4. Android插件化开发之动态加载技术系列索引

    动态加载介绍 在Android开发中采用动态加载技术,可以达到不安装新的APK就升级APP功能的目的,可以用来到达快速发版的目的,也可以用来修复一些紧急BUG. 现在使用得比较广泛的动态加载技术的核心 ...

  5. Android插件化开发之动态加载技术简单易懂的介绍方式

    转载地方:https://segmentfault.com/a/1190000004062866 基本信息 Author:kaedea GitHub:android-dynamical-loading ...

  6. Android插件化开发之动态加载基础之ClassLoader工作机制

    类加载器ClassLoader 早期使用过Eclipse等Java编写的软件的同学可能比较熟悉,Eclipse可以加载许多第三方的插件(或者叫扩展),这就是动态加载.这些插件大多是一些Jar包,而使用 ...

  7. 安卓插件化学习 - 类的加载

    安卓插件化学习 - 类的加载 引言 一.类的加载 1. 原理 2. 代码 2.1 宿主apk代码 2.1.1 插件管理器 2.1.2 配置文件 2.1.3 插件初始化 2.1.4 调用插件方法 2.2 ...

  8. 插件化中Activity的加载

    插件化中Activity的加载 前面一系列的文章中我们介绍了Android系统资源加载流程,最后引出插件化中资源加载的方法,完成了『资源动态加载』这一大块的介绍.本系列文章将重点介绍『代码动态加载』, ...

  9. 免Root 实现App加载Xposed插件的工具Xpatch源码解析(一)

    前言 Xpatch是一款免Root实现App加载Xposed插件的工具,可以非常方便地实现App的逆向破解(再也不用改smali代码了),源码也已经上传到Github上,欢迎各位Fork and St ...

最新文章

  1. 查看CentOS的网络带宽出口
  2. android shape的使用
  3. 在linux系统下安装两个nginx以及启动、停止、重起
  4. 如何快速清理docker资源
  5. ABAP BSP的工具类CL_BSP_UTILITY
  6. [Android] 输入系统(三):加载按键映射
  7. __dopostback
  8. 2012年5月份第2周51Aspx源码发布详情
  9. 网络流20+4题解题报告(已更前20题)
  10. vs2015 中无法链接strcasecmp 和 strncasecmp的解决办法
  11. java解压zip文件程序_java 解压zip文件
  12. 简述osi参考模型各层主要功能_简述OSI参考模型及各层的功能
  13. Linux cmd 常用快捷键(自用)
  14. 使用WDF开发驱动程序
  15. 【Java】Spring init-method和@PostConstruct 原理
  16. mocha.opts配置文件
  17. c语言lst文件,Keil C51 之LST文件
  18. JDO与JPA哪个更好?
  19. python去除图片马赛克_python 检测图片是否有马赛克
  20. nginx启动无反应

热门文章

  1. WeX5打包项目为app步骤详解
  2. 关于winfrom控件自动排序(想怎么排就怎么排)
  3. 猿创征文 | Java知识【Java基础语法】
  4. Visual Studio基本使用
  5. 全国计算机等级考试二级Java模拟试卷
  6. GitChat · 软件工程 | 一小时教你学会 Maven 项目的构建与管理
  7. 手持振弦传感器VH03读数仪测读仪频率仪地质灾害土木工程自动化监测
  8. uniapp微信小程序瀑布流布局
  9. Redis指南——03入门(上)
  10. B.FRIENDit:键盘轴体零死角剖析,挑选机械式键盘不必烦恼!