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虚拟机类载入机制

Dalvik虚拟机如同其它Java虚拟机一样。在执行程序时首先须要将相应的类载入到内存中。

而在Java标准的虚拟机中。类载入能够从class文件里读取。也能够是其它形式的二进制流,因此。我们经常利用这一点,在程序执行时手动载入Class,从而达到代码动态载入执行的目的,可是Dalvik虚拟机毕竟不算是标准的Java虚拟机,因此在类载入机制上。它们有同样的地方。也有不同之处。


我们先看下以下这张关于Android Classload机制的图。



与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

准备工作開始

一、打开Android studio 新建project:



project文件夹是这种:


动态载入进来的class怎样使用,一般有2种办法,一种是使用反射调用。这样的我不多做介绍;另一种是使用接口编程的方式来调用相应的方法,毕竟.dex文件也是我们自己维护的。所以能够把方法抽象成公共接口,把这些接口也拷贝到主项目里面去,就能够通过这些接口调用动态载入得到的实例的方法了。
接下来我们源代码包以下新建一个包名称是dynamic,然后在dynamic下新建一个interface接口Dynamic。里面有个接口方法,就叫sayHello()吧,返回一个String,到时候我们能够通过Toast弹出来,Dynamic.java:
package wangyang.zun.com.mydexdemo.dynamic;

/**
* Created by WangYang on 2016/3/11.
*/
public interface Dynamic {
String sayHello();
}
接着我们新建一个impl包。并实现Dynamic接口,DynamicImpl.java:
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."

详细的project文件夹例如以下图:


点击Build -> make project。这时候会在build\intermediates\classes\debug文件夹下生成相应的classes文件。




好了我们要把DynamicImpl这个class转换成Dalvik可识别的dex文件,分两步:
1.先导出DynamicImpl这个类为jar包的形式。
2.通过android sdk自带的dx.jar工具转换jar包为dex文件。

完毕第一步。当时遇到点麻烦。因为eclipse是基于ant而且有可视化工具,能够直接导出指定文件的jar包。可是android studio不行,那怎么办呢?
我们能够通过gradle task来打包,打开app文件夹下的build.gradle文件,切记不是根文件夹的build.gradle文件,加上下面代码:
//删除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。

第二步,使用sdk提供的dx.jar将导出的dynamic.jar转换成Dalvik可识别的dex格式。新版的sdk已经将dx.jar放到build-tools\23.0.2\lib文件夹下。我们在dos下或者在Android studio terminal以下进入到此文件夹,然后执行以下的命令:
dx --dex --output=dynamic_dex.jar dynamic.jar



output是你的输出文件夹,默认就是在当前的根文件夹下,运行完毕后我们就在当前文件夹下生成了Davilk虚拟机可运行的dex文件。由于这条命令同一时候会打包dex文件,因此后缀是jar。我们用jd-gui打开dynamic.jar和dynamic_dex.jar这两个文件,看下他们有的结构。






能够看到,打包后的文件事实上是一个classes.dex文件,眼下为止我们要做的工作已经准备就绪了,接下来就是要在demo中使用这个dex文件。


二、删除刚刚新建的impl包以及包内的文件:
由于等下我们要使用的是dex以下的IDynamic实现类。所以我们须要删除当前project下的IDynamic.java文件和impl包,避免执行时出错。

同一时候,我们要把刚刚生成的dynamic_dex.jar文件放到assets文件夹下,等下须要把它copy到app/data下使用。删除后的整个project文件夹例如以下:




FileUtils类是从assets文件夹下copy文件到app/data/cache文件夹。源代码例如以下:
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;
} }
打开MainActivity:
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();
}
} }
程序执行的效果图例如以下:

至此,我们关于Android Dex动态载入机制的原理说到这里。接下来我会分析下通过Dex实现热修复的基本原理。

Demo源代码地址:https://github.com/wy353208214/MyDexDemo

