大佬带你走进Android开发的世界,掌握了这些知识点,学习Android也可以很轻松。
核心分析内容
对于怎么学习Android,主要解决的是3个问题:学什么、怎么学 & 怎么用。
具体如下:
下面,我将带着上述几个问题,**详细讲解自身学习Android的方法和Android学习路径;**最后,还会结合前面内容,给出综合的具体执行学习Android的建议。
演示方法:
- 删掉 fix.dex ,运行app,你看到 手机屏幕中心 出现:“卧槽,有bug!”
- 还原 fix.dex ,运行app,你看到 手机屏幕中心 出现:“嘿嘿,bug已修复”
起作用的是谁?就是这个fix.dex
文件.
Demo源码概览
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yxe9h12O-1649660580538)(https://upload-images.jianshu.io/upload_images/14140248-ad8635e56d003692.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]
如上图所示: 核心类其实就只有一个: ClassLoaderHookHelper
,它 就是 让 fix.dex
这个补丁发挥作用的 " 幕后大佬". 这个核心类:有3个方法,分别是在不同的系统版本上,来对源码程序逻辑进行 hook,提高hook的兼容性.
下面是完整 ClassLoaderHookHelper
代码 以及 使用它的 MyApp
完整代码 :
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Field
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class ClassLoaderHookHelper {
//23和19的差别,就是 makeXXXElements 方法名和参数要求不同
//后者是 makeDexElements(ArrayList files, File optimizedDirectory,ArrayList suppressedExceptions)
//前者是 makePathElements(List files, File optimizedDirectory,List suppressedExceptions)
public static void hookV23(ClassLoader classLoader,File outDexFilePath,File optimizedDirectory)throws IllegalAccessException, InvocationTargetException {
Field pathList =ReflectionUtil.getField(classLoader,“pathList”);//1、获DexPathList pathList 属性
object dexpathListobj =pathList.get(classLoader);//2、获DexPathList pathList对象
Field dexElementsField =ReflectionUtil.getField(dexPathListObj, “dexElements”);//3、获得DexPathList的dexElements属性
Object[] oldElements =(Object[]) dexElementsField.get(dexPathListObj);//4、获得pathList对象中 dexElements 的属性值
…
}
}
Multidex热修复核心技术
其实 热修复的核心技术,就一句话,
HookClassLoader
,但是要深入了解它,需要相当多的基础知识,下面列举出必须要知道的一些东西。
基础知识预备
1.Dex文件是什么?
我们写安卓,目前还是用 java比较多,就算是用 kotlin,它最终也是要转换成 java来运行。 java文件,被编译成 class之后,多个 class文件,会被打包成 classes.dex
,被放到 apk
中,安卓设备拿到 apk
,去安装解析( 预编译balabala…),当我们运行 app时, app的程序逻辑全都是在classes.dex
中。所以, dex
文件是什么?一句话, dex
文件是 android app的源代码的最终打包
2.Dex文件如何生成?
androidStudio
打包 apk
的时候会生成 Dex
,其实它使用的是 SDK
的 dx命令,我们可以用 dx命令自己去打包想要打包的 class. 命令格式为:dx --dex --output=output.dex xxxx.class
将上面的output 和 xxxx换成你想要的文件名即可。
**注:**dx.bat在 安卓 SDK的目录下:比如我d的`C:\XXXXX\AndroidStudioAbout\sdk1\build-tools\28.0.3\dx.bat
3.ClassLoader
是什么?
ClassLoader
来自 jdk
,翻译为 :类加载器,用于将 class文件中的类,加载到内存中,生成 class对象。只有存在了 Class对象,我们才可以创建我们想要的对象。 android SDK
继承了JDK
的 classLoader
,创造出了新的 ClassLoader
子类。下图表示了 android9.0-28 所有的ClassLoader
直接或者间接子类.
比较多的是 BaseDexClassLoader
, DexClassLoader
, PathClassLoader
, 其他这些,应该是谷歌大佬 创造出来新的 类加载器子类吧,还没研究过。
注: 关于 DexClassLoader
和 PathClassLoader
,网上资料有个误区,应该不少人都认为, PathClassLoader
用于加载 app内部的 dex
文件, DexClassLoader
用于加载外部的 dex
文件,但是其实只要看一眼 这两个类的关系,就会发现,它们都是继承自 BaseDexClassLoader
,他们的构造函数内部都会去执行父类的构造函数。他们只有一个差别,那就是 PathClssLoader
不用传 optimizedDirectory
这个参数,但是 DexClassLoader
必须传。这个参数的作用是,传入一个 dex
优化之后的存放目录。而事实上,虽然 PathClassLoader
不要求传这个 optimizedDirectory
,但是它实际上是给了一个默认值。emmmm…所以不要再认为 PathClassLoader
不能加载外部的 dex
了,它只是没有让你传 optimizedDirectory
而已。
另外: BootClassLoader
用于加载 AndroidFramework
层class文件( SDK中没有这个BootClassLoader
,也是很奇怪) PathClassLoader
是用于Android应用程序类的加载器,可以加载指定的 dex,以及 jar、 zip、 apk中的 classes.dex
。 DexClassLoader
可以加载指定的 dex
,以及 jar、 zip、 apk中的 classes.dex
。
4.ClassLoader
的双亲委托机制是什么?
android里面 ClassLoader
的作用,是将 dex
文件中的类,加载到内存中,生成 Class对象,供我们使用 (举个例子:我写了一个 A类,app运行起来之后,当我需要new 一个 A, ClassLoader
首先会帮我查找 A的 Class对象是否存在,如果存在,就直接给我 Class对象,让我拿去 new A,如果不存在,就会出创建这个 A的 Class对象。) 这个查找的过程,就遵循 双亲委托机制。一句话解释 双亲委托机制:某个 类加载器在加载某个 类的时候,首先会将 这件事委托给 parent类加载器,依次递归,如果 parent类加载器可以完成加载,就会直接返回 Class对象。如果 parent找不到或者没有父了,就会 自己加载。
下图是 安卓源码 ClassLoader.java
:
红字注解,很容易读懂 ClassLoader
去 load一个 class的过程.
hook思路
OK,现在可以来解读我是如何去hook ClassLoader
的了. 解读之前,先弄清楚,我为何 要 hookClassLoader
,为什么 hook了它之后,我的 fix.dex
就能发挥作用?先解决这个疑问,既然是 hook,那么自然要读懂源码,因为 hook就是在理解源码思维的前提下,更改源码逻辑。 一张图解决你的疑问:
按照上面图,去追踪源码,会发现, ClassLoader
最终会从 DexFile
对象中去获得一个 Class对象。并且在 DexPathList
类中 findClass
的时候,存在一个 Element数组的遍历。这就意味着,如果存在多个 dex
文件,多个 dex
文件中都存在同样一个 class,那么它会从第一个开始找,如果找到了,就会立即返回。如果没找到,就往下一个 dex
去找。
也就是说,如果我们可以在 这个数组中插入我们自己的修复bug的 fix.dex
,那我们就可以让我们 已经修复bug的补丁类发挥作用,让类加载器优先读取我们的 补丁类.
OK,理解了源码的逻辑,那我们可以动手了。来解析SDK 23的 hookClassLoader
过程吧!
确定思路,我们要改变app启动之后,自带的ClassLoader对象(具体实现类是PathClassLoader )中 DexPathList 中 Element[] element 的实际值。
那么,步骤:
1.取得
PathClassLoader
的pathList
的属性
2.取得PathClassLoader
的pathList
的属性真实值(得到一个DexPathList
对象)
《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》开源
Android优秀开源项目:
- ali1024.coding.net/public/P7/Android/git
最后
那我们该怎么做才能做到年薪60万+呢,对于程序员来说,只有不断学习,不断提升自己的实力。我之前有篇文章提到过,感兴趣的可以看看,到底要学习哪些知识才能达到年薪60万+。
通过职友集数据可以查看,以北京 Android 相关岗位为例,其中 【20k-30k】 薪酬的 Android 工程师,占到了整体从业者的 30.8%!
北京 Android 工程师「工资收入水平 」
今天重点内容是怎么去学,怎么提高自己的技术。
1.合理安排时间
2.找对好的系统的学习资料
3.有老师带,可以随时解决问题
4.有明确的学习路线
当然图中有什么需要补充的或者是需要改善的,可以在评论区写下来,一起交流学习。
学习资料
3.有老师带,可以随时解决问题
4.有明确的学习路线
当然图中有什么需要补充的或者是需要改善的,可以在评论区写下来,一起交流学习。
[外链图片转存中…(img-FCyqeJ8u-1649660580540)]