DexClassLoader的使用

在Java环境中,有个概念叫做”类装载器(Class Loader)”,其作用是动态加载Class文件.标准的Java SDK中有一个ClassLoader类,借助他可以装载想要的Class文件,每个ClassLoader对象在初始化的时候必须指定Class文件的路径.

但我们在使用java的时候,基本上没有使用过ClassLoader,仅仅使用import就可以加载类文件了,简单的讲,import中所引用的类文件有两个特点:

1:必须存在于本地,当程序运行需要该类的时候,内部类装载器会自动装载该类,这对程序员来说是透明的,即程序员感知不到该过程

2:编译时必须在现场,否则编译过程会因找不到引用文件而不能正常编译.

但在有些情况下,所需要的类却不能满足以上两个条件.比如当该类是从远程下载并在本地执行的时候,典型的例子就是通过浏览器中的AppletLet执行的java程序,这些要执行的程序是在服务器端.另一种情况是,要引用的Class文件不方便在编译的时候直接参与,而只能在运行时动态调用.例如,在Android Framework中,所包含的Class文件是一些通用的类文件,但对于一些设备商而言,他们需要扩充Framework,扩充的具体工作包括两点:

1:需要增加一些额外的类文件,这些类文件提供厂商自定义的功能,这些文件一般以独立的jar包存在

2:需要修改Framework中的已有的类文件,比如WindowManagerService类,在该类中添加使用自定义jar包中的代码.使用自定义jar常用的方法是使用import关键字包含自定义的类,但为了保持和原生Framework的兼容性,对原声Framework最少化修改,可以使类装载器动态装载自定义jar包.

这就是使用ClassLoader的原因.

在一般情况下,应用程序不需要创建一个全新的ClassLoader对象,而是使用当前环境已经存在的ClassLoader.因为java的Runtime环境在初始化时,其内部会创建一个ClassLoader对象用于加载Runtime所需的各种java类.

每个ClassLoader必须有一个父ClasLoader,在装载Class文件的时候,子ClassLoader会先请求其父ClassLoader加载该Class文件,只有当其父ClassLoader找不到该Class的时候,子ClassLoader才会急促装载该类,这是一种安全机制.

对于Android的应用程序,本质上虽然也是用Java开发,并且使用标准的Java编译器编译出Class文件,但最终的APK文件中包含的确实dex类型的文件.dex文件是将所需的所有Class文件重新打包,打包的规则不是简单的压缩,而是完全对Class文件内部的各种函数表,变量表等进行优化,并产生一个新的文件,这就是dex文件.由于dex文件是一种经过优化的Class文件,因此要加载这样特殊的Class文件就需要特殊的类装载器,这就是DexClassLoader.Android SDK中提供了DexClassLoader类就是处于这个目的.

下面我们就来看一下DexClassLoader的调用.

首先我们新建一个Android Project,命名为Plugin.我们的包名设置为:

com.chen.plugin

在我们的包com.chen.plugin中创建一个activity,这个随便创建,我们用不到这个,该activity的用处仅仅就是用于启动android程序的.

在包com.chen.plugin中创建一个新的class,命名为PluginClass.

在类PluginClass中我们添加一个函数,名称为function1(int a,int b).

看一下我们的代码:

package com.chen.plugin;

import android.util.Log;

public class PluginClass {

    public PluginClass(){
        Log.e("Plugin","PluginClass client initialized");
    }

    public int function1(int a,int b){
        return a+b;
    }
}

然后运行我们这个项目,在手机中安装.

第二步,我们创建一个新的Android Project,命名为Host.同样创建一个新的Activity.

在MainActivity中添加一个方法,useDexClassLoader(),下面看一下这个方法的具体实现.

