Android黑科技动态加载(一)之Java中的ClassLoader

目录

Android黑科技动态加载(一)之Java中的ClassLoader
Android黑科技动态加载(二)之Android中的ClassLoader
Android黑科技动态加载(三)之动态加载资源
Android黑科技动态加载(四)之插件化开发

项目地址

我们的认识

我们都知道, 刚写好的Java的源文件是以.java为扩展名的, 需要让JVM去解析的时候, 必须编译成Java字节码, 而Java字节码文件就是以.class为扩展名的. ClassLoader的对象就是去加载这些Java字节码文件

Java默认的ClassLoader

在Java默认环境中, 提供了三种Classloader

  • BootStrap ClassLoader : 启动类加载器,Java类加载器中最顶层的类加载器,负责加载JDK中的核心类库,如:rt.jarresources.jarcharsets.jar

  • Extension ClassLoader : 扩展类加载器,负责加载Java的扩展类库,默认加载JAVA_HOME/jre/lib/ext/目下的所有jar

  • App ClassLoader : 系统类加载器,负责加载应用程序classpath目录下的所有jar和class文件

java_classloader.jpg

有兴趣的朋友可以通过Class.getClassLoader()去验证一下.

ClassLoader加载原理

加载顺序(双亲委托机制)

如果我们自定义一个ClassLoader叫做MyClassLoader, 当查找需要的类的时候, MyClassLoader会把该责任委托给它的父类加载器不是通过继承的父类, 是通过parent属性去持有. 整个过程从顶到下, 如果一个类找不到, 那么查找顺序是BootStrap ClassLoader->Extension ClassLoader->App ClassLoader->MyClassLoader->抛出异常ClassNotFoundException

两个类是否相等

一般我们认为两个类是否相等, 仅仅通过包名+类名去区分. 事实上还有一个条件, 就是加载该类的ClassLoader是否是同一个

两个关键方法

ClassLoader中认为比较关键的方法有三个:

  • public Class<?> loadClass(String name) : 根据双亲委托机制的方式查找class

  • protected Class<?> findClass(String name) : 根据该ClassLoader的方式去查找class

  • protected final Class<?> defineClass() : 该方法有多个重载, 具体参数不写了. 主要作用是把类加载到内存中

loadClass为一个模板方法, 有兴趣的话可以去看看源码. 所以一般我们只需要重写findClass即可.

上面三个方法有关系的, loadClass->findClass->defineClass. loadClass使用双亲委托机制去查找要加载的类, 当上层ClassLoader不能加载该类时, 就会去使用自己的findClass去加载类. 加载到的类(可能为空)会传入defineClass中, 如果为空则抛出异常.

自定义ClassLoader

我们下面自定义一个MyClassLoader去加载外部的RemoteClass.class文件

RemoteClass.class

package top.august1996.demo;

public class RemoteClass {

    public void catched() {
        System.out.println("I am catched...");
    }
}

我们把上面文件移到任意目录(/Users/August/Desktop)

cd /Users/August/Desktop
javac RemoteClass.java

现在我们就在桌面上编译了一个字节码文件

MyClassLoader.java

package top.august1996.demo;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class MyClassLoader extends ClassLoader {

    private String mClassPath; // Class存放的的目录

    public MyClassLoader(String classPath) {
        mClassPath = classPath;
    }

    @Override
    protected Class<?> findClass(String name) {

        File clsFile = new File(mClassPath, getClassName(name));

        FileInputStream fis = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] b = null;
        try {
            fis = new FileInputStream(clsFile);

            int data = 0;

            while ((data = fis.read()) != -1) {
                baos.write(data);
            }
            b = baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (baos != null) {
                try {
                    baos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        // 上面是基本的数据流操作, 把Class文件转换成二进制流

        return defineClass(name, b, 0, b.length);
    }

    /**
     * 获取Class的完整文件名
     * 
     * @param name
     * @return
     */
    private String getClassName(String name) {
        String clsName = null;

        if (name != null) {
            int lastIndexOf = name.lastIndexOf(".");
            if (lastIndexOf == -1) {
                clsName = name + ".class";
            } else {
                clsName = name.substring(lastIndexOf + 1) + ".class";
            }
        }

        return clsName;
    }

}

TestDemo.java

package top.august1996.demo;

import java.lang.reflect.Method;

public class TestDemo {

    public static void main(String[] args) throws Exception {

        MyClassLoader classLoader = new MyClassLoader("/Users/August/Desktop");
        Class<?> cls = classLoader.findClass("top.august1996.demo.RemoteClass");
        Object object = cls.newInstance();
        Method method = cls.getDeclaredMethod("catched", null);
        method.invoke(object, null);
    }

}
I am catched...

我们使用反射区调用自己加载的RemoteClasscatched方法, 可以看到结果已经是成功的了.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容