Android开发中类加载器DexClassLoader的简单使用讲解
简介
“类装载器”(classloader),顾名思义,就是用来动态装载class文件的。标准的java sdk中有个classloader类,借助此类可以装载需要的class文件,前提是classloader类初始化必须制定class文件的路径。
import关键字引用的类文件和classloader动态加载类的区别:
import引用类的两个特点:
1、必须存在于本地,当程序运行该类时,内部类装载器会自动装载该类。
2、编译时必须在现场,否则编译过程会因找不到引用文件而不能正常编译。
classloader的特点正好于import相反,而且更*灵活。
每一个classloader必须有一个父classloader,在装载class文件时,子classloader会先请求其父classloader加载该文件,只有当其父classloader找不到该文件时,子classloader才会继承装载该类。这是一种安全机制。对于android而言,最终的apk文件包含的是dex类型的文件,dex文件是将class文件重新打包,打包的规则又不是简单地压缩,而是完全对class文件内部的各种函数表,变量表进行优化,产生一个新的文件,即dex文件。因此加载这种特殊的class文件就需要特殊的类加载器dexclassloader。
在java中涉及到的类加载器就是classloader这个类,通过classloader.forname()的方法可以加载我们需要的类,从而实现在运行时动态加载类库的需求。但是在android中直接使用classloader是行不通的,因为classloader加载的java的字节码文件,而在android中使用的是dex格式的字节码,对此android专门提供了一个dexclassloader类来完成动态加载apk的需求。
实例
下面用一个简单的例子来说明一下dexclassloader这个类的使用,这个例子涉及到两个知识点:跨包取资源 & 反射调用方法。 首先建立一个client工程,这个工程很简单,只有一个简单的layout和一个sayhello()的方法。
public class main extends activity { @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); this.setcontentview(r.layout.main); } @suppresswarnings("unused") private void sayhello(string msg){ log.d("mmtag",msg); } }
接着建立另外一个工程,在这个工程中调用client工程中的view和调用sayhello()方法。在这个类中主要涉及到了dexclassloader这个类的使用。
package com.example.dexclassloaderserver; import java.lang.reflect.method; import java.util.collections; import java.util.list; import dalvik.system.dexclassloader; import android.annotation.suppresslint; import android.app.activity; import android.content.intent; import android.content.pm.activityinfo; import android.content.pm.packagemanager; import android.content.pm.resolveinfo; import android.os.bundle; import android.util.log; public class mainactivity extends activity { @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); usedexclassloader(); } @suppresslint("newapi") public void usedexclassloader() { intent mintent = new intent(); mintent.setclassname("com.example.dexclassloaderclient", "com.example.dexclassloaderclient.mainactivity"); packagemanager pm = this.getpackagemanager(); list<resolveinfo> mlist = pm.queryintentactivities(mintent, packagemanager.match_default_only); resolveinfo info = mlist.get(0); string apkpath = info.activityinfo.applicationinfo.sourcedir; string optpath = this.getcodecachedir().getabsolutepath(); string libpath = info.activityinfo.applicationinfo.nativelibrarydir; dexclassloader clsloader = new dexclassloader(apkpath, optpath, libpath, this.getclass().getclassloader()); try { class cls = clsloader .loadclass("com.example.dexclassloaderclient.mainactivity"); object obj = cls.newinstance(); method invokemethod = cls.getdeclaredmethod("sayhello", new class[] { string.class }); invokemethod.setaccessible(true); invokemethod.invoke(obj, "hello world,dexclassloader"); } catch (exception e) { e.printstacktrace(); } } }
生成的结果为:
i/dex2oat (20250): dex2oat took 1.547s (threads: 4) d/mmtag (20229): hello world,dexclassloader d/openglrenderer(20229): render dirty regions requested: tru
这样就成功的调用了其他的apk中的方法。