在开发Android App的过程当中,可能希望实现插件式软件架构,将一部分代码以另外一个APK的形式单独发布,而在主程序中加载并执行这个APK中的代码。
实现这个任务的一般方法是:
// 加载类cls
Context pluginContext = mainContext.createPackageContext(PLUGIN_PKG, Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE);
ClassLoader loader = pluginContext.getClassLoader();
Class<?> cls = loader.loadClass(CLASS_NAME);
// 通过反射技术,调用cls中的方法,下面是一个示例,实际代码因情况而定
Object obj = cls.newInstance();
Method method = cls.getDeclaredMethod("someMethod");
method.invoke(obj);
但是,这个方法在Android 4.1及之后的系统中存在一些问题:对于收费应用,Google Play会将其安装在一个加密目录之下(具体就是/data/app-asec),而不是一个普通目录之下(具体就是/data/app);安装在加密目录中的应用,我们是无法使用上述方法来加载并执行代码的;而实际情况是,我们经常就是依靠插件应用来收费的。
解决上述问题的一个方案是:将插件的二进制代码拷贝到SD卡中,主程序从SD卡中加载并执行其代码。
实现这个任务的具体方法是:
Class<?> cls = null;
try {
// 尝试第一种方法
cls = loadClass1(mainContext, pkg, entryCls);
} catch (Exception e) {
// 尝试第二种方法
cls = loadClass2(mainContext, pkg, entryCls);
}
// 示例代码
Object obj = cls.newInstance();
Method method = cls.getDeclaredMethod("someMethod");
method.invoke(obj);// 第一种加载方法
private Class<?> loadClass1(Context mainContext, String pkg, String entryCls) throws Exception {
Context pluginContext = mainContext.createPackageContext(pkg, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
ClassLoader loader = pluginContext.getClassLoader();
return loader.loadClass(entryCls);
}
// 第二种加载方法
private Class<?> loadClass2(Context mainContext, String pkg, String entryCls) throws Exception {
Context pluginContext = mainContext.createPackageContext(pkg, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
String path = generatePluginDexPath(mainContext, pkg);
ensureFileExist(pluginContext, pkg, path);
// cacheDir必须是主程序的私有目录,否则DexClassLoader可能会拒绝加载
String cacheDir = mainContext.getApplicationInfo().dataDir;
ClassLoader parentLoader = pluginContext.getClassLoader();
DexClassLoader loader = new DexClassLoader(path, cacheDir, null, parentLoader);
return loader.loadClass(entryCls);
}
// 获取程序版本号
private int getVersionCode(Context context, String pkg) {
PackageInfo info = null;
int versionCode = 0;
try {
info = context.getPackageManager().getPackageInfo(pkg, PackageManager.GET_ACTIVITIES);
versionCode = info.versionCode;
} catch (Exception e) {}
return versionCode;
}
// 获取插件二进制代码的存储位置,注意做好版本控制;路径必须是以.dex结束,否则加载会出问题
private String generatePluginDexPath(Context context, String pkg) {
int version = getVersionCode(context, pkg);
String path = getMyAppPath() + ".classes/" + pkg + version + ".dex";
return path;
}
// 主程序在SD卡上的数据目录
private String getMyAppPath() {
return Environment.getExternalStorageDirectory().getAbsolutePath() + "/MyApp/";
}// 拷贝插件的二进制代码到SD卡
private void ensureFileExist(Context pluginContext, String pkg, String path) throws Exception {
File file = new File(path);
if(file.exists()) return;
file.getParentFile().mkdirs();
Resources res = pluginContext.getResources();
int id = res.getIdentifier("classes", "raw", pkg);
InputStream in = res.openRawResource(id);
FileOutputStream out = new FileOutputStream(file);
try {
byte[] buffer = new byte[1024 * 1024];
int n = 0;
while((n = in.read(buffer)) > 0) {
out.write(buffer, 0, n);
} out.flush();
} catch (IOException e) {
in.close();
out.close();
}
}
插件工程这边也需要做相应的修改:
1.编译插件工程;
2.将bin目录之下的classes.dex拷贝到/res/raw目录之下;
3.重新编译插件工程;
4.发布插件APK。
分享到:
相关推荐
动态加载.so库是NDK开发中的一个重要技术,它使得应用程序在运行时按需加载库文件,而不是在安装时静态链接。这种方式可以减小APK的大小,提高应用程序的启动速度,以及便于更新和管理库文件。 Android系统提供了`...
在Android开发中,动态加载JAR(Java Archive)文件是一种常见的技术,它允许应用程序在运行时加载和执行不在原始APK文件中的代码。这种技术有多种应用场景,例如更新功能、热修复、插件化框架等。下面将详细介绍...
总结,动态加载外部资源文件是Android高级开发中的一个重要技术,它允许应用在运行时灵活地获取和使用外部apk中的图片、文字和颜色等资源,提高了应用的可维护性和用户体验。然而,这也需要开发者对Android的资源...
本项目“android动态加载Listview”旨在提供一种更实用、更贴近实际应用场景的数据动态加载解决方案。 首先,我们要理解什么是动态加载。动态加载(也称为懒加载)是在用户滚动ListView时按需加载数据的技术。它...
在Android开发中,动态加载Class是一项重要的技术,它允许应用程序在运行时加载未知或更新的类,从而提高软件的灵活性和可扩展性。这在处理插件化、热修复或者模块化开发时尤为常见。本篇文章将深入探讨如何在...
在Android开发中,动态加载jar代码是一种常见的技术,它允许应用程序在运行时加载新的功能或者更新已有代码,而无需重新安装整个应用。这种方式可以提高应用的灵活性和可维护性,尤其是在处理大型应用或需要频繁更新...
在Android开发中,"动态加载dex"是一种技术,它允许应用程序在运行时加载新的或更新的Dalvik执行文件(.dex格式),而不必通过Google Play或其他应用商店进行完整应用的更新。这种技术对于大型应用或者需要频繁更新...
在Android系统中,动态加载机制是一种关键的技术,它允许应用程序在运行时加载或者卸载代码模块,极大地提高了软件的灵活性和可扩展性。这一技术在实现插件化开发中尤为重要,因为它能让应用在不更新主程序的情况下...
在Android开发领域,APK动态加载是一项关键技术,它允许应用程序在运行时动态地加载和执行代码,从而提高软件的灵活性和可维护性。本研究主要关注如何实现这一技术,以及其在实际应用中的优势和挑战。 动态加载的...
在Android应用开发中,Fragment是Android SDK...总的来说,动态加载Fragment是Android开发中非常常见且实用的技术,能够实现灵活的界面设计和导航。熟练掌握Fragment的使用和管理,能够提升应用的用户体验和开发效率。
总的来说,这个示例代码结合了动态加载资源和主题切换两种技术,旨在提高Android应用的可扩展性和个性化程度。通过学习和理解这段代码,开发者可以掌握如何在实际项目中实现类似的功能,从而提升应用的竞争力和用户...
在Android应用开发中,动态加载和插件化技术是一种高级的优化策略,它允许应用程序在运行时加载新的功能模块或更新代码,而无需重新安装整个应用。这种技术的核心在于将应用的一部分功能剥离出来,作为独立的插件...
在Android开发中,布局是构建用户界面的基本元素,通常我们会在XML文件中预先定义好界面的布局...在Android_function_Demo中,你可能能找到更多关于动态加载布局和分组的示例代码,帮助你进一步理解和应用这个概念。
在Android系统中,动态加载机制是一项关键的技术,它允许应用程序在运行时加载和卸载代码模块,极大地提高了软件的灵活性和可维护性。本篇主要探讨Android动态加载机制的实现原理,结合源码分析,帮助开发者更好地...
在Android平台上,动态加载APK的能力是一项非常实用的技术,它允许程序在运行时根据需要加载新的功能或插件。这通常涉及到对Android的类加载机制、 DexClassLoader 和反射API的深入理解。标题“android动态运行已...
在“Android-DynamicFeatureModules的Android动态代码加载示例”中,我们将会探索如何设置和使用DFMs。以下是一些关键知识点: 1. **创建Dynamic Feature Module**: - 在Android Studio中,可以通过选择“New > ...
在Android开发中,动态加载是实现插件化和热修复技术的关键部分,它允许应用程序在运行时加载新的功能或更新代码而无需重新安装整个应用。本文将深入探讨如何在已安装的APK中加载类和资源,这在提高用户体验和减少...
在Android应用开发中,动态加载jar或dex文件是一种常见的优化技术,它允许应用程序在运行时加载新的代码或库,而不必通过更新整个apk来实现功能的扩展或修复。这种技术通常用于热修复、插件化或者模块化开发,能够极...
在Android开发中,有时我们需要实现动态加载功能,例如在运行时添加或更新功能模块,而无需重新编译和发布整个应用程序。这就是动态加载jar文件的作用。动态加载不仅可以提高应用程序的灵活性,还可以降低更新成本,...
在Android开发中,动态加载控件是一项重要的技术,它允许应用程序在运行时根据需要加载和显示用户界面元素,而不是在编译时固定。这为开发者提供了更大的灵活性,特别是在处理复杂和可变的用户界面或者更新UI组件时...