面试问题答题总结

1、apk瘦身你做了哪些?

(1)先说下我们项目中做的:

apk瘦身我们主要分线下开发、build.gradle配置、apk包检测控制等方面来开展,同时在上线前会对比前后版本apk差异,如果某一块变化太大再具体分析;

线下开发及build.gradle配置的时候主要从资源、代码、so包来展开:

1)资源方面:去除无用资源、重复资源、资源压缩(如图片png, webp, jpg我们接入了McImage对图片进行了压缩,同时根据图片的格式,我们尽量用webp和png)、图片如果能从服务器取尽量使用动态加载、如果能只用一套图,尽量选择一套xxhdpi下

2)代码方面:移除无用代码、第三库方面(去掉多余的、功能相同的第三方库,若只依赖部分代码,则选择部分源码依赖)、测试代码资源不带到线上包

3)so包方面:只保留一套so包、删除无用的symbol符号,so包如果能动态下载优先动态下载

4)在build.gradle方面的处理:打开混淆,压缩、资源压缩、资源最少化配置(只打中文语言)

5)打包后,再用腾讯的APKChecker来验证打打包出来后继续校验是否有重复资源、无用资源、未压缩的图片、不带透明度的png图片、大图检测等进行优化;

(2)自己了解到但未在工程中实践的:

facebook出的redex可以进行方法内联、删除无用代码、去除debug信息与行号信息、只有一个实现类的接口或父类直接用实现类代替、去掉java access方法、字符串缩减、Interdex(调整 dex中类的顺序),对应用启动速度和apk包体积优化效果都比较明显,后续准备接入。

 

apk主要由资源、代码、So包等组成,我们应用主要从以下几方面进行优化:

1)首先是资源方面:包括移除无用资源、移除重复资源(可通过遍历资源包中CRC-32checksum筛选出重复资源,把重复资原都重定向到同一文件,再删除资源包中重复资源)

图片方面:选择合适的图片(矢量图优于webp,wep优于png,最后是jp)、图片压缩(可通过tinypng, pngcrush, pngquat, guetzli等框架对png, jpg进行压缩)、图片动态加载、

资源混淆、资源文件最少化配置(比如语言只支持中文件、图片只在xxhdpi中放一套)、资源合并等方案

2)再从代码方面来说:代码混淆、移除无用代码、第三方库处理(若只依赖部分可选择依赖部分源码、同样功能的三方库只保留一套)、去除Debug信息和行号信息(StripDebugInfo)、dex分包优化(减少跨Dex调用的情况,尽量将有调用关系的类和方法分配到同一个Dex中,因为跨Dex调用会导致当前dex保留被调用dex的方法id,可以通过Redex的InterDexPass配置项把相互引用的类尽量放到同一个Dex,增加类的pre-verify,以此提升应用冷启动速度)、dex压缩、

避免产生java access方法(一般是内部类引用外部类的私有方法或是私有成员时候用到)、使用ByteX Gradle插件进行代码优化(如内联常量字段、移除多余赋值代码、移除Log代码、内联Get/Set方法)

3)第三点so处理:只保留一套如armeabi,同时还可以结合so移除方案优化版(对一些敏感的so包都放到同一个aremabi文件夹下,在运行时根据手机当前cpu类型选择动态加载不同的so库)、对Native Library进行压缩、删除Native Library中无用的导出symbol、so包动态下载

armeabli目录下的so可以兼容别的平台上的so,但是这样别的平台使用时性能会有损耗,失去了对特定平台的优化,要想完美支持所有的类型设置代价太大,可以采取折中方案,对性能敏感的模块,它所使用到的so,都放在armeabi目录当中,随着APK发出去,在代码中判断一下当前设备所 属的CPU类型,根据不同的设备的CPU类型来加载对应架构的so文件。

4)其他方案:如插件化、h5等方式开

 

关于去除debugInfoItem,debugItems主要包含了两种信息

1)调试的信息:包含函数的参数和所有的局部变量;2)排查问题的信息:包含所有的指令集行号与源文件行号的对应关系;

每一个debugItemInfo里面都有一个指令集行号和源文件行号的映射关系,可以把多余的debugInfoItem全部删除

ReDex的CrossDexDefMinizer类分析了类之间调用关系,使用贪心算法计算局部的最优化解,使用InterDexPass配置项可以把一相引用的类尽量放到同一个Dex中,增加类的pre-verify,以此提升应用冷启动速度。

 

