关于ClassLoader还可以这么做

674 阅读3分钟
原文链接: mp.weixin.qq.com

小编整理了一波Java-Android的学习资料。基本涵盖了从入门到高级开发所需要掌握的知识,希望能让大家少走找资料的弯路。请扫描文末图片二维码进群,小编免费分享哦!

庖丁解牛--自定义ClassLoader

上次我们说到Java的双亲委托机制,导致我们不能加载到WangHouse里的Socker类。今天我们来说说怎么在不改变目录结构的情况下加载Socker。

自定义ClassLoader

自定义ClassLoader可能比较陌生。既然我们知道AppClassLoader是对应当前的classpath,而ClassLoader会根据class path来加载类,那么我们可以再自定义一个ClassLoader,将它的路径指定到WangHouse。自定义ClassLoader分两步· 继承 java.lang.ClassLoader· 重写 findClass方法

这里需要说一下为什么只重写findClass方法。JDK在搜索类的时候,会先调用 loadClass,在找不到的时候会调用 findClass,虽然重写loadClass()方法也可以,但是这样会改写原有的正常查找逻辑,因此我们只需要重写findClass()就可以,这是Java预留给开发者的可重载方法。

public class DiskClassLoader extends ClassLoader {    private String mFilePath;    public DiskClassLoader(String mFilePath) {        this.mFilePath = mFilePath;    }    @Override    protected Class<?> findClass(String name) throws ClassNotFoundException {        String fileName = getFileName(name);        File file = new File(mFilePath, fileName);        try {            FileInputStream ins = new FileInputStream(file);            ByteArrayOutputStream bos = new ByteArrayOutputStream();            int len = 0;            try {                while((len = ins.read()) != -1) {                    bos.write(len);                }            } catch (IOException ioe) {                ioe.printStackTrace();            }            byte[] data = bos.toByteArray();            ins.close();            bos.close();            return defineClass(name, data, 0, data.length);        } catch (IOException exp) {            exp.printStackTrace();        }        return super.findClass(name);    }    private String getFileName(String name){        int index = name.lastIndexOf('.');        if(index == -1) {            return name + ".class";        } else {            return name.substring(index) + ".class";        }    }}

简单的解释代码的逻辑是在传进去的路径下查找对应的class文件,把它转换为文件流,通过ClassLoader的 defineClass方法获得Class对象。

用自定义Loader来加载class

现在我们有可以找class的工具了,目标的类Socker在隔壁WangHouse,调用的代码这么写

public class Ming {  public static void main(String[] args) {    System.out.println("Ming looking for socker");    findSocker();  }  private static void findSocker() {    DiskClassLoader loader = new DiskClassLoader("../WangHouse/");    try {      Class b = loader.loadClass("Socker");      if(b != null){        try {          Object object = b.newInstance();          Method method = b.getDeclaredMethod("callSockerMethod", null);          method.invoke(object, null);        } catch(Exception e) {          e.printStackTrace();        }      }    } catch (Exception e) {      e.printStackTrace();    }  }}

运行结果

$ java MingMing looking for sockerSocker method invoke

看到这里肯定会有疑问,为什么要用反射。我们接着说。

举一反三

为什么要用反射来获取Socker对象?既然都能找到类,也已经加载进来了,为何不直接 new 就完了?

其实很简单,因为Ming和Socker并不在同一个ClassLoader里,Ming在AppClassLoader中,而Socker我们加载到了DiskClassLoader里。子Loader加载类的时候可以去父Loader里找到,就像我们调用System,而父Loader用子Loader里的类的时候是不行的,只能用反射。

…可能有人已经意识到,Android的热修复是不是可以用自定义ClassLoader来做?没错!有些热修复方案就是基于ClassLoader做的。明天我们接着讲开发中时常遇到的ClassCastException。在我们准备好相关的点之后,就可以自己来写一个热修复框架了。

小编提供的学习资料若有侵权请联系删除。请下载后仅做学习用途于24小时内删除,切莫做商业用途传播。