共计 6436 个字符,预计需要花费 17 分钟才能阅读完成。
前言
年中的时候帮部门招人,发现很多候选人对于咱们部门还是很青眼的。也对鸡架部门做的事比拟感兴趣,所以明天这篇水文次要就给大家梳理下基架的面试题以及基础架构组波及的 sdk 相干。
因为最近几年面试常常被人吊打,所以也有了总结面试题的习惯。之后加上之前帮候选人的面试总结,明天给大家再卷一波。
也不是别的啥就是想要毛面试题的流量了呢。
SDK 相干
面试的时候我感觉哦,这些 sdk 有任意其实你钻研的比拟深刻就行了,应该能在面试中体现的很好了。还有就是集体倡议最好还是在繁多方向钻研的更深刻一点,别的只有大略晓得干什么的就行了。
配置核心以及灰度测试
app 必备工具之一,配置核心次要负责的就是动态化的配置,比方文本展现相似这些的。sdk 提供方须要负责的是提供动静更新能力,这里有个差异化更新,只更新 dif 局部,还有就是流量优化等等须要开发同学思考的。而后能够思考下存储性能方面的晋升等。
而 abtest 也是 app 必备工具之一了,动静的下发试验策略,之后开发同学能够切换试验的页面。另外次要须要思考灰度后果计算,分桶以及版本过滤白名单等等。这里只是一个简略的介绍不开展,因为我只是一个应用方。
调试组件
集体还是更举荐滴滴的 Dokit,性能点比拟多而且接入相对来说比较简单。而且提供了很多给开发同学定制的能力,能够在 debug 状况下减少很多业务相干的测试性能,不便测试同学,外围还是浮窗太不便了。
当然很多实验性的预研性能等其实都能够间接接在这里,而后在测试环境下充沛开展,之后在进行线上灰度计划。还有一些具备危险的 hook 操作,集体也比拟倡议放在 debug 组件上。
性能监控框架
这部分有几个不同的方面,首先是异样解体方面的,另外则是性能监控方面的,然而他们整体是划分在一起的,都属于线上性能监控体系的。
Crash 相干的,能够从爱奇艺的 xCrash 学起。蕴含了解体日志,ANR 以及 native crash,因为版本适配的问题 ANR 在高版本上曾经不是这么好捞了,还有就是 native crash 相干的。是一个十分牛逼的库了。
而线上的性能监控框架能够从腾讯的 Matrix 学起, 以前有两篇文章介绍的内容也都是和 Matrix 相干的, Matrix 首页上也有介绍,比方 fps,卡顿,IO,电池,内存等等方面的监控。其中卡顿监控波及到的就是办法前后插桩,同时要有函数的 mapping 表,插桩局部整体来说比较简单感觉。
另外对于线上内存相干的,举荐各位能够学习下快手的 koom, 对于 hprof 的压缩比例据说能达到 70%,也能实现线上的数据回捞以及监控等等,是一个十分屌的框架。上面给大家一个抄答案的形式。字节也有一个相似的原理其实也差不多。
主过程发现内存达到阈值的时候,用 leakcanary 的计划,通过 shark fork 过程内存,之后生成 hrop。因为 hrop 文件绝对较大,所以咱们须要对于咱们所要剖析的内容进行筛选,能够通过 xhook,之后对 hrop 的写入操作进行 hook,当发现写入内容的类型合乎咱们的须要的状况下才进行写入。
而当咱们要做线上日志回捞的状况,须要对 hprof 进行压缩,具体算法能够参考 koom/raphel,有提供对应的压缩算法。
最初线上回捞机制就是基于一个指令,回捞线上符合标准的用户的文件操作,这个自行设计。
其实上述几个库都还是有一个实质相干的货色,那么就是 plthook, 这个下面三个库应该都有对其的应用,之前是爱奇艺的 xhook,当初是字节的 bhook, 这个大佬也是我的偶像之一了,十分离谱了算是。
Android 性能采集之 Fps,Memory,Cpu 和 Android IO 监控
最近曾经不咋写这部分相干了,所以也就没有深挖,然而后续可能会有一篇对于 phtead hook 相干的,也是之前 matrix 更新的一个新货色,还在测试环境灰度阶段。
根底网络组件
尽管外围可能还是三方网络库,然而因为根本所有公司都对网络方面有调整和改变,以及解析器等方面的优化,其实能够挖的货色也还是蛮多的。
应酬面试的同学能够看看 Android 网络优化计划。当然还是要具体问题具体分析,毕竟头疼医头,脚疼医脚对吧。
之前和另外一个敌人聊了下,其实很多厂对 json 解析这部分有优化调整,通过 apt 之后更换原生成原生的解析形式,放慢反序列化速度的都是能够思考思考的。
埋点框架
其实这个应该要放在更后面一点的,数据上报数据分析啥的其实都还是蛮重要的。
这部分因为我齐全没写过哦,所以我压根不咋会,然而如果你会的话,面试的时候开展说说,能够帮忙你不少。
另外还须要有线上的异样用户数据回捞零碎,不便开发同学被动去把线上有异样的用户的日志给收集回来。
然而有些刁钻的页面曝光监控啦,自动化埋点啥的其实还是写过一点的,有趣味的能够翻翻历史,还有 github 上还有 demo。
AndroidAutoTrack demo 工程
启动相干
通过 DAG(有向无环图)的形式将 sdk 的初始化拆解成一个个 task,之后理顺依赖关系,让他们能依照固定的程序向下执行。
外围须要解决的是依赖关系,比如说其实埋点库依赖于网络库初始化,而后 APM 相干的则依赖于埋点库和配置核心 abtest 等等,这样的依赖关系须要开发同学去理顺的。
另外就是把 sdk 的粒度打的细碎一点,更容易察看每个 sdk 工作的耗时状况,之后减少 task 阈值告警,超过某个加载速度就告诉到相应的同学改一下。
多线程是能优化掉一部分,然而也须要防止频繁线程调度。还有就是我集体感觉这些启动相干的货色因为都无奈应用 sdk 级别的灰度,所以改变最好谨慎一点。出发点始终都是好的,然而还是后果导向吧。
启动优化的外围,我集体始终保持的就是提早能力优化。开发人员很难做到优化代码执行的复杂度,执行工夫之类的。尽人事听天命,玄学代码。
中间件 (图片 日志 存储 根底信息)
这部分没啥,最好是对第三方库有一层隔离的思维,然而这个隔离也须要对应的同学对于程序设计方面有很好的思维,说起来简略,其实也蛮简单的。
这里就不开展了,感觉面试也很少会问的很细。
第三方 sdk 大杂烩(偏中台方向)
根本一个 app 当初都有啥分享啦,推送啦,领取啦,账号体系啦,webview,jsbridge 等等服务于利用内的一些 sdk,这些货色就比拟偏差于业务。
有趣味的能够看看之前写的两篇对于 sdk 设计相干的。
活学活用责任链 SDK 开发的一点点心得 Android 厂商推送 Plugin 化
其余方面
大公司可能都会有些动态化计划的思考,比方插件化啊动态化之类的。这部分在下的确不行,我就不开展了啊。
编译相干
形容下 android 编译流程
基架很容易碰到的面试题,以前简略的形容写过。聊聊 Android 编译流程
尽管是几年前的知识点了,然而还是要拆开高下版本的 agp 做比拟的。所以这部分能够答复下,根本这题就能简略的拿下了。
Gradle 生命周期
简略的说下就是 buildSrc 先编译,之后是根目录的 settings.gradle, 根 build.gradle,最初才是 module build
网上一堆,你本人翻一番就晓得了。
apt 是编译中哪个阶段
APT 解析的是 java 形象语法树(AST),属于 javac 的一部分流程。大略流程:.java -> AST -> .class
聊聊 AbstractProcessor 和 Java 编译流程
Dex 和 class 有什么区别
链接传送门
Class 与 dex 的区别
1)虚拟机:class 用 jvm 执行,dex 用 dvm 执行
2)文档:class 中冗余信息多,dex 会去除冗余信息,蕴含所有类,查找不便,适宜手机端
JVM 与 DVM
1)JVM 基于栈(应用栈帧,内存),DVM 基于寄存器,速度更快,适宜手机端
2)JVM 执行 Class 字节码,DVM 执行 DEX
3)JVM 只能有一个实例,一个利用启动运行在一个 DVM
DVM 与 ART
1)DVM:每次运行利用都须要一次编译,效率升高。JIT
2)ART:Android5.0 以上默认为 ART,零碎会在过程装置后进行一次预编译,将代码转为机器语言存在本地,这样在每次运行时不必再进行编译,进步启动效率;。AOP & JIT
Transform 是如何被执行的
Transform 在编译过程中会被封装成 Task 依赖其余编译流程的 Task 执行。
Transform 和其余零碎 Transform 执行的程序
其实这个题目曾经是个过期了,前面对这些都合并整合了,而且最新版的 api 也做了替换,要不然思考下回怼下面试官?
如何监控编译速度变慢问题
./gradlew xxxxx — scan
复制代码
之后会生成一个 gradle 的网页,填写下你的邮箱就好了。
另外一个相对来说比较简单了。通过 gradle 原生提供的 listener 进行就行了。
// 耗时统计 kt 化
class TimingsListener : TaskExecutionListener, BuildListener {
private var startTime: Long = 0L
private var timings = linkedMapOf<String, Long>()
override fun beforeExecute(task: Task) {startTime = System.nanoTime()
}
override fun afterExecute(task: Task, state: TaskState) {val ms = TimeUnit.MILLISECONDS.convert(System.nanoTime() - startTime, TimeUnit.NANOSECONDS)
task.path
timings[task.path] = ms
project.logger.warn("${task.path} took ${ms}ms")
}
override fun buildFinished(result: BuildResult) {project.logger.warn("Task timings:")
timings.forEach {if (it.value >= 50) {project.logger.warn("${it.key} cos ms ${it.value}\n")
}
}
}
override fun buildStarted(gradle: Gradle) {
}
override fun settingsEvaluated(settings: Settings) {
}
override fun projectsLoaded(gradle: Gradle) {
}
override fun projectsEvaluated(gradle: Gradle) {
}
}
gradle.addListener(TimingsListener())
复制代码
Gradle 中如何给一个 Task 前后插入别的工作
最简略的能够思考间接获取到 Task 实例,之后在 after 和 before 插入一些你所须要的代码。
另外一个就是通过 dependOn 前置和 finalizedBy 挂载一个工作 mustAfter
Gradle 使用指南 — Gradle Task
ksp APT Transform 的区别
ksp 是 kotlin 专门独立的 ast 语法树
apt 是 java 的 ast 语法树
transform 是 agp 专门批改字节码的一个办法。
反杀时刻 AsmClassVisitorFactory, 能够看看我之前写的那篇文章。
Transform 上的编译优化能做哪些?
尽管是个行将过期的 api,然而大家对他的改变还是都比拟多的。
首先必定是须要实现增量编译的,具体的能够参考我的 demo 工程。记住,所有的 transfrom 都要全量。
另外能够思考多线程优化,将转化操作挪动到子线程内,倡议应用 gradle 外部的共享线程。
参考 agp 最新做法,形象出一个新的 interface,之后通过 spi 串联,之后将 asm 链式调用。我的文章也介绍过,具体的点在哪里本人盘算。
当初筹备好辞别 Transform 了吗
aar 源码切换插件原理
这个前几天刚介绍过,原理和计划业内都差不多,mulite-repo 应该都须要这个货色的。我的版本也比拟简陋,大厂外部必定都会有些魔改的。
相对来说性能必定会更丰盛,更全面一点。
aar 和源码切换插件 Plus
你们有哪些保障代码品质的伎俩
最简略的形式还是通过动态扫描 +pipline 解决,之后在合并 mr 之前进行一次拦挡。
动态扫描形式比拟多,上面给大家简略的介绍下
阿里的 sonar 然而对 kt 的反对很蹩脚,因为阿里应用,所以有很多现成的规定能够应用,然而如果从 0 - 1 接入,你可能会间接放弃。
原生的 lint,能够基于原生提供的 lint api,对其进行开发,反对品种也多,基本上算是一个十分优良的计划了,然而因为文档资料较少,对于开发的要求可能会较高。
AndroidLint
如何对第三方的依赖做动态查看?
魔高一尺道高一丈。lint 还是能解决这个问题的。
Tree Api+ClassScanner = 辨认三方隐衷权限调用
R.java code too large 解决方案
又是一个过期的问题,尽早降级 agp 版本,让 R8 帮你解决这个问题,R 文件齐全能够内联的。
或者用别的 AGP 插件的 R inline 也能够解决这个问题。
R inline 你须要留神些什么?
预扫描,先收集调用的信息,之后在进行替换。还有 javac 的时候可能就因为文件过大,间接挂掉了。
一个类替换父类 比方所有 activity 实现类替换 baseactivity
class node 间接替换 superName,想起了之前另外一个问题,感觉次要是要对构造函数进行批改,否则也会出异样。
R8 D8 以及混同相干的,还有 R8 除了混同还无能些什么?混同规定有没有碰到什么奇怪的问题?
D8 和 Dx 的区别,次要波及到编译速度以及编译产物的体积,包体积大略小 11%。
R8 则是变更了整个编译流程的,其中我感觉最奥妙的就是 java8 lambda 相干的,脱糖前后的差异还是比拟大的。同时 R8 也少了很多之前的 Transform。
R8 的混同局部,混同除了能减少代码浏览难度意外,更多的是对于代码优化方面的。比方有效代码优化,同时也删除代码等等都能够做。
编译的时候有没有碰到 javac 的常量优化
javac 会将动态常量间接优化成具体的数值。然而尤其是多模块场景下尤其容易出现异常,看起来是个理论的常量援用,然而产物上却是一个具体的常量值了。
其余局部
组件化相干
不仅仅要聊到路由,还须要聊下业务仓库的设计,如何防止两个模块之间互相互相援用导致的环问题。
另外就是路由的 apt aop 的局部都能够深刻的聊一下。
如果只聊路由的话,你就只说了一个字符串匹配规定,十分无聊了。
路由跳转
路由跳转只是一小部分,其外围原理就是字符串匹配,之后筛选出合乎逻辑的页面进行跳转。
另外就是拦截器的设计,同步异步拦截器两种齐全不同的写法。
其原理基于 apt+transform,apt 负责生成模块德 路由表,而 transform 则负责将各个模块的路由表进行收集。
服务发现
相似路由表,然而保护的是一个基于键值的类结构。ab 之间当有相互依赖的状况下,能够通过基于接口编程的形式进行调整,相互只依赖形象的接口,之后实现类在外部,通过注册的机制。之后在理论的应用中央用服务发现的机制寻找。
依赖注入
和服务发现相似,也是拿来解决不同模块间的依赖问题。能够应用 hilt,依赖注入的益处就是连结构的这部分工作也有 di 实现了,而且结构能力更多样。能够多参数结构。
虚拟机局部
很多人会感觉虚拟机这部分都是硬八股,比拟无聊。然而其实有时候咱们碰到的一些字节码相干的问题就和这部分根底姿态相干了。
尽管用的比拟少,然而也不是一个硬八股,比 hashmap 好玩太多了。
总结
其实以以后来说安卓的整个体系相对来说很简单,第三方库以及源代码量都比拟大,并不是要求每个同学都对这些有一个良好的把握,然而大体上应该理解的还是须要理解的。
面试造火箭可不是浪得虚名啊,然而鸡架可能还是须要应用到其中一些奇奇怪怪的黑科技的。
好了胡扯完结了,明天的文章就到此为止了。