2、应用启动速度优化你做了哪些?

从Application到闪屏页再到首页,我们分别做了优化:

1)Application方面:我们缕清楚应用中的第三方库的初始化,根据不同的第三方库可以区分懒加载、延迟初始化或是异步初始化,我们使用了加载器模式来加载第三方库,初始化时区分主子进程;

2)闪屏页:采用了替换应用启动主题、类预加载、绘制优化方式减少闪屏页启动时间,子进程懒加载;

3)首页:采用了异步加载xml,布局优化、绘制优化、fragment懒加载、首屏数据缓存等策略;延迟初始化、延迟上报等策略;

4)同时自己还了解到在android5.0下,Multidex可以在子进程中加载、dex分包优化(让应用启动的类都分配到主dex下,增加类的预校验)、抑制GC(启动阶段不让系统发生GC)、提高CPU频率等;

webview预加载、MultiDex预加载、闪屏页和首页的绘制优化、启动阶段不启动子进程、子进程加载multidex、页面数据预加载等

 

3、网络优化你做了哪些?

第一点:从请求接口方面可以做的优化有:合并网络请求、增量更新、APM上报和埋点等数据可以批量定时或是定量再批量上报;文件上传压缩,服务器返回裁剪后的图片或是视频;

第二点:根据用户当前网络状况,如4g\wifi、其他网络选择播放不同质量的音频、视频,如wifi\4g情况下,可以选择高品质视频,而3g等其他网络状态播放压缩后的音视频;

第三点:根据发起网络请求做的优化:先是DNS解析,可以接入HTTPDNS,加快解析和防止域名劫持等;

再是TCP三次握手和TLS的证书验证等,可以采用连接池复用,包括SSL连接池、HTTP代理连接池、SpdySession连接池、SOCKS连接池、WebSocket连接池;

连接后发起网络请求,可以对head进行压缩,body压缩,返回的数据用pb代替json等;

第四点:针对弱网优化,采用QUIC协议或是HTTP3协议

第五点:数据缓存,去除重复请求

做了这些网络优化还需要网络监控,确定网络监控指标,主要包括质量监控和流量监控,质量监控包括DNS解析时间、建立连接时间、接口请求失败率等;

流量监控:前后台流量,body数据包大小等对比。

 

4、数据库优化?

数据库分库分表,比如IM相关的放到单独的数据库中

数据库批量插入可以采用事务、数据库的查询建立合适的索引,如何判断索引是否有效;查询的时候,不使用select *,而是根据需要的字段进行select

数据表大数据量查询优化,比如某个群消息表超过500万数据量,可以使用分表,也可以加快查询速度,可以先查top500,取出按排序规则的前50条,后面再预加载;

也可以更换数据库如Realm, GreenDao等

 

5、应用稳定性优化你做了哪些?

保证应用稳定性我们主要从三方面来优化:分别是Crash率治理、性能稳定性维护、容灾策略处理。

(1)针对Crash率:我们对于线下开发或是测出出现的Crash,根据堆栈,尽量做到发版前无Crash bug;

每日跟进线上发生的Crash,优先解决增量,持续跟进存量,对于常规Crash一般比较容易解决,系统级Crash尝试通过Hook,疑难的Crash,长期跟踪,团队合作重点突破。

(2)针对性能稳定性:

我们主要做了确保业务高可用、关键业务添加埋点上报;

针对项目的主流程、核心路径、关键节点做相关埋点统计,如登录、支付、送礼相关的业力,形成有始有终统计,最终统计登录成功率、支付成功率和送礼成功率,若发现异常,可通过上报查看具体哪个环节出问题了。

先通过kibana上报查看用户的页面路径、操作路径、http请求等查看,是否可以定位出问题,

如果需要,我们会对单点问题进行追查,会在后台打开用户日志开关,等用户操作一段时间,发送命令,将用户日志上传到服务器,根据用户的操作、当前应用状态及手机版本等进行定点分析

(3)针对容灾方案:

1)配置了功能开关(如某些新上线和功能,由服务器下发配置,如果出现异常情况,可以随时关闭入口使上线的新功能处于可控状态;或是改动代码,保留之前的老方案,如果出问题可切回到老方案);

