Android中的类装载器DexClassLoader
2016-03-03 18:06
471 查看
在java中,有个概念叫做“类加载器”(ClassLoader),它的作用就是动态的装载Class文件。标准的java sdk中有一个
ClassLoader类,借助这个类可以装载想要的Class文件,每个ClassLoader对象在初始化时必须制定Class文件的路径。
可能有人会问,在写程序的时候不是有import关键字可以引用制定的类吗?为何还要使用这个类加载器呢?
原因其实是这样的,使用import关键字引用的类必须符合以下两个条件
类文件必须在本地,当程序运行时需要次类时,这时类装载器会自动装载该类,程序员不需要关注此过程。
编译的时候必须有这个类文件,否则编译不通过。
如果想让程序在运行的时候动态调用怎么办呢?用import显示是不符合上面的两种要求的。此时ClassLoader就派上用场了。
关于java的Classloader的装载机制请参考此链接 http://www.iteye.com/topic/83978,最好到网上自行搜索一下。
http://www.artima.com/insidejvm/ed2/《Inside the Java Virtural Machine》
对于android应用程序,本质上使用的是java开发,使用标准的java编译器编译出Class文件,和普通的java开发不同的
地方是把class文件再重新打包成dex类型的文件,这种重新打包会对Class文件内部的各种函数表、变量表等进行优化,
最终产生了dex文件。dex文件是一种经过android打包工具优化后的Class文件,因此加载这样特殊的Class文件就需要特殊的类装载器,
所以android中提供了DexClassLoader类。
类装载器DexClassLoader类结构
继承关系:
A class loader that loads classes from
This can be used to execute code not installed as part of an application.
This class loader requires an application-private, writable directory to cache optimized classes. Use
Do not cache optimized classes on external storage. External storage does not provide access controls necessary to protect your application from code injection attacks.
翻译:这个类加载器用来从.jar和.apk类型的文件内部加载classes.dex文件。通过这种方式可以用来执行非安装的程序代码,作为程序的一部分进行运行。
这个装载类需要一个程序私有的,可写的文件目录去存放优化后的classes文件。通过Contexct.getDir(String, int)来创建
这个目录:
File dexOutputDir = context.getDir("dex", 0);
不要把优化优化后的classes文件存放到外部存储设备上,防代码注入攻击。
这个类只有一个构造函数:
Added in API level 3
Creates a
in Jar or APK files.
创建一个DexClassLoader用来找出指定的类和本地代码(c/c++代码)。用来解释执行在DEX文件中的class文件。
路径的分隔符使用通过System的属性
String separeater = System.getProperty("path.separtor");
Parameters
类装载器DexClassLoader的具体使用
这个类的使用过程基本是这样:
通过PacageMangager获得指定的apk的安装的目录,dex的解压缩目录,c/c++库的目录
创建一个 DexClassLoader实例
加载指定的类返回一个Class
然后使用反射调用这个Class
下面是代码部分,代码参考自《Android内核剖析》(作者柯元旦,这本书不错,推荐阅读):
[html] view
plaincopy
@SuppressLint("NewApi") private void useDexClassLoader(){
//创建一个意图,用来找到指定的apk
Intent intent = new Intent("com.suchangli.android.plugin", null);
//获得包管理器
PackageManager pm = getPackageManager();
List<ResolveInfo> resolveinfoes = pm.queryIntentActivities(intent, 0);
//获得指定的activity的信息
ActivityInfo actInfo = resolveinfoes.get(0).activityInfo;
//获得包名
String pacageName = actInfo.packageName;
//获得apk的目录或者jar的目录
String apkPath = actInfo.applicationInfo.sourceDir;
//dex解压后的目录,注意,这个用宿主程序的目录,android中只允许程序读取写自己
//目录下的文件
String dexOutputDir = getApplicationInfo().dataDir;
//native代码的目录
String libPath = actInfo.applicationInfo.nativeLibraryDir;
//创建类加载器,把dex加载到虚拟机中
DexClassLoader calssLoader = new DexClassLoader(apkPath, dexOutputDir, libPath,
this.getClass().getClassLoader());
//利用反射调用插件包内的类的方法
try {
Class<?> clazz = calssLoader.loadClass(pacageName+".Plugin1");
Object obj = clazz.newInstance();
Class[] param = new Class[2];
param[0] = Integer.TYPE;
param[1] = Integer.TYPE;
Method method = clazz.getMethod("function1", param);
Integer ret = (Integer)method.invoke(obj, 1,12);
Log.i("Host", "return result is " + ret);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
Plugin1.apk中的一个类:
[html] view
plaincopy
package com.suchangli.plugin1;
public class Plugin1 {
public int function1(int a, int b){
return a+b;
}
}
Log输出的结果:
注意要在Plugin1中的清单文件的一个activity中添加一个如下的action,这个action必须是宿主和插件约定好的。
源代码:
通过以上代码就可以访问另一个apk中的代码了,一个比较麻烦的地方就是必须用反射才能调用方法,
有没不用反射也可以调用方法方式呢?当然有,通过接口。
ClassLoader类,借助这个类可以装载想要的Class文件,每个ClassLoader对象在初始化时必须制定Class文件的路径。
可能有人会问,在写程序的时候不是有import关键字可以引用制定的类吗?为何还要使用这个类加载器呢?
原因其实是这样的,使用import关键字引用的类必须符合以下两个条件
类文件必须在本地,当程序运行时需要次类时,这时类装载器会自动装载该类,程序员不需要关注此过程。
编译的时候必须有这个类文件,否则编译不通过。
如果想让程序在运行的时候动态调用怎么办呢?用import显示是不符合上面的两种要求的。此时ClassLoader就派上用场了。
关于java的Classloader的装载机制请参考此链接 http://www.iteye.com/topic/83978,最好到网上自行搜索一下。
http://www.artima.com/insidejvm/ed2/《Inside the Java Virtural Machine》
对于android应用程序,本质上使用的是java开发,使用标准的java编译器编译出Class文件,和普通的java开发不同的
地方是把class文件再重新打包成dex类型的文件,这种重新打包会对Class文件内部的各种函数表、变量表等进行优化,
最终产生了dex文件。dex文件是一种经过android打包工具优化后的Class文件,因此加载这样特殊的Class文件就需要特殊的类装载器,
所以android中提供了DexClassLoader类。
类装载器DexClassLoader类结构
继承关系:
A class loader that loads classes from
.jarand
.apkfiles containing a
classes.dexentry.
This can be used to execute code not installed as part of an application.
This class loader requires an application-private, writable directory to cache optimized classes. Use
Context.getDir(String, int)to create such a directory:
<code style="line-height: 14px;"><span class="typ" style="color: rgb(102, 0, 102);">File</span><span class="pln" style="color: rgb(0, 0, 0);"> dexOutputDir </span><span class="pun" style="color: rgb(102, 102, 0);">=</span><span class="pln" style="color: rgb(0, 0, 0);"> context</span><span class="pun" style="color: rgb(102, 102, 0);">.</span><span class="pln" style="color: rgb(0, 0, 0);">getDir</span><span class="pun" style="color: rgb(102, 102, 0);">(</span><span class="str" style="color: rgb(136, 0, 0);">"dex"</span><span class="pun" style="color: rgb(102, 102, 0);">,</span><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="lit" style="color: rgb(0, 102, 102);">0</span><span class="pun" style="color: rgb(102, 102, 0);">);</span></code>
Do not cache optimized classes on external storage. External storage does not provide access controls necessary to protect your application from code injection attacks.
翻译:这个类加载器用来从.jar和.apk类型的文件内部加载classes.dex文件。通过这种方式可以用来执行非安装的程序代码,作为程序的一部分进行运行。
这个装载类需要一个程序私有的,可写的文件目录去存放优化后的classes文件。通过Contexct.getDir(String, int)来创建
这个目录:
File dexOutputDir = context.getDir("dex", 0);
不要把优化优化后的classes文件存放到外部存储设备上,防代码注入攻击。
这个类只有一个构造函数:
public DexClassLoader (String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent)
Added in API level 3Creates a
DexClassLoaderthat finds interpreted and native code. Interpreted classes are found in a set of DEX files contained
in Jar or APK files.
创建一个DexClassLoader用来找出指定的类和本地代码(c/c++代码)。用来解释执行在DEX文件中的class文件。
路径的分隔符使用通过System的属性
path.separator获得
:.
String separeater = System.getProperty("path.separtor");
Parameters
dexPath | 需要装载的APK或者Jar文件的路径。包含多个路径用File.pathSeparator间隔开,在Android上默认是 ":" |
---|---|
optimizedDirectory | 优化后的dex文件存放目录,不能为null |
libraryPath | 目标类中使用的C/C++库的列表,每个目录用File.pathSeparator间隔开; 可以为 null |
parent | 该类装载器的父装载器,一般用当前执行类的装载器 |
这个类的使用过程基本是这样:
通过PacageMangager获得指定的apk的安装的目录,dex的解压缩目录,c/c++库的目录
创建一个 DexClassLoader实例
加载指定的类返回一个Class
然后使用反射调用这个Class
下面是代码部分,代码参考自《Android内核剖析》(作者柯元旦,这本书不错,推荐阅读):
[html] view
plaincopy
@SuppressLint("NewApi") private void useDexClassLoader(){
//创建一个意图,用来找到指定的apk
Intent intent = new Intent("com.suchangli.android.plugin", null);
//获得包管理器
PackageManager pm = getPackageManager();
List<ResolveInfo> resolveinfoes = pm.queryIntentActivities(intent, 0);
//获得指定的activity的信息
ActivityInfo actInfo = resolveinfoes.get(0).activityInfo;
//获得包名
String pacageName = actInfo.packageName;
//获得apk的目录或者jar的目录
String apkPath = actInfo.applicationInfo.sourceDir;
//dex解压后的目录,注意,这个用宿主程序的目录,android中只允许程序读取写自己
//目录下的文件
String dexOutputDir = getApplicationInfo().dataDir;
//native代码的目录
String libPath = actInfo.applicationInfo.nativeLibraryDir;
//创建类加载器,把dex加载到虚拟机中
DexClassLoader calssLoader = new DexClassLoader(apkPath, dexOutputDir, libPath,
this.getClass().getClassLoader());
//利用反射调用插件包内的类的方法
try {
Class<?> clazz = calssLoader.loadClass(pacageName+".Plugin1");
Object obj = clazz.newInstance();
Class[] param = new Class[2];
param[0] = Integer.TYPE;
param[1] = Integer.TYPE;
Method method = clazz.getMethod("function1", param);
Integer ret = (Integer)method.invoke(obj, 1,12);
Log.i("Host", "return result is " + ret);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
Plugin1.apk中的一个类:
[html] view
plaincopy
package com.suchangli.plugin1;
public class Plugin1 {
public int function1(int a, int b){
return a+b;
}
}
Log输出的结果:
注意要在Plugin1中的清单文件的一个activity中添加一个如下的action,这个action必须是宿主和插件约定好的。
源代码:
通过以上代码就可以访问另一个apk中的代码了,一个比较麻烦的地方就是必须用反射才能调用方法,
有没不用反射也可以调用方法方式呢?当然有,通过接口。
相关文章推荐
- 对Android回调的理解
- Activity的启动模式
- Android MVVM模式
- Porting WiFi drivers to Android (移植WiFi驱动到Android系统)
- [Android] 任意时刻从子线程切换到主线程的实现
- Android home键和back键区别
- 实现Android的在线更新
- Android输入法弹出时把布局顶上去和登录按钮顶上去的解决方法
- Android server机制
- Android的两种序列化
- 超完整!Android获取图片的三种方法
- 图片三级缓存及OOM--android
- 我是如何使用Android反编译软件的?
- 简析Android的垃圾回收与内存泄露
- Android一点 异常收集
- Android开发笔记
- ANDROID SDK 下载地址
- android学习之LayoutParams
- android标题栏RadioButton实现,下面展示切换viewpager,fragment,内容是listview
- Android Studio移除的Module如何恢复(转载)