Android动态加载代码技术

在开发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。

来自 :frydsh

Android动态加载代码技术的更多相关文章

  1. Android 插件技术:动态加载dex技术初探

    1.Android动态加载dex技术初探 http://blog.csdn.net/u013478336/article/details/50734108 Android使用Dalvik虚拟机加载可执 ...

  2. Android动态加载技术初探

    一.前言: 现在,已经有实力强大的公司用这个技术开发应用了,比如淘宝,大众点评,百度地图等,之所以采用这个技术,实际上,就是方便更新功能,当然,前提是新旧功能的接口一致,不然会报Not Found等错 ...

  3. 深入浅出Android动态加载jar包技术

    在实际项目中,由于某些业务频繁变更而导致频繁升级客户端的弊病会造成较差的用户体验,而这也恰是Web App的优势,于是便衍生了一种思路,将核心的易于变更的业务封装在jar包里然后通过网络下载下来,再由 ...

  4. Android 动态加载 (一) 态加载机制 案例一

    在目前的软硬件环境下,Native App与Web App在用户体验上有着明显的优势,但在实际项目中有些会因为业务的频繁变更而频繁的升级客户端,造成较差的用户体验,而这也恰恰是Web App的优势.本 ...

  5. 从高德 SDK 学习 Android 动态加载资源

    前不久跑去折腾高德 SDK 中的 HUD 功能,相信用过该功能的用户都知道 HUD 界面上的导航转向图标是动态变化的.从高德官方导航 API 文档中 AMapNaviGuide 类的描述可知,导航转向 ...

  6. Android动态加载jar、apk的实现

    前段时间到阿里巴巴参加支付宝技术分享沙龙,看到支付宝在Android使用插件化的技术,挺好奇的.正好这几天看到了农民伯伯的相关文章,因此简单整理了下,有什么错误希望大神指正. 核心类 1.1     ...

  7. android动态加载

    转载自: http://www.cnblogs.com/over140/archive/2012/03/29/2423116.html http://www.cnblogs.com/over140/a ...

  8. [转载] Android动态加载Dex机制解析

    本文转载自: http://blog.csdn.net/wy353208214/article/details/50859422 1.什么是类加载器? 类加载器(class loader)是 Java ...

  9. Android 动态加载 (二) 态加载机制 案例二

    探秘腾讯Android手机游戏平台之不安装游戏APK直接启动法 重要说明 在实践的过程中大家都会发现资源引用的问题,这里重点声明两点: 1. 资源文件是不能直接inflate的,如果简单的话直接在程序 ...

随机推荐

  1. HTML布局总结

    网页的三大元素结构(内容html标签)+表现(布局CSS)+行为(js) CSS选择器1.标记选择器2.类别选择器(.red)3.ID选择器(#name)4.复合选择器(交集选择器  标记选择器+类别 ...

  2. [C入门 - 游戏编程系列] 贪吃蛇篇(二) - 食物定义

    游戏中的食物没有那么多复杂属性,特别是贪吃蛇游戏中,我把食物看待的很简单: 1. 它必须属于世界,才能出现在世界.不可能一个不属于世界的食物,出现在世界中:但是可能存在着一个食物,它属于世界,但是却没 ...

  3. cf A. Vasily the Bear and Triangle

    http://codeforces.com/contest/336/problem/A #include <cstdio> #include <cstring> #includ ...

  4. 学习Haskell的一些资料

    趣学Haskell指南: http://fleurer-lee.com/lyah/chapters.htm 对应的英文原文: http://learnyouahaskell.com/chapters ...

  5. 【转】Gedit中文乱码

    原文网址:http://wiki.ubuntu.org.cn/Gedit%E4%B8%AD%E6%96%87%E4%B9%B1%E7%A0%81#.E5.91.BD.E4.BB.A4.E6.96.B9 ...

  6. 极简AWR报告收集指导

    1.以oracle用户登录oracle数据库,执行如下命令登录数据库: sqlplus / as sysdba 2.运行如下命令: @?/rdbms/admin/awrrpt.sql 3.出现如下信息 ...

  7. libeXosip2(1-3) -- How-To send or update registrations.

    How-To send or update registrations. The eXtented eXosip stack Initiate a registration To start a re ...

  8. 返回本机的外网ip地址

     ;             );             string ip = tempip.Replace("]", "").Replace(" ...

  9. poj 1149 pigs(最大流)

    题目大意:迈克在农场工作,农场有 m 个猪舍,每个猪舍有若干只猪,但是迈克不能打开任何一间猪舍.有 n 个顾客前来购买,每个顾客有最大的购买数量,每个顾客可以购买某些猪舍的猪,且顾客可以打开这些猪舍, ...

  10. Android之字符串的拆分-split

    字符串的拆分可以利用android的 split 来简单实现 具体看如下代码: String s3 = "Real-How-To"; String [] temp = null; ...