【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

相关文章

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作

动态规划---打家劫舍

题目: 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 思路: 动态规划五部曲: 1.确定dp数组及含义 dp数组是一维数组,dp[i]代表

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略 1. 特权模式限制2. 宿主机资源隔离3. 用户和组管理4. 权限提升控制5. SELinux配置 💖The Begin💖点点关注,收藏不迷路💖 Kubernetes的PodSecurityPolicy(PSP)是一个关键的安全特性,它在Pod创建之前实施安全策略,确保P