2)安全模式:如App多次启动失败,弹出h5页面提示用户升级版本,或是点击重置app到初始安装应用的状态;

3)统跳中心:如果某些功能加载失败,会弹出一个友好的界面,提示用户当前出现的问题,但用户还是可以进行操作,而不是一直转圈或是直接崩溃;

 

6、应用卡顿优化你做了哪些?

卡顿优化我们分以下几方面来做的:

第一方面:开发或是测试过程中发现卡顿问题、找问题原因、解决问题;

第二方面:线上监控检测到卡顿、定位问题原因、解决问题;

第三方面:主功能定点优化,同时上线前测试主功能页面的FPS、内存、CPU,对比前后版本,确认是否有大的差异;

先说第一方面,如果在开发或是测试过程中发现卡顿问题,比如滑动人为感觉卡顿、或是出现ANR、或是自己接入的卡顿检测包检测到主线程中有消息处理时间过长的情况,会分别从以下三方面来考虑:

1)是不是UI引起的?2)是不是内存泄漏引起的;3)是不是方法耗时引起的?

针对UI问题,会确认是否有过度绘制、帖率检测、层级检测;针对内存问题,会用LeakCanary来检测;针对方法耗时会使用TraceView来确认调用次数多的方法和耗时长的方法;一步步定位优化;

对于ANR问题,会根据日志查看当前主线程中处理消息耗时长的原因,是有锁阻塞、还是在主线程中进行了IO操作或是大量计算操作;

同时在线下也会结合严格模式的日志来查看是否有耗时操作或是未关必的数据库或Closeable对象;

第二方面:针对线上监控上的ANR,我们在BlockCanary第三方库基础上,加入了自己应用的一些内容,如上报机型、用户所在页面、用户设备标识等,会在后台做聚类分析,看看哪些堆栈容易引发卡顿,再根据日志分配给相应开发解决;

第三方面:针对一些主功能界面,我们会跑自动化测试,测试界面的流畅度,丢帧率,帧速等,确保帧速在每秒50帧左右,如果小于这个值或是跟历史版本差异比较大,会进行一些优化;

如RecyclerView优化(item设置固定长宽、滑动时不加载图片、图片不可见时回收图片、缓存池大小设置大一些、预加载机制接入、局部刷新、DiffUtils只更新有变更的数据等)

如布局优化,使用AsyncInflator, X2C, Facebook Litho, ConstranintLayout, ViewStub, include, merge,避免过度绘制,避免层级太深;

线程池管理,避免开启太多线程,同时又加锁,导致主线程锁阻塞等;

 

7、应用内存优化你做了哪些?

针对内存优化:我们主要从以下几方面入手:

1)分析现状、确认问题;2)针对主功能界面的内存优化;3)监控

(1)查看是否有内存泄漏、内存抖动、内存占用过高的问题,有则通过Memory Profiler, LeakCanary, ResourceCanary进行分析

(2)主功能界面内存优化,运行一段主功能界面,dump出hprof进行分析,查看对象分配;

根据设备分级来使用不同的内存和分配回收策略;

针对低端机型做功能或图片加载降级处理;

大图监控和重复图片的监控;

在前台每隔一段时间去获取应用内存占最大内存比例,当超过阈值时主动释放应用的cache

当ui隐藏时释放内存以增加系统缓存应用进程的能力;

实现全局的线程监听;

针对内存使用的重度场景实现GC监控;

线下native内存泄漏监控;

(3)监控方面

1)如果图片尺寸比当前view尺寸大或是比屏幕尺寸大,发生预警;

2)发生内存泄漏打印出泄漏引用链的堆栈

3)监测

(4)编码中一些常规优化手段

1)图片处理:低端机型使用ARGB_565, ARGB_8888, Bitmap分配放到Native层,Bitmap内存复用,Bitmap缩放显示,图片的三级缓存;图片在onLowMemory时释放缓存;不可见时释放图片所占内存;图片放置优化(只在xxhdpi)下放一套图片;

2)优化内存空使用:避免装箱、内存复用(对象池、Bitmap对象复用、视频复用、资源复用)、使用最优的数据类型(用ArrayMa代替HashMap\ 避免使用枚举)、LruCache缓存对象

防止GC太过频敏;全局的线程组件监控、GC监控、内存使用监控、内存泄漏监控、大图监控,内存兜底策略(如果内存即将达上报应用切后台达一定时间让进用自杀重启)

