小编整理了一波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小时内删除,切莫做商业用途传播。
