Android ClassLoader 小结

简介

Android的ClassLoader和Java的ClassLoader有一些差异:

  1. Java中的ClassLoader可以加载jar文件和Class文件(本质是加载Class文件)
  2. Android中ClassLoader加载的不再是Class文件,而是dex文件(DVM,ART都是如此)

不过都是遵循双亲模式:有解析请求会先交给父类,如果父类无法解析才会往下传

这里写图片描述

Java ClassLoader

ClassLoader分为两种类型:系统ClassLoader和自定义ClassLoader

系统ClassLoader

  • Bootstrap ClassLoader:用于加载系统类,如java.lang.、java.uti.等 (C++实现)
  • Extensions ClassLoader:用于加载 Java 的拓展类
  • App ClassLoader:负责加载当前应用程序Classpath目录下的所有jar和Class文件

Custom ClassLoader:自定义类加载器,继承java.lang.ClassLoader

类的继承关系

这里写图片描述

Android ClassLoader

同Java ClassLoader类似,也分为系统ClassLoader和自定义ClassLoader

系统ClassLoader包括三种

  • BootClassLoader:预加载常用类(Java实现)
    • 访问修饰符是默认,只有在同一个包中才可以访问,因此应用程序中是无法直接调用
    • 源码ClassLoader.java
  • PathClassLoader
    • 加载系统类和应用程序的类,加载非系统应用程序类,则会加载data/app/目录下的dex文件
    • 源码PathClassLoader
  • DexClassLoader
    • 可以加载dex文件以及包含dex的apk文件或jar文件
    • 也支持从SD卡进行加载,即可以在应用未安装的情况下加载dex(热修复和插件化技术的基础)
    • 源码DexClassLoader

类继承关系图
这里写图片描述

Code示例

先来看下DexClassLoader的参数

DexClassLoader(dexPath, optimizedDirectory, libraryPath, parent)

  • dexPath:目标类所在的APK或者jar包,demo程序放在 (没有root的机器请放在请放在外卡其他地方,不过访问要加上权限)

    dexPath = /storage/emulated/0/Android/data/com.wenxi.learn.dexclassloaderdemo/cache/demodex.jar
  • optimizedDirectory:从APK或者jar解压出来的dex文件存放路径。demo程序放在

        optimizedDirectory = /data/user/0/com.wenxi.learn.dexclassloaderdemo/app_dex)
    
        建议不要放在外卡防止注入
  • libraryPath:native库路径,可以为null
  • parent:父类装载器

测试步骤

  1. 创建app project 和 demoplugin Module
  2. demoplugin 创建一个IChange接口,一个DataChange类

    IChange接口--------------------
    
    package com.wenxi.learn.plugin;
    
    public interface IChange {
        String start();
    }
    
    DataChange类------------------
    
    package com.wenxi.learn.demoplugin;
    
    import com.wenxi.learn.plugin.IChange;
    
    public class DataChange implements IChange {
    
        @Override
        public String start() {
            return "Change from plugin";
        }
    }
    

    生成jar包(AS默认生成aar文件,如何生成jar看这里

  3. 把jar文件打包成dex文件

    dx --dex --output=demodex.jar E:\demo.jar
  4. app 中加载dex文件

    public class MainActivity extends AppCompatActivity {
    private final static String TAG = "DexDemo";
    
    private TextView mResult;
    private Button mBtnLoad;
    
    private final static String PLUGIN_NAME = "demodex.jar";
    private final static String PLUGIN_CLASS = "com.wenxi.learn.demoplugin.DataChange";
    private Class<?> mDexClazz;
    private File dexOutputDir;
    private File pluginFile;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        mResult = (TextView) findViewById(R.id.tv_result);
        mBtnLoad = (Button) findViewById(R.id.btn_load);
        mBtnLoad.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                loadPlugin();
            }
        });
    
        initPlugin();
    }
    
    private void initPlugin() {
        // optimizedDirectory
        dexOutputDir = getDir("dex", 0);
    
        // get plugin jar file that include class.dex
        pluginFile =
                new File(PathUtils.getDiskCacheDir(this) + File.separator + PLUGIN_NAME);
    
        // make sure you have permission for target path
        Log.d(TAG, "plugin.canRead = " + pluginFile.canRead());
        if (!pluginFile.exists()) {
            Log.w(TAG, PLUGIN_NAME + " not exists");
            return;
        }
    
        Log.w(TAG, "dexPath = " + pluginFile.getAbsolutePath());
        Log.w(TAG, "optimizedDirectory = " + dexOutputDir.getAbsolutePath());
    }
    
    private void loadPlugin() {
        DexClassLoader dexClassLoader =
                new DexClassLoader(pluginFile.getAbsolutePath(), dexOutputDir.getAbsolutePath(), null, getClassLoader());
        try {
            // load DataChange
            mDexClazz = dexClassLoader.loadClass(PLUGIN_CLASS);
            // change to target IChange, please make sure it must has same package name withe aar
            IChange change = (IChange) mDexClazz.newInstance();
            mResult.setText(change.start());
            Log.d(TAG, "result = " + change.start());
        } catch (ClassNotFoundException e) {
            Log.w(TAG, "ClassNotFoundException ex", e);
        } catch (Exception e) {
            Log.w(TAG, "Exception ex", e);
        }
    }
    }
    

    解析之后在optimizedDirectory( /data/user/0/com.wenxi.learn.dexclassloaderdemo/app_dex)路径下可以看到文件demodex.dex

Github源码下载地址在这里

特别说明:笔者用的是已经root的机器,如果是没有root的手机,需要改下dexPath

Reference

Android解析ClassLoader(一)Java中的ClassLoader
Android解析ClassLoader(二)Android中的ClassLoader
热修复入门:Android 中的 ClassLoader

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值