Android插件化(二):使用DexClassLoader动态载入assets中的apk

简单介绍

上一篇博客讲到。我们能够使用MultiDex.java载入离线的apk文件。须要注意的是,apk中的类是载入到当前的PathClassLoader其中的,假设apk文件过多。可能会出现ANR的情况。那么。我们能不能使用DexClassLoader载入apk呢?当然是能够的!

首先看一下Doc文档.

A class loader that loads classes from .jar and .apk files containing a classes.dex entry. This can be used to execute code not installed as part of an application.

也就是说,DexClassLoader能够载入一个含有classes.dex文件的压缩包,既能够是jar也能够是apk。那么载入一个离线的apk文件须要注意哪些呢?

  • 1.DexClassLoader的构造方法:

    DexClassLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent)

  • 2.私有文件夹

    This class loader requires an application-private, writable directory to cache optimized classes.

了解到上述两点,我们就能够依据DexClassLoader所须要的參数。动态载入assets中的apk了。

源代码

BundleClassLoaderManager

该类主要是负责管理这些DexClassLoader的,首先,我们定义了一个叫做BundleDexClassLoader的类,它继承自DexClassLoader,用于载入离线的apk文件。每个apk文件相应一个BundleDexClassLoader,而BundleClassLoaderManager则保存了一个List,在载入的时候。用于查找类。

详细代码例如以下:

package net.mobctrl.hostapk;

import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.List; import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build; /**
* @Author Zheng Haibo
* @PersonalWebsite http://www.mobctrl.net
* @version $Id: BundleClassLoaderManager.java, v 0.1 2015年12月11日 下午7:30:59
* mochuan.zhb Exp $
* @Description
*/
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public class BundleClassLoaderManager { public static List<BundleDexClassLoader> bundleDexClassLoaderList = new ArrayList<BundleDexClassLoader>(); /**
* 载入Assets里的apk文件
* @param context
*/
public static void install(Context context) {
AssetsManager.copyAllAssetsApk(context);
// 获取dex文件列表
File dexDir = context.getDir(AssetsManager.APK_DIR,
Context.MODE_PRIVATE);
File[] szFiles = dexDir.listFiles(new FilenameFilter() { @Override
public boolean accept(File dir, String filename) {
return filename.endsWith(AssetsManager.FILE_FILTER);
}
});
for (File f : szFiles) {
System.out.println("debug:load file:" + f.getName());
BundleDexClassLoader bundleDexClassLoader = new BundleDexClassLoader(
f.getAbsolutePath(), dexDir.getAbsolutePath(), null,
context.getClassLoader());
bundleDexClassLoaderList.add(bundleDexClassLoader);
}
} /**
* 查找类
*
* @param className
* @return
* @throws ClassNotFoundException
*/
public static Class<? > loadClass(Context context,String className) throws ClassNotFoundException {
try {
Class<?> clazz = context.getClassLoader().loadClass(className);
if (clazz != null) {
System.out.println("debug: class find in main classLoader");
return clazz;
}
} catch (Exception e) {
e.printStackTrace();
}
for (BundleDexClassLoader bundleDexClassLoader : bundleDexClassLoaderList) {
try {
Class<?> clazz = bundleDexClassLoader.loadClass(className);
if (clazz != null) {
System.out.println("debug: class find in bundle classLoader");
return clazz;
}
} catch (Exception e) {
e.printStackTrace();
}
}
throw new ClassCastException(className + " not found exception");
}
}

注意点:

  • 1.install方法

    install方法主要是将assets中的apk所有复制到私有文件夹,然后再遍历私有文件夹。使用BundleDexClassLoader载入apk文件。然后将这些BundleDexClassLoader保存到数组中。

  • 2.loadClass方法

    该方法先从当前的ClassLoader中查找须要的类,假设找不到,在从List中遍历查找。

DEMO执行

