窝牛号

Android类加载机制

今天窝牛号就给我们广大朋友来聊聊安卓手机如何打开.class文件,以下观点希望能帮助到您。

Android类加载机制

答Android手写热修复(一)--ClassLoader

我们平时编写的 .java 文件不是可执行文件,需要先编译成 .class 文件才可以被虚拟机执行。所谓类加载是指通过 类加载器 把class文件加载到虚拟机的内存空间,具体来说是方法区。类通常是按需加载手或,即第一次使用该类时才加载。

首先,Java与Android都是把类加载到虚拟机内存中,然后由虚拟机转换成设备识别的机器码。但是由于二者使用的虚拟机不同,所以在类加载方面也是有所区别的。Java的虚拟机是JVM,Android的虚拟机是dalvik/art(5.0以后虚拟机是art,是对dalvik的一种升级)。 Java虚拟机运行的是毕空伍class文件,而Android 虚拟机运行的是dex文件。 dex其实是class文件的集合,是对class文件优化的产物,是为了避免出现重复的class。

从上面的讲解中,我们已经知道我们平时写的类是被 类加载器 加载尽虚拟机内存才能运行。下面就通过Framework源码来为大家讲解Android中最主要的5个类加载器。

在Activity做个简单验证:

结果:

可以看出系统类由BootClassLoader加载,apk中的类由PathClassLoader加载,PathClassLoader的父类加载器是BootClassLoader。如果暂时不能理解父类加载器是什么,没关系,后面讲双亲委托机制的时候会理解的。

下面的源码解析基于 Android SDK API28 ,这几个类加载器(除了ClassLoader)没办法直接在AS上查看源码,AS搜索到的是反编译的class的内容,是不可信的,为大家推荐一个在线工具查看,亏雀 在线查看Android Framework源码 。

用来加载本地文件系统上的文件或目录,通常是用来加载apk中我们自己写的类,而像 Activity.class 这种系统的类不是由它加载。注意:这里,并不像很多网上文章说的那样只能加载apk,本地的其他目录的文件也是可以的,这一点我会在后面验证说明。

也是被用来加载 jar 、apk、dex,通常用来加载未安装到应用中的文件。注意,它需要一个应用私有的可写的目录来存放优化后的dex文件。千万不要选择外部存储路径,因为这样可能会导致你的应用遭到注入攻击。

关于dex文件优化,可能很多人还是不理解,水平有限,我简单解释一下,

构造器参数解释:

关于optimizedDirectory:

1、这是dex优化后的路径,它必须是一个应用私有的可写的目录否则会存在注入攻击的风险;

2、这个参数在API 26(8.0)之前是有值的,之后的话,这个参数已经没有影响了,因为在调用父构造器的时候这个参数始终为null,也就是说Android 8.0 以后DexClassLoader和PathClassLoader基本一样的来;

3、在加载app的时候,apk内部的dex已经执行过优化了,优化之后放在系统目录/data/dalvik-cache下。

这个构造器的关键是初始化了一个DexPathList对象,这个是后面加载class的关键类。

这个构造方法等关键是通过 makeDexElements() 方法来获取Element数组,这个Element数组非常关键,后面查找class就会用到它,也是热修复的关键点之一。

splitDexPath(dexPath) 方法是把dexPath目录下的所有文件转换成一个File集合,如果是多个文件的话,会用 : 作为分隔符。

makeDexElements()

小结一下,这个方法就是把指定目录下的文件apk/jar/zip/dex按不同的方式封装成Element对象,然后按顺序添加到Element[]数组中。

DexPathList#loadDexFile()

可以看到 DexFile 最终是调用了openDexFile、native方法openDexFileNative去打开Dex文件的,如果outputName为空,则自动生成一个缓存目录,具体来说是 /data/dalvik-cache/xxx@classes.dex 。openDexFileNative这个native方法就不具体分析了,主要是对dex文件进行了优化操作,将优化后得odex文件通过mmap映射到内存中。感兴趣的同学可以参考:

《DexClassLoader和PathClassLoader加载Dex流程》

现在在回头看看DexClassLoader与PathClassLoader的区别。DexClassLoader可以指定odex的路径,而PathClassLoader则采用系统默认的缓存路径,在8.0以后没有区别。

ClassLoader是一个抽象类,有3个构造方法,最终调用的还是第一个构造方法,主要功能是保存实现类传入的parent参数,也就是父类加载器。ClassLoader的实现类主要有2个,一个是前面讲过的BaseDexClassLoader,另一个是BootClassLoader。

BootClassLoader是ClassLoader的内部类,而且继承了ClassLoader。

这是加载一个类的入口,流程如下:

1、 先检查这个类是否已经被加载,有的话直接返回Class对象;

2、如果没有加载过,通过父类加载器去加载,可以看出parent是通过递归的方式去加载class的;

3、如果所有的父类加载器都没有加载过,就由当前的类加载器去加载。

通常我们自己写的类是通过当前类加载器调用 findClass 方法去加载的,但是在 ClassLoader 中这是个空方法,具体的实现在它的子类 BaseDexClassLoader 中。

BaseDexClassLoader # findClass

可以看到是通过pathList去查找class的,这个对象其实之前讲过,它是在BaseDexClassLoader 的构造方法中初始化的,它实际上是一个 DexPathList 对象。

DexPathList # findClass()

