【Android高级】DexClassloader和PathClassloader动态加载插件的实现

本文主要是介绍【Android高级】DexClassloader和PathClassloader动态加载插件的实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

(一)DexClassloader

一、基本概念:


         在Android中可以跟java一样实现动态加载jar,但是Android使用德海Dalvik VM,不能直接加载java打包jar的byte code,需要通过dx工具来优化Dalvik byte code。
         Android在API中给出可动态加载的有:DexClassLoader 和 PathClassLoader。
         DexClassLoader:可加载jar、apk和dex,可以从SD卡中加载(本文使用这种方式)
         PathClassLoader:只能加载已经安装搭配Android系统中的apk文件

二、实施
        编写接口:Dynamic

package com.smilegames.dynamic.interfaces;public interface Dynamic {public String helloWorld();public String smileGames();public String fyt();
}
        编写实现类:DynamicTest
package com.smilegames.dynamic.impl;import com.smilegames.dynamic.interfaces.Dynamic;public class DynamicTest implements Dynamic {@Overridepublic String helloWorld() {return "Hello Word!";}@Overridepublic String smileGames() {return "Smile Games";}@Overridepublic String fyt() {return "fengyoutian";}}
三、打包并编译成dex
       将接口打包成jar:dynamic.jar( 只打包这Dynamic.java这一个接口
       将实现类打包成jar:dynamic_test.jar( 只打包DynamicTest.java这一个实现类
       将打包后的实现类(dynamic_test.jar)编译成dex:dynamic_impl.jar
              1、将dynamic_test.jar拷贝到SDK安装目录android-sdk-windows\platform-tools下( ps:如果platform-tools没有dx.bat,可拷贝到build-tools目录下有dx.bat的子目录
              2、执行以下命令:
dx --dex --output=dynamic_impl.jar dynamic_test.jar
             3、将dynamic.jar引入测试实例
             4、将dynamic_impl.jar放到模拟器或真机的sdcard

四、修改onCreate例子
       
    @Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//        或许activity按钮helloWorld = (Button) findViewById(R.id.helloWorld);smileGames = (Button) findViewById(R.id.smileGames);fyt = (Button) findViewById(R.id.fyt);/*使用DexCkassLoader方式加载类*/// dex压缩文件的路径(可以是apk,jar,zip格式)String dexPath = Environment.getExternalStorageDirectory().toString() + File.separator + "dynamic_impl.jar";// dex解压释放后的目录String dexOutputDirs = Environment.getExternalStorageDirectory().toString();// 定义DexClassLoader// 第一个参数:是dex压缩文件的路径// 第二个参数:是dex解压缩后存放的目录// 第三个参数:是C/C++依赖的本地库文件目录,可以为null// 第四个参数:是上一级的类加载器DexClassLoader dexClassLoader = new DexClassLoader(dexPath,dexOutputDirs,null,getClassLoader());Class libProvierClazz = null;// 使用DexClassLoader加载类try {libProvierClazz = dexClassLoader.loadClass("com.smilegames.dynamic.impl.DynamicTest");// 创建dynamic实例dynamic = (Dynamic) libProvierClazz.newInstance();} catch (Exception e) {e.printStackTrace();}helloWorld.setOnClickListener(new HelloWorldOnClickListener());smileGames.setOnClickListener(new SmileGamesOnClickListener());fyt.setOnClickListener(new FytOnClickListener());}private final class HelloWorldOnClickListener implements View.OnClickListener {@Overridepublic void onClick(View v) {if (null != dynamic) {Toast.makeText(getApplicationContext(), dynamic.helloWorld(), 1500).show();} else {Toast.makeText(getApplicationContext(), "类加载失败", 1500).show();}}}private final class SmileGamesOnClickListener implements View.OnClickListener {@Overridepublic void onClick(View v) {if (null != dynamic) {Toast.makeText(getApplicationContext(), dynamic.smileGames(), 1500).show();} else {Toast.makeText(getApplicationContext(), "类加载失败", 1500).show();}}}
       1、运行这段代码时4.0.1以上版本会报: java.lang.IllegalArgumentException: optimizedDirectory not readable/writable: /storage/sdcard0
      可以通过这个授权解决:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 
      2、授权之后又会报: java.lang.IllegalArgumentException: Optimized data directory /storage/sdcard0 is not owned by the current user. Shared storage cannot protect your application from code injection attacks.
      这个问题的原因是:在4.1系统由于This class loader requires an application-private, writable directory to cache optimized classes为了防止一下问题:
External storage does not provide access controls necessary to protect your application from code injection attacks.
所以加了一个判断Libcore.os.getuid() != Libcore.os.stat(parent).st_uid判断两个程序是不是同一个uid
 private DexFile(String sourceName, String outputName, int flags) throws IOException {if (outputName != null) {try {String parent = new File(outputName).getParent();
if (Libcore.os.getuid() != Libcore.os.stat(parent).st_uid) {throw new IllegalArgumentException("Optimized data directory " + parent+ " is not owned by the current user. Shared storage cannot protect"+ " your application from code injection attacks.");}} catch (ErrnoException ignored) {// assume we'll fail with a more contextual error later}}mCookie = openDexFile(sourceName, outputName, flags);mFileName = sourceName;guard.open("close");//System.out.println("DEX FILE cookie is " + mCookie);}
        解决方法是:指定dexoutputpath为APP自己的缓存目录
File dexOutputDir = context.getDir("dex", 0);
DexClassLoader dexClassLoader = new DexClassLoader(dexPath,dexOutputDir.getAbsolutePath(),null,getClassLoader());
  ps:第四步的问题最终是2导致的,所以只需用2的解决方案即可,不需要1的授权。

 执行结果自行尝试。。。


(二)PathClassloader


	public void dexLoad() {/** 使用DexClassLoader方式加载类 */// dex压缩文件的路径(可以是apk,jar,zip格式)String dexPath = Environment.getExternalStorageDirectory().toString()+ File.separator + "dynamic_temp.jar";// dex解压释放后的目录// String dexOutputDir = getApplicationInfo().dataDir;// String dexOutputDirs = Environment.getExternalStorageDirectory()// .toString();// 定义DexClassLoader// 第一个参数:是dex压缩文件的路径// 第二个参数:是dex解压缩后存放的目录// 第三个参数:是C/C++依赖的本地库文件目录,可以为null// 第四个参数:是上一级的类加载器File dexOutputDir = getApplicationContext().getDir("dex", 0);DexClassLoader cl = new DexClassLoader(dexPath,dexOutputDir.getAbsolutePath(), null, getClassLoader());Log.i("tag", dexOutputDir.getAbsolutePath());Class libProviderClazz;try {libProviderClazz = cl.loadClass("com.dynamic.impl.Dynamic");// dynamic = (IDynamic) libProviderClazz.newInstance();} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}// MyInterface dynamic;Class dynamic;public void pathLoad() {/** 使用PathClassLoader方法加载类 */// 创建一个意图,用来找到指定的apk:这里的"com.dynamic.impl是指定apk中在AndroidMainfest.xml文件中定义的<action name="com.dynamic.impl"/>Intent intent = new Intent("com.example.androidendyeartest.MainActivity", null);// 获得包管理器PackageManager pm = getPackageManager();List<ResolveInfo> resolveinfoes = pm.queryIntentActivities(intent, 0);// 获得指定的activity的信息ActivityInfo actInfo = resolveinfoes.get(0).activityInfo;// 获得apk的目录或者jar的目录String apkPath = actInfo.applicationInfo.sourceDir;// native代码的目录String libPath = actInfo.applicationInfo.nativeLibraryDir;// 创建类加载器,把dex加载到虚拟机中// 第一个参数:是指定apk安装的路径,这个路径要注意只能是通过actInfo.applicationInfo.sourceDir来获取// 第二个参数:是C/C++依赖的本地库文件目录,可以为null// 第三个参数:是上一级的类加载器PathClassLoader pcl = new PathClassLoader(apkPath, libPath,this.getClassLoader());// 加载类try {dynamic = pcl.loadClass("com.test.bean.MyBean");} catch (Exception exception) {exception.printStackTrace();}}
应用:

	@OnClick(R.id.b1)public void t1(View v) {if (dynamic != null) {// dynamic.show1(getApplicationContext());Method method;try {method = dynamic.getMethod("show1",Context.class);//Context context=getApplicationContext();method.invoke(dynamic.newInstance(), getApplicationContext());} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}} else {Toast.makeText(getApplicationContext(), "类加载失败", 1500).show();}}



这篇关于【Android高级】DexClassloader和PathClassloader动态加载插件的实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/330849

相关文章

SpringBoot集成redisson实现延时队列教程

《SpringBoot集成redisson实现延时队列教程》文章介绍了使用Redisson实现延迟队列的完整步骤,包括依赖导入、Redis配置、工具类封装、业务枚举定义、执行器实现、Bean创建、消费... 目录1、先给项目导入Redisson依赖2、配置redis3、创建 RedissonConfig 配

Python的Darts库实现时间序列预测

《Python的Darts库实现时间序列预测》Darts一个集统计、机器学习与深度学习模型于一体的Python时间序列预测库,本文主要介绍了Python的Darts库实现时间序列预测,感兴趣的可以了解... 目录目录一、什么是 Darts?二、安装与基本配置安装 Darts导入基础模块三、时间序列数据结构与

Python使用FastAPI实现大文件分片上传与断点续传功能

《Python使用FastAPI实现大文件分片上传与断点续传功能》大文件直传常遇到超时、网络抖动失败、失败后只能重传的问题,分片上传+断点续传可以把大文件拆成若干小块逐个上传,并在中断后从已完成分片继... 目录一、接口设计二、服务端实现(FastAPI)2.1 运行环境2.2 目录结构建议2.3 serv

C#实现千万数据秒级导入的代码

《C#实现千万数据秒级导入的代码》在实际开发中excel导入很常见,现代社会中很容易遇到大数据处理业务,所以本文我就给大家分享一下千万数据秒级导入怎么实现,文中有详细的代码示例供大家参考,需要的朋友可... 目录前言一、数据存储二、处理逻辑优化前代码处理逻辑优化后的代码总结前言在实际开发中excel导入很

SpringBoot+RustFS 实现文件切片极速上传的实例代码

《SpringBoot+RustFS实现文件切片极速上传的实例代码》本文介绍利用SpringBoot和RustFS构建高性能文件切片上传系统,实现大文件秒传、断点续传和分片上传等功能,具有一定的参考... 目录一、为什么选择 RustFS + SpringBoot?二、环境准备与部署2.1 安装 RustF

Nginx部署HTTP/3的实现步骤

《Nginx部署HTTP/3的实现步骤》本文介绍了在Nginx中部署HTTP/3的详细步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录前提条件第一步:安装必要的依赖库第二步:获取并构建 BoringSSL第三步:获取 Nginx

MyBatis Plus实现时间字段自动填充的完整方案

《MyBatisPlus实现时间字段自动填充的完整方案》在日常开发中,我们经常需要记录数据的创建时间和更新时间,传统的做法是在每次插入或更新操作时手动设置这些时间字段,这种方式不仅繁琐,还容易遗漏,... 目录前言解决目标技术栈实现步骤1. 实体类注解配置2. 创建元数据处理器3. 服务层代码优化填充机制详

Python实现Excel批量样式修改器(附完整代码)

《Python实现Excel批量样式修改器(附完整代码)》这篇文章主要为大家详细介绍了如何使用Python实现一个Excel批量样式修改器,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一... 目录前言功能特性核心功能界面特性系统要求安装说明使用指南基本操作流程高级功能技术实现核心技术栈关键函

Java实现字节字符转bcd编码

《Java实现字节字符转bcd编码》BCD是一种将十进制数字编码为二进制的表示方式,常用于数字显示和存储,本文将介绍如何在Java中实现字节字符转BCD码的过程,需要的小伙伴可以了解下... 目录前言BCD码是什么Java实现字节转bcd编码方法补充总结前言BCD码(Binary-Coded Decima

SpringBoot全局域名替换的实现

《SpringBoot全局域名替换的实现》本文主要介绍了SpringBoot全局域名替换的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录 项目结构⚙️ 配置文件application.yml️ 配置类AppProperties.Ja