public void useDexClassLoader(){
        Intent intent = new Intent(Intent.ACTION_MAIN,null);
        intent.addCategory(Intent.CATEGORY_LAUNCHER);
        PackageManager pm = getPackageManager();

        final List<ResolveInfo> plugins = pm.queryIntentActivities(intent, 0);

        ResolveInfo rinfo = null;

        if(plugins != null){
            for(int i = 0;i < plugins.size();i++){
                ResolveInfo r = plugins.get(i);
                ActivityInfo ainfo = r.activityInfo;

                String div = System.getProperty("path.seperator");
                String packageName = ainfo.packageName;

                if(packageName.equals("com.chen.plugin")){
                    rinfo = plugins.get(i);
                }
            }
        }

        ActivityInfo ainfo = rinfo.activityInfo;

        String div = System.getProperty("path.seperator");
        String packageName = ainfo.packageName;
        String dexPath = ainfo.applicationInfo.sourceDir;
        String dexOutputDir = getApplicationInfo().dataDir;
        String libPath = ainfo.applicationInfo.nativeLibraryDir;

        DexClassLoader cl = new DexClassLoader(dexPath, dexOutputDir, libPath, this.getClass().getClassLoader());

        try{
            Class<?> clazz = cl.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, 12,34);
            Log.e("Host","return value is"+ret);

        }catch(Exception e){
            Log.e("errpr",e.getMessage());
        }

    }

在我们的MainActivity的OnCreate()方法中调用该方法,然后在手机中运行Host,就可以看到我们的调试信息.

我们先来看一下结果:

很明显,Plugin中的函数在Host中被调用了.

下面我们看一下DexClassLoader构造函数的参数的意义:

  • 1:dexPath,指目标类所在的APK或jar文件的路径.类装载器将从该路径中寻找指定的目标类,该类必须是APK或jar的全路径.如果要包含多个路径,路径之间必须使用特定的分割符分隔,特定的分割符可以使用System.getProperty(“path.separtor”)获得.
  • 2:dexOutputDir,由于dex文件被包含在APK或者Jar文件中,因此在装载目标类之前需要先从APK或Jar文件中解压出dex文件,该参数就是制定解压出的dex 文件存放的路径.在Android系统中,一个应用程序一般对应一个Linux用户id,应用程序仅对属于自己的数据目录路径有写的权限,因此,该参数可以使用该程序的数据路径.
  • 3:libPath,指目标类中所使用的C/C++库存放的路径
  • 4:最后一个参数是指该装载器的父装载器,一般为当前执行类的装载器

创建了DexClassLoader对象之后,就可以调用loadClass()来装载指定的类了.该函数返回的是一个Class对象,注意区分Class对象和目标类PluginClass对象,Class对象是ClassLoader所能识别的类,而PluginClass是程序执行后所能识别的类,此时仅仅装载了PluginClasss的程序代码,但是还没有创建 PluginClass对象,因此接下来调用Class对象的newInstance()方法,该方法内部会调用PluginClass的构造函数,并返回i一个真正的PluginClass对象.

虽然生成了PluginClass对象,但是Host本地并没有其函数,所以只能使用反射机制来调用PluginClass的方法.

关于反射机制,后面文章中会有更加详细的使用方式.

版权声明:您好,转载请留下本人博客的地址,谢谢

时间: 2024-11-08 07:42:37

DexClassLoader的使用的相关文章

DexClassLoader

DexClassLoader加载自己写的第三方jar包,例如金山毒霸需要加载ksremote.jar. 现在将DexClassLoader加载jar包成果分享一下.   1.新建Android工程,封装功能java类.   2.选中需要导出的文件夹,右键选中“Export”->"Java(Jar file)"导出jar文件.   3.使用dx工具将jar包转换为android 字节码.      命令:dx  --dex --Output=xx.jar    hello.jar 

Andorid DexClassLoader的创建过程解析(基于5.0)