对Element数组遍历,再通过Element对象的 findClass 方法去查找class,有的话就直接返回这个class,找不到则返回null。 这里可以看出获取Class是通过DexFile来实现的,而各种类加载器操作的是Dex。Android虚拟机加载的dex文件,而不是class文件。

1、加载一个类是通过双亲委托机制来实现的。

2、如果是第一次加载class,那是通过 BaseDexClassLoader 中的findClass方法实现的;接着进入 DexPathList 中的findClass方法,内部通过遍历Element数组,从Element对象中去查找类;Element实际上是对Dex文件的包装,最终还是从dexfile去查找的class。

3、一般app运行主要用到2个类加载器,一个是PathClassLoader:主要用于加载自己写的类;另一个是BootClassLoader:用于加载Framework中的类;

4、热修复和插件化一般是利用DexClassLoader来实现。

5、PathClassLoader和DexClassLoader其实都可以加载apk/jar/dex,区别是 DexClassLoader 可以指定 optimizedDirectory ,也就是 dex2oat 的产物 .odex 存放的位置,而 PathClassLoader 只能使用系统默认位置。但是在8.0 以后二者是没有区别的,只能使用系统默认的位置了。

这张图来源于:

Android虚拟机框架:类加载机制

在类加载流程分析中,我们已经知道,查找class是通过DexPathList来完成的,实际上DexPathList最终还是遍历其Element数组,获取DexFile对象来加载Class文件。 由于数组是有序的,如果2个dex文件中存在相同类名的class,那么类加载器就只会加载数组前面的dex中的class。如果apk中出现了有bug的class,那只要把修复的class打包成dex文件并且放在 DexPathList 中Element数组`的前面,就可以实现bug修复了 。下一篇为大家带来的手写热修复。

Android类加载机制的细枝末节

从JVM到Dalivk再到ART(class,dex,odex,vdex,ELF)

类加载机制系列2——深入理解Android中的类加载器

Android 热修复核心原理,ClassLoader类加载

有没有手机上可以运行java程序的软件?

答能不能再手机系统运行,取决于这段java代码是什么格式。还有手机是什么系统。比如。如果手机是苹果的。那么就不可以,它的应用是用oc或者swift来写的。如果是微软手机系统的也不行,用C#语言开发。如果是安卓手机,他的应用是java开发的。但是也要遵守一定规范并且制作成apk才可以运行。如果非得要运行一段java代码,也是可以的。就是自己制作一个apk来调用java程序中class的已知方法。(但是程序自己本身是不能独立执行的。需要外部程序的调用。)

肯定晌掘是有的,下面介绍一款可以随时随地进行编辑,编译和运行Java代码的工具。

它的主要特性包括如下内容:

1)支持Java9编译器;

2)开发者键盘,在默认键盘上增加一排新按键,包含常用符号,标点,括号等;

3)支持智能代码补全功尘谨顷能,自动补全类,函数,关键字的名称。软件会分析文本,给出选项,开发者可以自由选择正确的内容;

4)支持程序员喜欢的黑色背景主题;

1.如果你使用的苹果手机,打开AppStore,搜索Pico编译器-Java编辑器,该软件93.8MB.点击获取将自动下载并安装。

2.安装完成后点击软件打开,在“项目”选项卡中可以创建新的工程,创建完成后进入派陆代码编辑框。

3.在代码编辑框输入自己的代码,可以看出对于关键字等,有彩色显示,行号显示。

4.完成代码编写后,可以点击左上角的绿色三角,编译运行程序,软件自动跳转到输出,并将结果显示在输出框中。

5.该软件还有提供一些技术文档,可供参考,如软件的使用文档。最主要是有:JavaAPISpecification,JavaProgramming。

该软件是收费软件,要想获得完整功能,需要购买专业版。不过价格也不高。

如何查看Android源码

答当我们在eclipse中开发android程序的时候,往往需要看源代码(可能是出于好奇,可能是读源码习惯),那么如何查看Android源代码呢?

比镇毁颤如下面这种情况

假设我们想参看Activity类的源代码,按着Ctrl键,左击它,现实的结果却看不到代码的,提示的信息便是“找不到余春Activity.class文件”。

此时点击下面的按钮,“Change Attached Source…”,选择android源代码所在位置,便弹出图三的对话框。

第一种是选择工作目录,即已经存在的android应用程序源代码。

第二种分两种方式

(1)选择External File…按钮,添加Jar格式文件或者zip格式文件路径;

(2)选择External Floder…按钮,添加文件夹所在路径。

下面问题就来了,源代码在哪里?不能御败凭空产生阿。

可以通过Android SDK Manager进行源代码下载;(推荐该种方法),如图四

勾选Source for Android SDK,进行下载即可。

此外也可通过其他途径下载,网上有很多共享的资源。

这里选择第二种方式的(2)方法,选择源码所在目录(即图四下载源代码目录所在路径),如图五

点击“OK”按钮,此时,Activity文件便能够查看源代码了,如图六。

这样就大功告成了!

通过上文关于安卓手机如何打开.class文件的相关信息,窝牛号相信你已经得到许多的启发,也明白类似这种问题的应当如何解决了,假如你要了解其它的相关信息,请点击窝牛号的其他页面。

本站所发布的文字与图片素材为非商业目的改编或整理,版权归原作者所有,如侵权或涉及违法,请联系我们删除

窝牛号 wwww.93ysy.com   沪ICP备2021036305号-1