可以先参考java的java类加载器。
一、DexClassLoader
一般情况下,我们使用import就可以了,为什么还要使用类装载器呢?
import中所引用的类文件有两个特点:
1.一定在存在于本地,当程序运行时需要这个类时,内部类装载器就会自动装载,程序员感知不到这个过程。
2.编译时一定要在现场,否则会因找不到引用文件而不能正常编译。
但是在有的情况下,要用的类不一定满足这些条件,如从运程下载一个类并在本地运行,例子如applet执行的java,另一种是要引用的class文件不方便在编译时直接参与 ,而只能在运行时动态调用。如在android中修改FrameWork中已经有的类,为了保持与原生的FrameWork最小化的修改,可以使用类装载器动态装载自己定义 的jar包。
二、DexClassLoader的使用方法
有两个apk。
- 插件部分
定义了类PluginClass。如下
public class PluginClass { public PluginClass(){ Log.d("PluginCLass", "PluginClass initiazed"); } public int function1(int a,int b){ return a +b; } }
还定义了
一个空的Activity,这个作用只是为了让宿主apk中可以找到package而已,所以只要有就可以了。
<activity android:name=".BlankActivity" android:label="@string/title_activity_blank" > <intent-filter> <action android:name="com.example.lsj.testapp.plugin.client"/> </intent-filter> </activity>
我们将可以通过intent-filter得到相关的信息。
- 宿主部分
通过下面的ClassLoader可以装载插件中的类,再构造出Method对象 ,并构造出Method对象所使用的参数对象 ,然后才能调用 。
ublic void useDexClassLoader(){ Intent intent = new Intent("com.example.lsj.testapp.plugin.client", null); PackageManager pm = getPackageManager(); final List<ResolveInfo> plugins = pm.queryIntentActivities(intent, 0); ResolveInfo rinfo = plugins.get(0); ActivityInfo ainfo = rinfo.activityInfo; String div = System.getProperty("path.separator"); String packageName = ainfo.packageName; String dexPath = ainfo.applicationInfo.sourceDir; String dexOutputDir = getApplicationInfo().dataDir; String libPath = ainfo.applicationInfo.nativeLibraryDir; DexClassLoader dexClassLoader = new DexClassLoader(dexPath, dexOutputDir,libPath,this.getClass().getClassLoader()); try{ Class <?> clazz = dexClassLoader.loadClass(packageName+".PluginClass"); Object obj = clazz.newInstance() ; Class [] params = new Class[2]; params[0]= Integer.TYPE; params[1] = Integer.TYPE ; Method action = clazz.getMethod("function1", params) ; Integer ret = (Integer)action.invoke(obj, 1,2); Log.e(TAG, "return value is :"+ ret); }catch ( ClassNotFoundException e){ }catch (InstantiationException e2 ){ }catch (IllegalAccessException e3){ }catch (NoSuchMethodException e4){ }catch (InvocationTargetException e5){ } }
DexClassLoader方法参数:
dexPath:目标所在的apk或者jar文件的路径,装载器将从路径中寻找指定的目标类。
dexOutputDir:由于dex 文件在APK或者 jar文件中,所以在装载前面前先要从里面解压出dex文件,这个路径就是dex文件存放的路径,在 android系统中,一个应用程序对应一个linux用户id ,应用程序只对自己的数据目录有写的权限,所以我们存放在这个路径中。
libPath :目标类中使用的C/C++库。
最后一个参数是该装载器的父装载器,一般为当前执行类的装载器。
这里我们得到的Class对象是CLassLoader可以识别的类,而PluginClass是程序执行后可以识别的类,此时仅仅装载了PluginClass的程序代码,还没有创建出其对象 ,接下来可以使用Class.newInsance()方法来调用PluginClass类的构造方法,并返回一个真正的PluginClass对象。
但是我们不能直接去调用 PluginClass的任何方法,只能通过反射去调用其方法。
反射机制时主要使用了Method类,Class对象的getMethod()方法可以返回这个类中任何方法,getMethod()有两个参数 ,一个是要访问的函数名,另一个是函数的参数类型。
得到目标类的Method对象后,就可以调用Method对象的invoke方法,这个方法的第一个参数是执行目标函数的对象。