Android动态加载机制

本文介绍了Android中的DexClassLoader和PathClassLoader,它们都是基于ClassLoader的类加载器。DexClassLoader和PathClassLoader通过DexFile实现类加载,前者支持从.zip、.apk、.jar加载dex,后者仅支持直接dex文件或已安装apk。PathClassLoader使用DexFile的loadClassBinaryName方法,DexClassLoader使用loadClass方法。通过实例展示了动态加载类的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Dalvik虚拟机类加载机制

DexClassLoader和PathClassLoader这两个类都继承自ClassLoader的类加载器,本质上是重载了ClassLoader的findClass方法。

它们在加载一个类之前,会去检查自己以及自己以上的类加载器是否已经加载过这个类,如果已经加载过,就会直接将之返回,而不会重复加载。

DexClassLoader和PathClassLoader其实都是通过DexFile这个类来实现类加载的。

Dalvik虚拟机识别的是dex文件,而不是class文件,因此,我们供类加载的文件也只能是dex文件或者包含dex文件的.apk、.jar或.zip文件。

PathClassLoader是通过构造函数new DexFile(path)来产生DexFile对象的;而DexClassLoader是通过其静态方法loadDex(path, outpath, 0)得到DexFile对象。

这两者的区别在于DexClassLoader需要提供一个可写的outpath路径,用来存放.apk包或者.jar包中的dex文件。

换个说法来说,就是PathClassLoader不能主动从zip包中解析出dex,因此只支持直接操作dex格式文件,或者已经安装的apk(因为已经安装的apk在cache中存在缓存的dex文件)。而DexClassLoader可以支持.apk、.jar和.dex文件,并且会在指定的outpath路径存放解析出的dex文件。

PathClassLoader在加载类时调用的是DexFile的loadClassBinaryName方法,而DexClassLoader调用的是loadClass方法。

      

例子结构如上图,DynamicTest先编译个apk放到根目录下,再作为一个lib库文件被DynamicDemo引用。

public class ClassLister {

    private String mDexPath;

    public ClassLister(String apkPath) {
        mDexPath = apkPath;
    }

    public List<Class<?>> getAllClasses(String packageName, String suffix) {
        List<String> classNames = getClassNames(packageName, suffix);
        ArrayList<Class<?>> allClass = new ArrayList<Class<?>>();
        for (String name : classNames) {
            try {
                allClass.add(Class.forName(name));
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return allClass;
    }

    /**
     * 获取所有以包名开头,以默认字符串为后缀的类
     * 
     * @param packageName
     * @param suffix
     * @return
     */
    private List<String> getClassNames(String packageName, String suffix) {
        ArrayList<String> classNames = new ArrayList<String>();
        DexFile dexFile = null;
        try {
            dexFile = new DexFile(new File(mDexPath));
            Enumeration<String> entries = dexFile.entries();
            while (entries.hasMoreElements()) {
                String className = entries.nextElement();
                if (className.endsWith(suffix)
                        && className.startsWith(packageName)) {
                    classNames.add(className);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (dexFile != null) {
                try {
                    dexFile.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return classNames;
    }

    /**
     * 使用PathClassLoader方法加载类
     * @param context
     * @param className
     * @return
     */
    public static Class<?> getClassByPathClassLoader(Context context,
            String className) {
        Class<?> loadClass = null;
        String dexPath = context.getApplicationInfo().sourceDir;
        String libPath = context.getApplicationInfo().nativeLibraryDir;
        // 第一个参数:apk安装的路径,只能通过appInfo.sourceDir获取
        // 第二个参数:C/C++依赖的本地库文件路径,可以为null
        // 第三个参数:上一级类加载器
        PathClassLoader pathClassLoader = new PathClassLoader(dexPath, libPath,
                context.getClassLoader());
        try {
            loadClass = pathClassLoader.loadClass(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return loadClass;
    }

    /**
     * 使用DexClassLoader方式加载类
     * @param context
     * @param className
     * @return
     */
    public static Class<?> getClassByDexClassLoader(Context context, String className) {
        Class<?> loadClass = null;
        String dexPath = Environment.getExternalStorageDirectory().toString()
                + File.separator + "DynamicTest.apk";
        String dexOutputDirs = context.getApplicationInfo().dataDir;
        // 第一个参数:dex压缩文件的路径
        // 第二个参数:dex解压缩存放的路径
        // 第三个参数:C/C++依赖的本地库文件路径,可以为null
        // 第四个参数:上一级类加载器
        DexClassLoader dexClassLoader = new DexClassLoader(dexPath,
                dexOutputDirs, null, context.getClassLoader());
        try {
            loadClass = dexClassLoader.loadClass(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return loadClass;
    }

    /**
     * 获取包中实现了某个接口的所有非抽象类
     * @param packageName
     * @param interfaceName
     * @param suffix
     * @return
     */
    public List<Class<?>> listConcreteClasses(String packageName,
            String interfaceName, String suffix) {
        ArrayList<Class<?>> impClasses = new ArrayList<Class<?>>();
        try {
            List<Class<?>> allClasses = getAllClasses(packageName, suffix);
            Class<?> interfaceClass = Class.forName(interfaceName);
            for (Class<?> c : allClasses) {
                if (isConcreteClassOf(c, interfaceClass)) {
                    impClasses.add(c);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return impClasses;
    }

    /**
     * 判断sub是否是base的子类,并且sub不是抽象类
     * @param sub
     * @param base
     * @return
     */
    private boolean isConcreteClassOf(Class<?> sub, Class<?> base) {
        return base.isAssignableFrom(sub) && isNotAbstract(sub);
    }

    private boolean isNotAbstract(Class<?> clazz) {
        return (clazz.getModifiers() & Modifier.ABSTRACT) == 0;
    }
}


 

public class DemoModule {

}


 

public class DemoTestModule {

}


 

public interface ILoad {

}


 

public class DemoLoadImp implements ILoad {

}


 

public class LoadDemoTestImp implements ILoad {

}


DynamicDemo的MainActivity:

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ClassLister classLister = new ClassLister(getApplicationInfo().sourceDir);
        List<Class<?>> allClasses = classLister.getAllClasses("com.example", "Module");
        for (Class<?> c : allClasses) {
            System.out.println("class:" + c.toString());
        }
        List<Class<?>> listConcreteClasses = classLister.listConcreteClasses("com.example", "com.example.dynamictest.ILoad", "Imp");
        for (Class<?> c : listConcreteClasses) {
            System.out.println("imp:" + c.toString());
        }
        Class<?> classByPathClassLoader = ClassLister.getClassByPathClassLoader(this, "com.example.dynamictest.MainActivity");
        if (classByPathClassLoader != null) {
            System.out.println("pathClass:" + classByPathClassLoader);
        }
        Class<?> classByDexClassLoader = ClassLister.getClassByDexClassLoader(this, "com.example.dynamictest.MainActivity");
        if (classByDexClassLoader != null) {
            System.out.println("dexClass:" + classByDexClassLoader.toString());
        }
    }

}

输出结果:

class:class com.example.dynamicdemo.DemoModule
class:class com.example.dynamictest.DemoTestModule
imp:class com.example.dynamicdemo.DemoLoadImp
imp:class com.example.dynamictest.LoadDemoTestImp
pathClass:class com.example.dynamictest.MainActivity
dexClass:class com.example.dynamictest.MainActivity

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值