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. 什么是SVN(Subversion)? 为什么要用SVN? (2011-09-05 15:09:47) 转载 ▼

    转自:http://blog.sina.com.cn/s/blog_54ccd3500100tkvo.html 什么是SVN(Subversion)? 有一个简单但不十分精确比喻: SVN = 版本控 ...

  2. [NOIP2015模拟10.22] 最小代价 解题报告 (最小生成树)

    Description 给出一幅由n个点m条边构成的无向带权图.其中有些点是黑点,其他点是白点.现在每个白点都要与他距离最近的黑点通过最短路连接(如果有很多个黑点,可以选取其中任意一个),我们想要使得 ...

  3. 2.LINUX常用命令

    Linux 必备指令摘要一般用户指令/bin 指令 功能说明 范例 bash GNU Bouren-Again Shell bash shell_script cat 观看一般文本文件 cat fil ...

  4. Kali linux 2016.2(Rolling)里Metasploit的数据库

    为什么要在Metasploit里提及到数据库? 大家都知道,这么多信息,我怎样才能把它们整理好并保存起来?怎么展现给老大看,最后怎么体现在要提交的渗透测试报告中呢?   你的担忧真的很有必要,因为啊, ...

  5. vuejs fatherandson

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  6. Unity3D 之 C# 脚本

    引用类型 :class ,interface, 数组 , delegate , object ,string (object 是C#中所有类型,包括所有的值类型和引用类型的根类,string 类型是一 ...

  7. 888E - Maximum Subsequence 中途相遇法

    Code: #include<cstdio> #include<algorithm> #include<cstring> #include<string> ...

  8. lhgDialog使用--loading提示(不自动关闭)

    使用lhgDialog时,发现有一个$.dialog.tips()方法可以实现loading样式的提示,但是存在默认关闭时间.方法如下图所示, 为了实现不自动关闭的方法,查看了相应的源码后,实现不关闭 ...

  9. JS触发按钮事件

    前台代码: <asp:Button ID="btnSaveBattery" runat="server" Text="保存" OnCl ...

  10. Python对象引用的所有权

    目录 引用所有权 传递引用的所有权--返回值 出借引用的所有权--返回值 占据引用的所有权--参数 出借引用的所有权--参数 引用所有权 谁持有对象引用的所有权,谁就要对对象负责. 引用的所有权对函数 ...