Android动态载入Dex机制解析的更多相关文章

  1. Android动态载入JAR包的实现方法

    有时候我们须要使用动态更新技术,简单来说就是:当我们把开发jar包发送给用户后.假设后期升级了部分代码.这时让用户的jar包自己主动更新,而不是用户主动手动地去更新的技术就是动态更新.这个须要使用的技 ...

  2. 深入浅出Android动态载入jar包技术

    在实际项目中.因为某些业务频繁变更而导致频繁升级client的弊病会造成较差的用户体验,而这也恰是Web App的优势,于是便衍生了一种思路.将核心的易于变更的业务封装在jar包里然后通过网络下载下来 ...

  3. [转载] Android动态加载Dex机制解析

    本文转载自: http://blog.csdn.net/wy353208214/article/details/50859422 1.什么是类加载器? 类加载器(class loader)是 Java ...

  4. 实现Android 动态载入APK(Fragment or Activity实现)

    尊重原创:http://blog.csdn.net/yuanzeyao/article/details/38565345 近期由于项目太大了.导致编译通只是(Android对一个应用中的方法个数貌似有 ...

  5. Android Touch事件传递机制解析 (推荐)

    最近新闻列表里的下拉 down up  move 等等让我十分头疼 ,无意间看到了一篇非常不错的帖子,转载如下: 开篇语:最近程序在做一个小效果,要用到touch,结果整得云里面雾里的,干脆就好好把a ...

  6. Android Touch事件传递机制解析

    android系统中的每个ViewGroup的子类都具有下面三个和TouchEvent处理密切相关的方法: 1)public boolean dispatchTouchEvent(MotionEven ...

  7. Android 使用动态载入框架DL进行插件化开发

    如有转载,请声明出处: 时之沙: http://blog.csdn.net/t12x3456    (来自时之沙的csdn博客) 概述: 随着应用的不断迭代.应用的体积不断增大,项目越来越臃肿,冗余添 ...

  8. DL动态载入框架技术文档

    DL动态载入框架技术文档 DL技术交流群:215680213 1. Android apk动态载入机制的研究 2. Android apk动态载入机制的研究(二):资源载入和activity生命周期管 ...

  9. Android 动态加载 (二) 态加载机制 案例二

    探秘腾讯Android手机游戏平台之不安装游戏APK直接启动法 重要说明 在实践的过程中大家都会发现资源引用的问题,这里重点声明两点: 1. 资源文件是不能直接inflate的,如果简单的话直接在程序 ...

随机推荐

  1. Java坦克大战 (五) 之产生敌方坦克和爆炸效果

    本文来自:小易博客专栏.转载请注明出处:http://blog.csdn.net/oldinaction 在此小易将坦克大战这个项目分为几个版本,以此对J2SE的知识进行回顾和总结,希望这样也能给刚学 ...

  2. Solidity 文档--目录

    Solidity是一种语法类似JavaScript的高级语言.它被设计成以编译的方式生成以太坊虚拟机代码.在后续内容中你将会发现,使用它很容易创建用于投票.众筹.封闭拍卖.多重签名钱包等等的合约. 注 ...

  3. javascript实现网页返回顶部功能

    在浏览网页时,我们一般是拖动滚动条向下滑动,浏览下面的内容,当页面超过单页时,右下角会出现一个回到顶部的图标,有些网站这个图标一直显示在右下角的. 有些网站使用锚链接来实现页面内容的跳转,但这种效果的 ...

  4. 十个技巧快速优化你的Laravel 5 程序

    性能一直是 Laravel 框架为人诟病的一个点,所以调优 Laravel 程序算是一个必学的技能. 接下来分享一些开发的最佳实践,还有调优技巧,大家有别的建议也欢迎留言讨论. 这里是简单的列表: 配 ...

  5. HDU 3516 Tree Construction

    区间$dp$,四边形优化. #pragma comment(linker, "/STACK:1024000000,1024000000") #include<cstdio&g ...

  6. Python的异步编程[0] -> 协程[1] -> 使用协程建立自己的异步非阻塞模型

    使用协程建立自己的异步非阻塞模型 接下来例子中,将使用纯粹的Python编码搭建一个异步模型,相当于自己构建的一个asyncio模块,这也许能对asyncio模块底层实现的理解有更大的帮助.主要参考为 ...

  7. ganglia笔记:rrd数据库

    为了给ganglia监控的数据加上报警的功能,在jenkins里加了一个定时job,运行报警的脚本.脚本需要对ganglia记录的数据进行分析,以下是一点粗浅的理解. rrdtool的本质是用于画图的 ...

  8. 洛谷——P1655 小朋友的球

    P1655 小朋友的球 题目描述 @发源于 小朋友最近特别喜欢球.有一天他脑子抽了,从口袋里拿出了N个不同的球,想把它们放到M个相同的盒子里,并且要求每个盒子中至少要有一个球,他好奇有几种放法,于是尝 ...

  9. 多表联合查询,利用 concat 模糊搜索

    select * from t1 as a join t2 as b on a.id = b.id where CONCAT(a.name,b.name) like '%测试%'

  10. 【C++】类的两种实例化方法

    直接上代码: #include<stdio.h> #include<string> #include<iostream> using namespace std; ...