先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip204888 (备注Android)
正文
前言
现如今的Android面试中,热修复、插件化、组件化等技术几乎成为了各大小厂面试必问的知识点。特此为大家做了一个“全家桶”。让大伙不用再去网上收集各种零散的知识点。希望的朋友请点个赞支持一下~
1、热修复和插件化
Android中ClassLoader的种类&特点
- BootClassLoader(Java的BootStrap ClassLoader): 用于加载Android Framework层class文件。
- PathClassLoader(Java的App ClassLoader): 用于加载已经安装到系统中的apk中的class文件。
- DexClassLoader(Java的Custom ClassLoader): 用于加载指定目录中的class文件。
- BaseDexClassLoader: 是PathClassLoader和DexClassLoader的父类。
热修补技术是怎样实现的,和插件化有什么区别?
插件化:动态加载主要解决3个技术问题:
- 1、使用ClassLoader加载类。
- 2、资源访问。
- 3、生命周期管理。
插件化是体现在功能拆分方面的,它将某个功能独立提取出来,独立开发,独立测试,再插入到主应用中。以此来减少主应用的规模。
热修复:
原因:因为一个dvm中存储方法id用的是short类型,导致dex中方法不能超过65536个。
代码热修复原理:
- 将编译好的class文件拆分打包成两个dex,绕过dex方法数量的限制以及安装时的检查,在运行时再动态加载第二个dex文件中。
- 热修复是体现在bug修复方面的,它实现的是不需要重新发版和重新安装,就可以去修复已知的bug。
- 利用PathClassLoader和DexClassLoader去加载与bug类同名的类,替换掉bug类,进而达到修复bug的目的,原理是在app打包的时候阻止类打上CLASS_ISPREVERIFIED标志,然后在热修复的时候动态改变BaseDexClassLoader对象间接引用的dexElements,替换掉旧的类。
相同点:
都使用ClassLoader来实现加载新的功能类,都可以使用PathClassLoader与DexClassLoader。
不同点:
热修复因为是为了修复Bug的,所以要将新的类替代同名的Bug类,要抢先加载新的类而不是Bug类,所以多做两件事:在原先的app打包的时候,阻止相关类去打上CLASS_ISPREVERIFIED标志,还有在热修复时动态改变BaseDexClassLoader对象间接引用的dexElements,这样才能抢先代替Bug类,完成系统不加载旧的Bug类.。 而插件化只是增加新的功能类或者是资源文件,所以不涉及抢先加载新的类这样的使命,就避过了阻止相关类去打上CLASS_ISPREVERIFIED标志和还有在热修复时动态改变BaseDexClassLoader对象间接引用的dexElements.
所以插件化比热修复简单,热修复是在插件化的基础上在进行替换旧的Bug类。
热修复原理:
资源修复:
很多热修复框架的资源修复参考了Instant Run的资源修复的原理。
传统编译部署流程如下:
Instant Run编译部署流程如下:
- Hot Swap:修改一个现有方法中的代码时会采用Hot Swap。
- Warm Swap:修改或删除一个现有的资源文件时会采用Warm Swap。
- Cold Swap:有很多情况,如添加、删除或修改一个字段和方法、添加一个类等。
Instant Run中的资源热修复流程:
- 1、创建新的AssetManager,通过反射调用addAssetPath方法加载外部的资源,这样新创建的AssetManager就含有了外部资源。
- 2、将AssetManager类型的mAssets字段的引用全部替换为新创建的AssetManager。
代码修复:
1、类加载方案:
65536限制:
65536的主要原因是DVM Bytecode的限制,DVM指令集的方法调用指令invoke-kind索引为16bits,最多能引用65535个方法。
LinearAlloc限制:
- DVM中的LinearAlloc是一个固定的缓存区,当方法数超过了缓存区的大小时会报错。
Dex分包方案主要做的是在打包时将应用代码分成多个Dex,将应用启动时必须用到的类和这些类的直接引用类放到Dex中,其他代码放到次Dex中。当应用启动时先加载主Dex,等到应用启动后再动态地加载次Dex,从而缓解了主Dex的65536限制和LinearAlloc限制。
加载流程:
- 根据dex文件的查找流程,我们将有Bug的类Key.class进行修改,再将Key.class打包成包含dex的补丁包Patch.jar,放在Element数组dexElements的第一个元素,这样会首先找到Patch.dex中的Key.class去替换之前存在Bug的Key.class,排在数组后面的dex文件中存在Bug的Key.class根据ClassLoader的双亲委托模式就不会被加载。
类加载方案需要重启App后让ClassLoader重新加载新的类,为什么需要重启呢?
- 这是因为类是无法被卸载的,要想重新加载新的类就需要重启App,因此采用类加载方案的热修复框架是不能即时生效的。
各个热修复框架的实现细节差异:
- QQ空间的超级补丁和Nuwa是按照上面说的将补丁包放在Element数组的第一个元素得到优先加载。
- 微信的Tinker将新旧APK做了diff,得到path.dex,再将patch.dex与手机中APK的classes.dex做合并,生成新的classes.dex,然后在运行时通过反射将classes.dex放在Elements数组的第一个元素。
- 饿了么的Amigo则是将补丁包中每个dex对应的Elements取出来,之后组成新的Element数组,在运行时通过反射用新的Elements数组替换掉现有的Elements数组。
2、底层替换方案:
当我们要反射Key的show方法,会调用Key.class.getDeclaredMethod(“show”).invoke(Key.class.newInstance());,最终会在native层将传入的javaMethod在ART虚拟机中对应一个ArtMethod指针,ArtMethod结构体中包含了Java方法的所有信息,包括执行入口、访问权限、所属类和代码执行地址等。
替换ArtMethod结构体中的字段或者替换整个ArtMethod结构体,这就是底层替换方案。
AndFix采用的是替换ArtMethod结构体中的字段,这样会有兼容性问题,因为厂商可能会修改ArtMethod结构体,导致方法替换失败。
Sophix采用的是替换整个ArtMethod结构体,这样不会存在兼容问题。
底层替换方案直接替换了方法,可以立即生效不需要重启。采用底层替换方案主要是阿里系为主,包括AndFix、Dexposed、阿里百川、Sophix。
3、Instant Run方案:
什么是ASM?
ASM是一个java字节码操控框架,它能够动态生成类或者增强现有类的功能。ASM可以直接产生class文件,也可以在类被加载到虚拟机之前动态改变类的行为。
Instant Run在第一次构建APK时,使用ASM在每一个方法中注入了类似的代码逻辑:当Change不为Null时,则调用他的access dispatch方法,参数为具体的方法名和方法参数。当MainActivity的onCreate方法做了修改,就会生成替换类MainActivity
change设置为MainActivity Override
最后change就不会为null,则会执行MainActivity Override
dispatch方法,最终会执行onCreate方法,从而实现了onCreate方法的修改。
借鉴Instant Run原理的热修复框架有Robust和Aceso。
动态链接库修复:
重新加载so。
加载so主要用到了System类的load和loadLibrary方法,最终都会调用到nativeLoad方法。其会调用JavaVMExt的LoadNativeLibrary函数来加载so。
so修复主要有两个方案:
- 1、将so补丁插入到NativeLibraryElement数组的前部,让so补丁的路径先被返回和加载。
- 2、调用System的load方法来接管so的加载入口。
为什么选用插件化?
在Android传统开发中,一旦应用的代码被打包成APK并被上传到各个应用市场,我们就不能修改应用的源码了,只能通过服务器来控制应用中预留的分支代码。但是很多时候我们无法预知需求和突然发生的情况,也就不能提前在应用代码中预留分支代码,这时就需要采用动态加载技术,即在程序运行时,动态加载一些程序中原本不存在的可执行文件并运行这些文件里的代码逻辑。其中可执行文件包括动态链接库so和dex相关文件(dex以及包含dex的jar/apk文件)。随着应用开发技术和业务的逐步发展,动态加载技术派生出两个技术:热修复和插件化。其中热修复技术主要用来修复Bug,而插件化技术则主要用于解决应用越来越庞大以及功能模块的解耦。详细点说,就是为了解决以下几种情况:
- 1、业务复杂、模块耦合:随着业务越来越复杂,应用程序的工程和功能模块数量会越来越多,一个应用可能由几十甚至几百人来协同开发,其中的一个工程可能就由一个小组来进行开发维护,如果功能模块间的耦合度较高,修改一个模块会影响其它功能模块,势必会极大地增加沟通成本。
- 2、应用间的接入:当一个应用需要接入其它应用时,如淘宝,为了将流量引流到其它的淘宝应用如:飞猪旅游、口碑外卖、聚划算等等应用,如使用常规技术有两个问题:可能要维护多个版本的问题或单个应用体积将会非常庞大的问题。
- 3、65536限制,内存占用大。
插件化的思想:
安装的应用可以理解为插件,这些插件可以自由地进行插拔。
插件化的定义:
插件一般是指经过处理的APK,so和dex等文件,插件可以被宿主进行加载,有的插件也可以作为APK独立运行。
将一个应用按照插件的方式进行改造的过程就叫作插件化。
插件化的优势:
- 低耦合
- 应用间的接入和维护更便捷,每个应用团队只需要负责自己的那一部分。
- 应用及主dex的体积也会相应变小,间接地避免了65536限制。
- 第一次加载到内存的只有淘宝客户端,当使用到其它插件时才会加载相应插件到内存,以减少内存占用。
插件化框架对比:
- 最早的插件化框架:2012年大众点评的屠毅敏就推出了AndroidDynamicLoader框架。
- 目前主流的插件化方案有滴滴任玉刚的VirtualApk、360的DroidPlugin、RePlugin、Wequick的Small框架。
- 如果加载的插件不需要和宿主有任何耦合,也无须和宿主进行通信,比如加载第三方App,那么推荐使用RePlugin,其他情况推荐使用VirtualApk。由于VirtualApk在加载耦合插件方面是插件化框架的首选,具有普遍的适用性,因此有必要对它的源码进行了解。
插件化原理:
Activity插件化:
主要实现方式有三种:
- 反射:对性能有影响,主流的插件化框架没有采用此方式。
- 接口:dynamic-load-apk采用。
- Hook:主流。
Hook实现方式有两种:Hook IActivityManager和Hook Instrumentation。主要方案就是先用一个在AndroidManifest.xml中注册的Activity来进行占坑,用来通过AMS的校验,接着在合适的时机用插件Activity替换占坑的Activity。
Hook IActivityManager:
1、占坑、通过校验:
在Android 7.0和8.0的源码中IActivityManager借助了Singleton类实现单例,而且该单例是静态的,因此IActivityManager是一个比较好的Hook点。
接着,定义替换IActivityManager的代理类IActivityManagerProxy,由于Hook点IActivityManager是一个接口,建议这里采用动态代理。
- 拦截startActivity方法,获取参数args中保存的Intent对象,它是原本要启动插件TargetActivity的Intent。
- 新建一个subIntent用来启动StubActivity,并将前面得到的TargetActivity的Intent保存到subIntent中,便于以后还原TargetActivity。
- 最后,将subIntent赋值给参数args,这样启动的目标就变为了StubActivity,用来通过AMS的校验。
然后,用代理类IActivityManagerProxy来替换IActivityManager。
- 当版本大于等于26时,使用反射获取ActivityManager的IActivityManagerSingleton字段,小于时则获取ActivityManagerNative中的gDefault字段。
- 然后,通过反射获取对应的Singleton实例,从上面得到的2个字段中拿到对应的IActivityManager。
- 最后,使用Proxy.newProxyInstance()方法动态创建代理类IActivityManagerProxy,用IActivityManagerProxy来替换IActivityManager。
2、还原插件Activity:
- 前面用占坑Activity通过了AMS的校验,但是我们要启动的是插件TargetActivity,还需要用插件TargetActivity来替换占坑的SubActivity,替换时机为图中步骤2之后。
- 在ActivityThread的H类中重写的handleMessage方法会对LAUNCH_ACTIVITY类型的消息进行处理,最终会调用Activity的onCreate方法。在Handler的dispatchMessage处理消息的这个方法中,看到如果Handelr的Callback类型的mCallBack不为null,就会执行mCallback的handleMessage方法,因此mCallback可以作为Hook点。我们可以用自定义的Callback来替换mCallback。
自定义的Callback实现了Handler.Callback,并重写了handleMessage方法,当收到消息的类型为LAUNCH_ACTIVITY时,将启动SubActivity的Intent替换为启动TargetActivity的Intent。然后使用反射将Handler的mCallback替换为自定义的CallBack即可。使用时则在application的attachBaseContext方法中进行hook即可。
3、插件Activity的生命周期:
- AMS和ActivityThread之间的通信采用了token来对Activity进行标识,并且此后的Activity的生命周期处理也是根据token来对Activity进行标识的,因为我们在Activity启动时用插件TargetActivity替换占坑SubActivity,这一过程在performLaunchActivity之前,因此performLaunchActivity的r.token就是TargetActivity。所以TargetActivity具有生命周期。
Hook Instrumentation:
最后
今天关于面试的分享就到这里,还是那句话,有些东西你不仅要懂,而且要能够很好地表达出来,能够让面试官认可你的理解,例如Handler机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。
最后在这里小编分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。
还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
【算法合集】
【延伸Android必备知识点】
【Android部分高级架构视频学习资源】
**Android精讲视频领取学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-Nq34f0lS-1713078509438)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!