做Android插件框架时,经常会用到dex的动态加载,就要直接或间接的使用DexClassLoader,在new DexClassLoader的时候Android系统做了很多工作,下面我们详细分析一下: public class DexClassLoader extends BaseDexClassLoader { public DexClassLoader(String dexPath, String optimizedDirectory, String libraryPath, Class

通过DexClassLoader动态加载代码

动态加载代码,会有多种需求,有的是APK过大,想缩小点:有的是部分代码需要灵活变动,例如视频站点的解析规则. 奉上一个Demo,在这个demo中验证了从dexclassloader加载 1.db 2.,sharedpreference 3.webview 4. so库 5.context 6.传入listener回调 7.加载不同package下的类 欢迎大家下载: http://download.csdn.net/download/ameryzhu/8970167 版权声明:本文为博主原创文章

Android中的类装载器DexClassLoader

http://blog.csdn.net/com360/article/details/14125683 java中,有个概念叫做“类加载器”(ClassLoader),它的作用就是动态的装载Class文件.标准的java sdk中有一个 ClassLoader类,借助这个类可以装载想要的Class文件,每个ClassLoader对象在初始化时必须制定Class文件的路径. 可能有人会问,在写程序的时候不是有import关键字可以引用制定的类吗?为何还要使用这个类加载器呢? 原因其实是这样的,使

Android插件化(二):使用DexClassLoader动态加载assets中的apk

Android插件化(二):使用DexClassLoader动态加载assets中的apk 简介 上一篇博客讲到,我们可以使用MultiDex.java加载离线的apk文件.需要注意的是,apk中的类是加载到当前的PathClassLoader当中的,如果apk文件过多,可能会出现ANR的情况.那么,我们能不能使用DexClassLoader加载apk呢?当然是可以的!首先看一下Doc文档. A class loader that loads classes from .jar and .apk

android DexClassLoader动态加载技术详解

介绍 做项目到一定庞大的时候就会发现方法数太多,安装包根本就装不上去了,这个也不足为奇,我们都知道当方法数目超过65536这个数目限制的时候,挡在2.x的系统上面就会出现无法安装的情况,这个时候动态加载技术就显得非的重要了,我们的项目中为了兼容2.x的手机也是用到了android的动态加载技术,这里我会详细的讲解一下怎么去用,怎么实战,我感觉,空谈理论不如动手来得实在. 实例 下面就通过一个例子反复的说明怎么来实现动态加载,通过不同的方法来调用. 准备工作 1:新建一个java工程(我比较懒我就

DexClassLoader和PathClassLoader类载入机制

0x00 在DexClassLoader和PathClassLoader载入Dex流程一文中,我们分析了dex文件怎样形成了DexFile结构体.本文中解说类载入机制,实际上就是生成ClassObject对象. 我们以DexClassLoader为例.解说类载入机制,PathClassLoader是一样的. 我们在载入类时一般会调用loadClass,那么我们就从loadClass来開始分析. 0x01 DexClassLoader类没有loadClass方法.所以调用的是父类ClassLoad

Android插件化开发之DexClassLoader动态加载dex、jar小Demo

一.温故动态加载ClassLoader机制 如果对Android的ClassLoader加载机制不熟悉,猛戳Android插件化开发动态加载基础之ClassLoader工作机制 http://blog.csdn.net/u011068702/article/details/53248960 二.介绍 我们知道在Android中可以跟java一样实现动态加载jar,但是Android使用德海Dalvik VM,不能直接加载java打包jar的byte code,需要通过dx工具来优化Dalvik

DexClassLoader使用-(1)

可以先参考java的java类加载器. 一.DexClassLoader 一般情况下,我们使用import就可以了,为什么还要使用类装载器呢? import中所引用的类文件有两个特点: 1.一定在存在于本地,当程序运行时需要这个类时,内部类装载器就会自动装载,程序员感知不到这个过程. 2.编译时一定要在现场,否则会因找不到引用文件而不能正常编译. 但是在有的情况下,要用的类不一定满足这些条件,如从运程下载一个类并在本地运行,例子如applet执行的java,另一种是要引用的class文件不方便在

被用坏的DexClassLoader,关于DexClassLoader内存泄露的问题

原因: 目前有两种猜测,第一种就是类加载容易,但是类卸载就不那么容易了,第二种猜测就是类缓存机制,从classloader的运行过程可以分析到有个过程是缓存常用类的,感觉这个过程没法释放,因为他会认为你以后会一直用. 测试内存泄露代码: 可以用DDMS测试下,当你调用GC的时候byte是可以回收的,但是classloader就没法回收了,也许你认为是因为调用了activity的classloader造成的,但是回头想想这个classloader可是整个应用的,除非你关闭应用,否则无法去掉clas