1.什么是类载入器?
类载入器(class loader)是 Java™中的一个非常重要的概念。类载入器负责载入 Java 类的字节代码到 Java 虚拟机中。
Java 虚拟机使用 Java 类的方式例如以下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类载入器负责读取 Java 字节代码,并转换成java.lang.Class类的一个实例。每一个这种实例用来表示一个 Java 类。通过此实例的 newInstance()方法就能够创建出该类的一个对象。实际的情况可能更加复杂。比方 Java 字节代码可能是通过工具动态生成的,也可能是通过网络下载的。
基本上全部的类载入器都是 java.lang.ClassLoader类的一个实例,须要了解ClassLoader能够參考这篇文章深入ClasssLoader
2.Dalvik虚拟机类载入机制
而在Java标准的虚拟机中。类载入能够从class文件里读取。也能够是其它形式的二进制流,因此。我们经常利用这一点,在程序执行时手动载入Class,从而达到代码动态载入执行的目的,可是Dalvik虚拟机毕竟不算是标准的Java虚拟机,因此在类载入机制上。它们有同样的地方。也有不同之处。
与JVM不同,Dalvik的虚拟机不能用ClassCload直接载入.dex,Android从ClassLoader派生出了两个类:DexClassLoader和PathClassLoader;而这两个类就是我们载入dex文件的关键,这两者的差别是:
1.DexClassLoader:能够载入jar/apk/dex。能够从SD卡中载入未安装的apk;
2.PathClassLoader:要传入系统中apk的存放Path,所以仅仅能载入已经安装的apk文件。
关于Android 动态载入基础 ClassLoader工作机制大家能够參考这里:https://segmentfault.com/a/1190000004062880。
准备工作開始
package wangyang.zun.com.mydexdemo.dynamic; /** * Created by WangYang on 2016/3/11. */ public interface Dynamic { String sayHello(); }
package wangyang.zun.com.mydexdemo.dynamic.impl; import wangyang.zun.com.mydexdemo.dynamic.Dynamic; /** * Created by WangYang on 2016/3/11. */ public class DynamicImpl implements Dynamic { @Override public String sayHello() { return new StringBuilder(getClass().getName()).append(" is loaded by DexClassLoader").toString(); } }非常easy输出一句话,"DynamicImpl is loaded by DexClassLoader."
//删除dynamic.jar包任务 task clearJar(type: Delete) { delete 'libs/dynamic.jar' } //打包任务 task makeJar(type:org.gradle.api.tasks.bundling.Jar) { //指定生成的jar名 baseName 'dynamic' //从哪里打包class文件 from('build/intermediates/classes/debug/wangyang/zun/com/mydexdemo/dynamic/') //打包到jar后的文件夹结构 into('wangyang/zun/com/mydexdemo/dynamic/') //去掉不须要打包的文件夹和文件 exclude('test/', 'Dynamic.class', 'BuildConfig.class', 'R.class') //去掉R$开头的文件 exclude{ it.name.startsWith('R$');} } makeJar.dependsOn(clearJar, build)
打开AS的 terminal窗体: cd app进入app文件夹,运行gradle makeJar,然后等待直到出现Build Successfully,这时会在build文件夹下出现libs/dynamic.jar文件。这个文件就是我们要用的jar包,我们能够使用jd-gui打开看下是不是仅仅有DynamicImpl这个class。
同一时候,我们要把刚刚生成的dynamic_dex.jar文件放到assets文件夹下,等下须要把它copy到app/data下使用。删除后的整个project文件夹例如以下:
public class FileUtils { public static void copyFiles(Context context, String fileName, File desFile) { InputStream in = null; OutputStream out = null; try { in = context.getApplicationContext().getAssets().open(fileName); out = new FileOutputStream(desFile.getAbsolutePath()); byte[] bytes = new byte[1024]; int i; while ((i = in.read(bytes)) != -1) out.write(bytes, 0 , i); } catch (IOException e) { e.printStackTrace(); }finally { try { if (in != null) in.close(); if (out != null) out.close(); } catch (IOException e) { e.printStackTrace(); } } } public static boolean hasExternalStorage() { return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); } /** * 获取缓存路径 * * @param context * @return 返回缓存文件路径 */ public static File getCacheDir(Context context) { File cache; if (hasExternalStorage()) { cache = context.getExternalCacheDir(); } else { cache = context.getCacheDir(); } if (!cache.exists()) cache.mkdirs(); return cache; } }
public class MainActivity extends AppCompatActivity { private Dynamic dynamic; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //加入一个点击事件 findViewById(R.id.tx).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { loadDexClass(); } }); } /** * 载入dex文件里的class,并调用当中的sayHello方法 */ private void loadDexClass() { File cacheFile = FileUtils.getCacheDir(getApplicationContext()); String internalPath = cacheFile.getAbsolutePath() + File.separator + "dynamic_dex.jar"; File desFile = new File(internalPath); try { if (!desFile.exists()) { desFile.createNewFile(); FileUtils.copyFiles(this, "dynamic_dex.jar", desFile); } } catch (IOException e) { e.printStackTrace(); } //以下開始载入dex class DexClassLoader dexClassLoader = new DexClassLoader(internalPath, cacheFile.getAbsolutePath(), null, getClassLoader()); try { Class libClazz = dexClassLoader.loadClass("wangyang.zun.com.mydexdemo.dynamic.impl.IDynamic"); dynamic = (Dynamic) libClazz.newInstance(); if (dynamic != null) Toast.makeText(this, dynamic.sayHelloy(), Toast.LENGTH_LONG).show(); } catch (Exception e) { e.printStackTrace(); } } }