8、APK打包流程

1)通过AAPT工具进行资源文件(包括AndroidMainfest.xml, 布局文件、各种xml资源等)的打包,生成R.java文件;

2)通过AIDL工具处理 AIDL文件,生成相应的Java文件;

3)通过Java compiler编译R.java, Java接口文件,Java源文件,生成.class文件;

4)通过dex文件,将.class文件和第三方库的.class文件生成处理生成classes.dex,该过程主要完成Java字节码转换成Dalvik字节码,压缩常量池以及清除冗余信息等工作;

5)通过ApkBuilder工具将资源文件, Dex文件打包生成APK文件

6)通过Jarsigner工具,利用KeyStore对生成APK文件进行签名;

7)如果是正式版的APK,还会利用ZipAlign工具进行对齐处理,对齐过程就是将APK文件中所有的资源文件

9、IO优化

微信Matrix IOCanary检测

IOCanary采用hook方案收集IO信息,找到android中FileInputStream,hook libjavacore.so的open符号

1)检测主线程IO,操作线程为主线程,连续读写耗时超过一定阈值或单次write/read耗时超过一定阈值

2)检测读写Buffer过小,会导致read/write次数增多,从而影响性能;

3)重复读,添加内存cache

4) Closeable Leak监控:指打开资源包括文件、Cursor等,没有及时close,引起泄漏

StrictMode主要依赖一个工具类dalvik.system.CloseGuard来实现,会在GC准备回收调用finalize时调用warnIfOpen,如果没有close,就调用REPORTER.report

10、有遇到什么棘手问题么?

管理方面的问题:我们的排期和最终的上线时间总是不匹配,上线时间总是会被delay

针对这个问题我从改变团队成员态度、团队成员时间观念几方面来做的:

(1)让大家树立owner意识,主要从两点出发:

第一点:第一是认真负责的态度,对交付的结果负责,项目中的设计文档、自己写的代码都要认真完成,对它的质量负责 。如果设计文档逻辑混乱、代码没有注释、测试时发现一堆Bug,影响的不仅仅是交付质量,还会对协同工作如与QA, PM等产生不好的影响。这样下去不仅工作效率会下降、团队成员之间也会产生不信任感。对于普通程序员来说,写出的代码提测前先自测,保证功能符合产品需求、性能没有退化,认真对待每一个 bug。

第二点:第二是积极主动的精神,对工作积极主动,而不是等靠要,比如测试没有反馈,我就等着;设计没有出图,我就等着;服务器接口没有完成,我就等着。这些都是被动消极的态度,应该让大家积极主动地推动问题的解决,如果时间无法排开或不知道如何解决,可将问题反馈给能解决的上司或是其他部门同事。

(2)时间观念和工作效率

这个主要从一个任务的todo ,  doing,  done三方面来优化

先是todo阶段:根据以往开发经验,排期尽量细化、合理,根据任务的复杂度、个人能力合理排期,计划尽量精准、细粒度;

doing阶段:每天早上站会,先对昨天的工作进行总结,再分配今天要完成的工作;晚上下班前,统计大家工作完成情况,是否有延期,对于延期的任务,明晰原因,是任务太复杂、估期不准还是由于其他部门不配合导致delay或是自己能力原因导致的delay,针对不同的原因,找出不同的对策。

done阶段:也即测试到上线这个阶段,测试阶段,关注每天的bug数量,随着测试的深入,bug是否收敛,根据bug的严重等级,确认哪些是要尽快解决,哪些可以延期解决,哪些可以和产品商量,不用解决或是不跟着上线等。

(3)闭环思维

要做到凡事有交代、件件有着落、事事有回音,上线完成后,及时总结。把项目中遇到的问题及时梳理、总结。上线后及时查看上报,如崩溃率、应用稳定性、应用性能等方面,如果出现重大bug, 及时出hotfix版本,case study进行复盘,对故障原因进行5W分析(what, where, when, why, how), 给出明可执行的To Do List。召开项目总结大会、季度总结大会,大家自我反省问题所在,如何改善。

(4)保持敬畏之心

工作中的各种规范,如代码规范、设计规范、上线规范等,团队里每个人都必须明白且遵守。对于拿不准的事情,多问问其他同事,不要凭自己的感觉做事。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值