`

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。

分享到:
评论

相关推荐

    apk加壳和动态加载技术

    动态加载技术 该技术在Java中是一个比较成熟的技术,而Android中该技术还没有被大家充分利用起来。该技术思想主要分为以下几步: 1.将核心代码编译成dex文件的Jar包 2. 对jar包进行加密处理 3.在程序主入口利用NDK...

    《Android系统源代码情景分析》

    《Android系统源代码情景分析》随书光盘内容(源代码) 目录如下: 第1篇 初识Android系统 第1章 准备知识 1.1 Linux内核参考书籍 1.2 Android应用程序参考书籍 1.3 下载、编译和运行Android源代码 ...

    Android高级编程--源代码

    作为使用androidsdk构建这些应用程序的实用指南书籍,《android高级编程》从始至终穿插了一系列示例项目,每个项目都引入android的新功能和新技术,以助您达到最圆满的学习效果。书中介绍android的所有基本功能,并...

    Android系统源代码情景分析光盘

    Android系统源代码情景分析光盘资料 目录 第1篇初识Android系统 第1章 准备知识................................................................ 2 1.1 Linux内核参考书籍......................................

    Android系统源代码情景分析-罗升阳-源码

    《Android系统源代码情景分析》随书光盘内容(源代码) 目录如下: 第1篇 初识Android系统 第1章 准备知识 1.1 Linux内核参考书籍 1.2 Android应用程序参考书籍 1.3 下载、编译和运行Android源代码 1.3.1 下载...

    Android代码-安卓结合HTML5快速开发APP

    2,这是我结合Android与H5技术,利用webview,viewpager与PagerSlidingTab,文章均来自于微信热文精选。 3,webview本地缓存,没有网时也能浏览之前加载过的新闻 4,activity里用viewpager,viewpager的每个条目都是...

    Android代码-ApkProtect

    APK安全加固是面向移动应用程序的深度安全保护服务,可以为您的APP穿上一层“软猬铠甲”,通过加密、加壳、RPC、动态加载等技术为您的应用进行全方位安全保护,有效防止逆向工程、反编译、嵌入病毒、非法扣费等恶意...

    Android代码-外包项目聚合信息

    外包集中营 整合多个软件外包平台项目信息,替你筛选优质项目 关于我们 功能 首页 展示你关注的外包信息,比如: 移动app、网站开发、微信/小程序。...common            公用代码 a

    Android代码-AppInit

    AppInit 用于解决美团收银 B 端 App 在业务演进过程中的实际问题,取得了不错的效果,因此我们决定将其开源,希望更多技术同行一起开发,应用到更广泛的场景里去。 背景 随着业务的快速发展,新项目新业务不断出现,...

    android解决加载图片内存溢出

    android 解析加载图片是一个常用的技术 但是如何优化 这是一个问题 想要用户体验好必须加载速度快 稳定 那么你看了这个代码就明白了

    Adroid动态加载Apk-插件化技术框架(动态代理方案)源码

    Android动态加载Apk-插件化技术(动态代理方案) 一. 什么是插件化 1. 主App(宿主App)加载插件apk的实现 2. 每个业务组件模块形成一个独立的Apk, 然后通过主App动态加载部署业务组件模块Apk的一种方案 二.插件化的...

    Android代码-Componentization

    随着业务的发展 App 开发技术也越来越成熟,对开发者来说 App 代码量也迅速地增长到一个数量级。对于如何架构 App 已经每个开发者面临的实际问题。好的架构可以提高开发者的效率,降低维护成本。 由于业务增长引起...

    Android代码-玩安卓客户端

    添加Fragment懒加载V1.0.1 文章详情页添加收藏功能 修复几个小Bug 优化部分代码V1.0 首页 知识体系 登录、注册 收藏 搜索 分享 功能 登录和注册 登录 注册首页相关 首页文章列表 首页banner体系 知识体系 知识...

    android源代码 Rss阅读器

    原创 自己写的源代码 导入即可用 Android 应用程序开发技术 rss阅读器实例 实现通过Rss地址阅读到新闻 网页等内容 可自己添加Rss地址 并保存至数据库 功能强大

    Android代码-利用Android AccessibilityService 实现自动加好友,拉人进群聊

    版本的按键精灵,其实就是点点点,本项目仅用于技术研究学习之用,请勿用于商业用途,所以项目不会做任何适配! 只保证在笔者手机上是可以正常运行的,代码开源,有问题或者建议欢迎提issues。运行项目后,需要启用...

    Android代码-Android-Material-Sloth

    Sloth 项目介绍: 这是一个用于Android学习的项目,使用了代码家的...有新的技术也会不断往项目里面添加. 希望能通过这个项目技术有一定得提升.当然你也可以安装到手机上,空闲时间看看技术文章和妹纸. 效果:

    Android代码-Github客户端

    A android app client for Github. 效果图 关于项目 Git.NB现以升级到2.0,功能大有升级,UI大改版本。各种有趣的操作,欢迎下载代码试完。 如果发现有任何问题和建议,随时欢迎Email或者开Issues, 如果有版权问题...

    Android代码-WebView缓存例子

    做一个webview的页面,功能类似于微信发朋友圈一样,要求能上传本地图片到webview中进行展示,并按用户喜好添加和删除,当用户点击发布的时候,将这些图片上传至阿里云oss,收到oss响应后封装页面信息提交给服务器 ...

    Android代码-SimpleGitHub

    其中会使用一些比较新的流行技术,当做学习 目前使用到的(used Library) 响应式编程:rxJava 数据库:realm,尽管有些坑,但还是在继续使用(线程问题实在麻烦,还是换回SQLite) orm:greenDao 图片加载:glide 网络...

    Android Studio实现一个校园图书管理系统,满分课设!

    只能将学生、图书、工作人员信息通过Litepal写进手机自带的数据库中, 没有实现服务器-客户端模式,使得每个用户访问的图书馆信息能够相同, 其实原来的初衷是想把访问服务器数据库的功能加入,但是由于技术不到位,...

Global site tag (gtag.js) - Google Analytics