- AndroidManifest.xml:包含核心Android清单文件。该文件列出应用程序的名称,版本,访问权限和引用的库文件。该文件使用Android的二进制XML格式。
- lib、class.dex和res占用了超过90%的空间,所以这三块是优化Apk大小的重点(实际情况不唯一)
减少res,压缩图文文件:图片文件压缩是针对jpg和png格式的图片。我们通常会放置多套不同分辨率的图片以适配不同的屏幕,这里可以进行适当的删减。在实际使用中,只保留一到两套就足够了(保留一套的话建议保留xxhdpi,两套的话就加上hdpi),然后再对剩余的图片进行压缩(jpg采用优图压缩,png尝试采用pngquant压缩)
减少dex文件大小:
- 添加资源混淆
- shrinkResources为true表示移除未引用资源,和代码压缩协同工作。
- minifyEnabled为true表示通过ProGuard启用代码压缩,配合proguardFiles的配置对代码进行混淆并移除未使用的代码。
- 代码混淆在压缩apk的同时,也提升了安全性。
减少lib文件大小:由于引用了很多第三方库,lib文件夹占用的空间通常都很大,特别是有so库的情况下。很多so库会同时引入armeabi、armeabi-v7a和x86这几种类型,这里可以只保留armeabi或armeabi-v7a的其中一个就可以了,实际上微信等主流app都是这么做的。
只需在build.gradle直接配置即可,NDK配置同理
4、如何通过Gradle配置多渠道包?
参考回答:首先要了解设置多渠道的原因。在安装包中添加不同的标识,配合自动化埋点,应用在请求网络的时候携带渠道信息,方便后台做运营统计,比如说统计我们的应用在不同应用市场的下载量等信息
这里以友盟统计为例:
- 首先在manifest.xml文件中设置动态渠道变量:
- 接着在app目录下的build.gradle中配置productFlavors,也就是配置打包的渠道:
最后在编辑器下方的Teminal输出命令行:
- 执行./gradlew assembleRelease ,将会打出所有渠道的release包;
- 执行./gradlew assembleVIVO,将会打出VIVO渠道的release和debug版的包;
- 执行./gradlew assembleVIVORelease将生成VIVO的release包。
推荐文章:美团Android自动化之旅—Walle生成渠道包(https://github.com/Meituan-Dianping/walle)
5、插件化原理分析
参考回答:插件化是指将 APK 分为宿主和插件的部分。把需要实现的模块或功能当做一个独立的提取出来,在 APP 运行时,我们可以动态的载入或者替换插件部分,减少宿主的规模
- 宿主: 就是当前运行的APP。
- 插件: 相对于插件化技术来说,就是要加载运行的apk类文件。
而热修复则是从修复bug的角度出发,强调的是在不需要二次安装应用的前提下修复已知的bug。
类加载机制:Android中常用的两种类加载器,DexClassLoader和PathClassLoader,它们都继承于BaseDexClassLoader,两者区别在于PathClassLoader只能加载内部存储目录的dex/jar/apk文件。DexClassLoader支持加载指定目录(不限于内部)的dex/jar/apk文件
插件通信:通过给插件apk生成相应的DexClassLoader便可以访问其中的类,可分为单DexClassLoader和多DexClassLoader两种结构。
- 若使用多ClassLoader机制,主工程引用插件中类需要先通过插件的ClassLoader加载该类再通过反射调用其方法。插件化框架一般会通过统一的入口去管理对各个插件种类的访问,并且做一定的限制。
- 若使用单ClassLoader机制,主工程则可以直接通过类名去访问插件中的类。该方式有个弊端,若两个不同的插件工程引用了一个库的不同版本,则程序可能会出错。
资源加载:原理在于通过反射将插件apk的路径加入AssetManager中并创建Resource对象加载资源,有两种处理方式:
- 合并式:addAssetPath时加入所有插件和主工程的路径;由于AssetManager中加入了所有插件和主工程的路径,因此生成的Resource可以同时访问插件和主工程的资源。但是由于主工程和各个插件都是独立编译的,生成的资源id会存在相同的情况,在访问时会产生资源冲突。
- 独立式:各个插件只添加自己apk路径,各个插件的资源是互相隔离的,不过如果想要实现资源的共享,必须拿到对应的Resource对象。
推荐文章:
Android动态加载技术 简单易懂的介绍方式(https://segmentfault.com/a/1190000004062866)
深入理解Android插件化技术(https://yq.aliyun.com/articles/361233)
为什么要做热更新(https://www.cnblogs.com/baiqiantao/p/9160806.html)
6、组件化原理
参考回答:引入组件化的原因:项目随着需求的增加规模变得越来越大,规模的增大导致了各种业务错综复杂的交织在一起, 每个业务模块之间,代码没有约束,带来了代码边界的模糊,代码冲突时有发生, 更改一个小问题可能引起一些新的问题, 牵一发而动全身,增加一个新需求,需要熟悉相关的代码逻辑,增加开发时间
- 避免重复造轮子,可以节省开发和维护的成本。
- 可以通过组件和模块为业务基准合理地安排人力,提高开发效率。
- 不同的项目可以共用一个组件或模块,确保整体技术方案的统一性。
- 为未来插件化共用同一套底层模型做准备。
组件化开发流程就是把一个功能完整的App或模块拆分成多个子模块(Module),每个子模块可以独立编译运行,也可以任意组合成另一个新的 App或模块,每个模块即不相互依赖但又可以相互交互,但是最终发布的时候是将这些组件合并统一成一个apk,遇到某些特殊情况甚至可以升级或者降级
举个简单的模型例子:
App是主application,ModuleA和ModuleB是两个业务模块(相对独立,互不影响),Library是基础模块,包含所有模块需要的依赖库,以及一些工具类:如网络访问、时间工具等。
**注意:**提供给各业务模块的基础组件,需要根据具体情况拆分成 aar 或者 library,像登录,基础网络层这样较为稳定的组件,一般直接打包成 aar,减少编译耗时。而像自定义 View 组件,由于随着版本迭代会有较多变化,就直接以源码形式抽离成 Library
推荐文章:干货 | 从智行 Android 项目看组件化架构实践(https://mp.weixin.qq.com/s?__biz=MjM5MDI3MjA5MQ==&mid=2697268363&idx=1&sn=3db2dce36a912936961c671dd1f71c78)
7、跨组件通信
跨组件通信场景:
- 第一种是组件之间的页面跳转 (Activity 到 Activity, Fragment 到 Fragment, Activity 到 Fragment, Fragment 到 Activity) 以及跳转时的数据传递 (基础数据类型和可序列化的自定义类类型)。
- 第二种是组件之间的自定义类和自定义方法的调用(组件向外提供服务)。
跨组件通信方案分析:第一种组件之间的页面跳转实现简单,跳转时想传递不同类型的数据提供有相应的 API即可。
第二种组件之间的自定义类和自定义方法的调用要稍微复杂点,需要 ARouter 配合架构中的 公共服务(CommonService) 实现:
- 提供服务的业务模块:
- 在公共服务(CommonService) 中声明 Service 接口 (含有需要被调用的自定义方法), 然后在自己的模块中实现这个 Service 接口, 再通过 ARouter API 暴露实现类。
- 使用服务的业务模块:通过 ARouter 的 API 拿到这个 Service 接口(多态持有, 实际持有实现类), 即可调用 Service 接口中声明的自定义方法, 这样就可以达到模块之间的交互。
- 此外,可以使用 AndroidEventBus 其独有的 Tag, 可以在开发时更容易定位发送事件和接受事件的代码, 如果以组件名来作为 Tag 的前缀进行分组, 也可以更好的统一管理和查看每个组件的事件, 当然也不建议大家过多使用 EventBus。
如何管理过多的路由表?
- RouterHub 存在于基础库, 可以被看作是所有组件都需要遵守的通讯协议, 里面不仅可以放路由地址常量, 还可以放跨组件传递数据时命名的各种 Key 值, 再配以适当注释, 任何组件开发人员不需要事先沟通只要依赖了这个协议, 就知道了各自该怎样协同工作, 既提高了效率又降低了出错风险, 约定的东西自然要比口头上说强。
- Tips: 如果您觉得把每个路由地址都写在基础库的 RouterHub 中, 太麻烦了, 也可以在每个组件内部建立一个私有 RouterHub, 将不需要跨组件的路由地址放入私有 RouterHub 中管理, 只将需要跨组件的路由地址放入基础库的公有 RouterHub 中管理, 如果您不需要集中管理所有路由地址的话, 这也是比较推荐的一种方式。
ARouter路由原理:ARouter维护了一个路由表Warehouse,其中保存着全部的模块跳转关系,ARouter路由跳转实际上还是调用了startActivity的跳转,使用了原生的Framework机制,只是通过apt注解的形式制造出跳转规则,并人为地拦截跳转和设置跳转条件。
常见的组件化方案如下:
8、组件化中路由、埋点的实现
参考回答:因为在组件化中,各个业务模块之间是各自独立的, 并不会存在相互依赖的关系, 所以一个业务模块是访问不了其他业务模块的代码的, 如果想从 A 业务模块的 A 页面跳转到 B 业务模块的 B 页面, 光靠模块自身是不能实现的,这就需要一种跨组件通信方案—— 路由(Router)
路由主要有以下两种场景:
- 第一种是组件之间的页面跳转 (Activity 到 Activity, Fragment 到 Fragment, Activity 到 Fragment, Fragment 到 Activity) 以及跳转时的数据传递 (基础数据类型和可序列化的自定义类类型)
- 第二种是组件之间的自定义类和自定义方法的调用(组件向外提供服务)
其原理在于将分布在不同组件module中的某些类按照一定规则生成映射表(数据结构通常是Map,Key为一个字符串,Value为类或对象),然后在需要用到的时候从映射表中根据字符串从映射表中取出类或对象,本质上是类的查找。
埋点则是在应用中特定的流程收集一些信息,用来跟踪应用使用的状况:
- 代码埋点:在某个事件发生时调用SDK里面相应的接口发送埋点数据,百度统计、友盟、TalkingData、Sensors Analytics等第三方数据统计服务商大都采用这种方案
- 全埋点:全埋点指的是将Web页面/App内产生的所有的、满足某个条件的行为,全部上报到后台服务器
- 可视化埋点:通过可视化工具(例如Mixpanel)配置采集节点,在Android端自动解析配置并上报埋点数据,从而实现所谓的自动埋点
- 无埋点:它并不是真正的不需要埋点,而是Android端自动采集全部事件并上报埋点数据,在后端数据计算时过滤出有用数据
推荐文章:安卓组件化开源方案实现(https://juejin.im/post/5a7ab8846fb9a0634514a2f5)
9、Hook以及插桩技术
参考回答:Hook是一种用于改变API执行结果的技术,能够将系统的API函数执行重定向(应用的触发事件和后台逻辑处理是根据事件流程一步步地向下执行。而Hook的意思,就是在事件传送到终点前截获并监控事件的传输,像个钩子钩上事件一样,并且能够在钩上事件时,处理一些自己特定的事件,例如逆向破解App)
image.gif
Android 中的 Hook 机制,大致有两个方式:
- 要 root 权限,直接 Hook 系统,可以干掉所有的 App。
- 无 root 权限,但是只能 Hook 自身app,对系统其它 App 无能为力。
插桩是以静态的方式修改第三方的代码,也就是从编译阶段,对源代码(中间代码)进行编译,而后重新打包,是静态的篡改; 而Hook则不需要在编译阶段修改第三方的源码或中间代码,是在运行时通过反射的方式修改调用,是一种动态的篡改
推荐文章:
Android插件化原理解析——Hook机制之动态代理(http://weishu.me/2016/01/28/understand-plugin-framework-proxy-hook/)
android 插桩基本概念(https://blog.csdn.net/fei20121106/article/details/51879047)
Android逆向之旅(http://www.520monkey.com/)
10、Android的签名机制?
参考回答:Android的签名机制包含有消息摘要、数字签名和数字证书
- **消息摘要:**在消息数据上,执行一个单向的 Hash 函数,生成一个固定长度的Hash值
- **数字签名:**一种以电子形式存储消息签名的方法,一个完整的数字签名方案应该由两部分组成:签名算法和验证算法
- **数字证书:**一个经证书授权(Certificate Authentication)中心数字签名的包含公钥拥有者信息以及公钥的文件
推荐文章:一篇文章看明白 Android v1 & v2 签名机制(https://blog.csdn.net/freekiteyu/article/details/84849651)
11、v3签名key和v2还有v1有什么区别
参考回答:在v1版本的签名中,签名以文件的形式存在于apk包中,这个版本的apk包就是一个标准的zip包,V2和V1的差别是V2是对整个zip包进行签名,而且在zip包中增加了一个apk signature block,里面保存签名信息。
v2版本签名块(APK Signing Block)本身又主要分成三部分:
- SignerData(签名者数据):主要包括签名者的证书,整个APK完整性校验hash,以及一些必要信息
- Signature(签名):开发者对SignerData部分数据的签名数据
- PublicKey(公钥):用于验签的公钥数据
v3版本签名块也分成同样的三部分,与v2不同的是在SignerData部分,v3新增了attr块,其中是由更小的level块组成。每个level块中可以存储一个证书信息。前一个level块证书验证下一个level证书,以此类推。最后一个level块的证书,要符合SignerData中本身的证书,即用来签名整个APK的公钥所属于的证书
推荐文章:
APK 签名方案 v3(https://source.android.google.cn/security/apksigning/v3)
Android P v3签名新特性(https://xuanxuanblingbling.github.io/ctf/android/2018/12/30/signature/)
12、Android5.0~10.0之间大的变化
Android 5.0新特性:
- MaterialDesign设计风格
最后
Android学习是一条漫长的道路,我们要学习的东西不仅仅只有表面的 技术,还要深入底层,弄明白下面的 原理,只有这样,我们才能够提高自己的竞争力,在当今这个竞争激烈的世界里立足。
人生不可能一帆风顺,有高峰自然有低谷,要相信,那些打不倒我们的,终将使我们更强大,要做自己的摆渡人。
我把自己这段时间整理的Android最重要最热门的学习方向资料放在了我的GitHub,里面还有不同方向的自学编程路线、面试题集合/面经、及系列技术文章等。
资源持续更新中,欢迎大家一起学习和探讨。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
外链图片转存中…(img-hAt3D2P0-1711009484421)]
[外链图片转存中…(img-kcpiTJK6-1711009484421)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-PrJoeqJK-1711009484421)]