简介
Android的ClassLoader和Java的ClassLoader有一些差异:
- Java中的ClassLoader可以加载jar文件和Class文件(本质是加载Class文件)
- 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:父类装载器
测试步骤
- 创建app project 和 demoplugin Module
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看这里)
把jar文件打包成dex文件
dx --dex --output=demodex.jar E:\demo.jar
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