在MainActivity中,我们能够通过例如以下方式。调用apk类中的方法:

      private void loadApk() {
try {
Class<? > clazz = BundleClassLoaderManager.loadClass(getApplicationContext(),
"net.mobctrl.normal.apk.Utils");
Constructor<?> constructor = clazz.getConstructor();
Object bundleUtils = constructor.newInstance(); Method printSumMethod = clazz.getMethod("printSum", Context.class,
int.class, int.class, String.class);
printSumMethod.setAccessible(true);
Integer sum = (Integer) printSumMethod.invoke(bundleUtils,
getApplicationContext(), 10, 20, "计算结果");
System.out.println("debug:sum = " + sum);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}

与MultiDex不同一时候。我们是通过BundleClassLoaderManager来载入类的,而不是当前的ClassLoader。

改进方案

正如BundleClassLoaderManager中的loadClass方法。事实上我们创建一个ClassLoader对象,通过重写当前ClassLoader的findClass方法就可以,然后在Override的findClass方法中,首先从当前ClassLoader中查找类,然后再从BundleDexClassLoader中遍历查找,这样既能够在Host项目中调用Bundle中的类,也能够在Bundle中调用Host中的类。


mClassLoader = new ClassLoader(super.getClassLoader()) { @Override
protected Class<?> findClass(String className)
throws ClassNotFoundException {
Class clazz = BundleClassLoaderManager.loadClass(context,className);
if (clazz == null) {
throw new ClassNotFoundException(className);
}
return clazz;
}
};

总结

上一篇博客和这一篇博客将的都是类的载入。假设所须要载入的类都是工具类,不须要载入资源等,那么上面的方案都没啥问题。可是假设载入的类是Fragment或者Activity等UI,须要引用资源文件,这又改怎样处理呢?

下一篇博文:Android资源的离线载入。

參考

1.BaseDexClassLoader源代码

Android插件化(二):使用DexClassLoader动态载入assets中的apk的更多相关文章

  1. Android插件化(二):OpenAtlas插件安装过程分析

    Android插件化(二):OpenAtlas插件安装过程分析   转 https://www.300168.com/yidong/show-2788.html   核心提示:在前一篇博客 Andro ...

  2. Android插件化(三):OpenAtlas的插件重建以及使用时安装

    Android插件化(三):OpenAtlas的插件重建以及使用时安装 转 https://www.300168.com/yidong/show-2778.html    核心提示:在上一篇博客 An ...

  3. Android 插件化 动态升级

    最新内容请见原文:Android 插件化 动态升级 不少朋友私信以及 Android开源交流几个 QQ 群 中都问到这个问题,这里简单介绍下 1.作用 大多数朋友开始接触这个问题是因为 App 爆棚了 ...

  4. Android插件化(三)载入插件apk中的Resource资源

    Android载入插件apk中的Resource资源 简单介绍 怎样载入未安装apk中的资源文件呢?我们从android.content.res.AssetManager.java的源代码中发现,它有 ...

  5. Android插件化开发

    客户端开发给人的印象往往是小巧,快速奔跑.但随着产品的发展,目前产生了大量的门户型客户端.功能模块持续集成,开发人员迅速增长.不同的开发小组开发不同的功能模块,甚至还有其他客户端集成进入.能做到功能模 ...

  6. Android插件化开发---执行未安装apk中的Service

    欢迎各位增加我的Android开发群[257053751​] 假设你还不知道什么叫插件化开发.那么你应该先读一读之前写的这篇博客:Android插件化开发,初入殿堂 上一篇博客主要从总体角度分析了一下 ...

  7. Android 插件化和热修复知识梳理

    概述 在Android开发中,插件化和热修复的话题越来越多的被大家提及,同时随着技术的迭代,各种框架的发展更新,插件化和热修复的框架似乎已经日趋成熟,许多开发者也把这两项技术运用到实际开发协作和正式的 ...

  8. Android插件化技术——原理篇

    <Android插件化技术——原理篇>     转载:https://mp.weixin.qq.com/s/Uwr6Rimc7Gpnq4wMFZSAag?utm_source=androi ...

  9. android 插件化框架VitualAPK

    推荐阅读: 滴滴Booster移动App质量优化框架-学习之旅 一 Android 模块Api化演练 不一样视角的Glide剖析(一) LeakCanary 与 鹅场Matrix ResourceCa ...

随机推荐

  1. Car Talk1

    This question is based on a Puzzler that was broadcast on the radioprogram Car Talk1: “I was driving ...

  2. Linux基础04

    ** Linux基本操作常用命令(四) ** Linux系统管理命令 1.top:查看系统资源,每隔三秒刷新一次,按q:退出浏览状态 2.free:查看内存信息,-m,以MB单位显示 3.netsta ...

  3. 使用Visual Studio2012调试Redis源码

    Redis是一款C语言编写Key-Value存储系统,基于BSD协议开放源码,其源码托管在github上,大概有三万行. 源码地址:https://github.com/antirez/redis 源 ...

  4. vue 学习笔记1 入门

    可以在 JSFiddle上在线学习vue 注意:所演示的示例,都是在JS中将Vue实例绑定至HTML中的指定元素,然后再通过Vue实例中data内的属性或者methods中的方法,来对所绑定元素的子元 ...

  5. BZOJ 4373算术天才⑨与等差数列(线段树)

    题意:给你一个长度为n的序列,有m个操作,写一个程序支持以下两个操作: 1. 修改一个值 2. 给出三个数l,r,k, 询问:如果把区间[l,r]的数从小到大排序,能否形成公差为k的等差数列. n,m ...

  6. CF732F Tourist Reform(边双联通)

    题意 在一张有向图中,设 ri 为从点 i 出发能够到达的点的数量. 定义有向图的“改良值”为 ri 的最小值. 现给出一张无向图,要求给每条边定一个方向,使产生的有向图“改良值”最大. 输出 最大改 ...

  7. Unity 框架(一)

    当项目需求中,后期可能接入多种输入设备的时候,可以借鉴一下以下代码 using System.Collections; using System.Collections.Generic; using ...

  8. 二、 HBase核心功能模块。

      Hadoop 框架包含两个核心组件: HDFS 和 MapReduce 其中     HDFS                是文件存储系统,负责数据存储:     MapReduce     是 ...

  9. jquery快速清除复选框、单选框的选中

    $(":checked").attr("checked", "");

  10. poj2031-Building a Space Station(最小生成树,kruskal,prime)

    Building a Space Station Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 5874   Accepte ...