前言
年中的时候帮部门招人,发现很多候选人对于咱们部门还是很青眼的。也对鸡架部门做的事比拟感兴趣,所以明天这篇水文次要就给大家梳理下基架的面试题以及基础架构组波及的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 = 0Lprivate 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好玩太多了。
总结
其实以以后来说安卓的整个体系相对来说很简单,第三方库以及源代码量都比拟大,并不是要求每个同学都对这些有一个良好的把握,然而大体上应该理解的还是须要理解的。
面试造火箭可不是浪得虚名啊,然而鸡架可能还是须要应用到其中一些奇奇怪怪的黑科技的。
好了胡扯完结了,明天的文章就到此为止了。