2019一个移动端开发者的总结与思考

7,362 阅读14分钟

    平常都在写技术类文章,今天写篇作为一个移动端开发者对过去一年的总结和思考吧,既是对过去的回顾,也是对未来的思考。

2019

    这一年对于我来说,当然最多的时间还是花在工作上。对于移动端开发来说,2019年依然是动荡的一年,各种跨平台技术依然层出不穷,原生将死的论调依然在各大公众号热炒和营销。这一年,随着工作经验的提升,自己对技术的看法和对未来的方向也发生了些许改变,分享一下自己这一年的总结与反思吧。

一. 这一年都做了什么?

这一年还是挺忙碌的,不管是纯业务开发还是技术改造或者是对我们产线现有问题或者痛点的解决方案都有一些个人的输出,技术确实不能是半空建楼阁,依托具体的业务问题,以技术方案去解决,是更加良好的循环和沉淀,还有很多东西值得去提高去学习。

1、上半年产线中的几个复杂页面的流畅度在版本迭代中一直会由于开发的不注意导致频繁出现反复,分析下来根本原因是由于缺乏必要的监控以及可持续的优化手段,所以2019Q1在业务需求之外主要就是实现了产线内部对于用户流畅度的监控以及优化,主要包括:debug下帧率的监控、上线前主流程流畅度的自动化测试、线上用户真实流畅度监控、实现耗时方法排查工具MethodTraceMan一些收益比较大的流畅度优化等,主要目标是从监控到排查问题工具再到卡顿解决形成一个闭环的方案。

对于流畅度监控与优化归纳成文,有兴趣的可见:App流畅度优化:利用字节码插桩实现一个快速排查高耗时方法的工具

2.对于内存上的问题,我们产线其实很早之前就碰到了,线上爆出可观数量的OOM,当时我对我们app做了一次内存上的分析与优化,效果还是非常明显的,当时对分析和优化过程做了记录:实践App内存优化:如何有序地做内存分析与优化,后续的几个版本中我们发现OOM的数量大幅度下降,但是依然会有少数的OOM上报,分析下来发现这部分发生OOM的机型大部分单应用最大可用的内存只有64M,也就是说有部分很老旧的机型内存实在小,所以我们最后上线了内存适配方案,也就是对不同内存大小的机型做不同的内存使用方案,比如对于一些老旧内存非常小的机型,选择适当去降低图片的质量,关闭一些花哨的动画,对于一些大内存的使用更加谨慎,更加及时的去做一些内存的回收等等,收到了不错的效果。后面对于内存方面的优化一直没有补充到上面的那篇文章里,等有空了,补上后续的一些内存优化手段,供交流分享。

3.Google近年来对Jetpack进行不断的补充和完善,我也对Jetpack进行一定的学习和引进,特别是lifecycle组件等已经在我们产线中使用很久了,我们使用响应式的编程方式来组织或者重构问题代码,解决了一些我们产线现有的问题,提高了效率。还是那句话,不应该以新或者旧来看待技术,而应该以他是否能提高你的效率以及解决你的问题的维度来看待他。

对于响应式编程以及lifecycle组件的一些总结与看法: 响应式编程在Android 中的一些探索

4.这一年也对团队在代码规范和约束方面做了一些努力,在团队开发过程中这方面其实非常重要,如何保证代码质量以及稳定性直接影响了上线app质量,所以我们团队在挺早之前就建立了警告和报错的责任制,即上线前每个人要清掉属于自己的警告和报错,当然主要还是靠Lint。但是有一些Lint无法触及的团队内部的一些约定,这确实在版本迭代中由于开发的疏忽在线上引起几次问题,基于这个问题,主要的目标还是希望可以在开发阶段就能在编译器中对于团队内部约定的规则进行报错或者警告提示,于是我发起并实现了一个自定义Lint规则的项目,主要是对Lint的拓展,基于AST树,去实现一些探测器。比如可以配置禁止使用哪些类,哪些方法,同时提示解决方案,团队成员也不断的拓展和自定义规则,现在这些我们团队内部自定义的规则已经和Lint警告一起作为我们上线前需要清理的问题。

对于如何实现自定义Lint规则,其实网上有很多资料,有兴趣的同学可以看看

二. 技术深度 or 技术广度

    一年是漫长的也是短暂的,这一年团队全面切到Kotlin开发,自己个人也关注学习了ReactNative和Flutter,同时对JS以及React等前端技术做了一些学习,技术上个人想法是争取往所谓的T型技术人上去努力吧,又是那个老生常谈的问题,技术是重深度还是广度,对于这个问题,这一年也有了更深的看法,技术就像是一棵树,在顶部叶子上各个领域看似毫不相干,但是在一个领域越往下深入,各个领域相互交错到的知识或者设计方式就越多,所以技术深度和广度并不是对立面,对技术深度的探索不仅有利于你在特定领域有更深理解,更加可以帮助你轻松切换到另一个领域,特别是像前端的各细分领域的工作,很多领域的知识背后都殊途同归,而技术的广度也不是有的人说的那样不堪,在有技术深度的基础上,去拓展自己的技术广度,其实大大有利于你对相关技术更深更饱满的理解。

    在技术广度方面,举几个小例子,比如做Android一直用的是java的时候你可能很能想象会有像Kotlin中拓展函数这种看起来像可以给类动态添加方法的功能,但是如果你对JS有一些的了解的话,利用原型其实JS一直就可以很方便的实现这种动态添加方法属性的功能,当然与Kotlin背后实现的原理完全不一样。再比如Android开发中我们实现异步的方式从回调方法到RxJava这种解耦的链式调用再到Kotlin协程这种看起来同步的代码效果,是不是和前端领域实现异步从回调方法到Promise再到aysnc await的历程挺像的。不断的对比和思考,更能够碰撞出不一样的火花,比如前端领域大火的响应式UI以及状态管理模式Redux,好在哪里,如果移植到原生开发会是什么样的形式,会有什么样的好处和效果,又有哪些差异和限制?学一样新东西时,或多或少慢慢的就会看到原来你已经掌握了的技术的影子,技术的广度可以帮助你对技术有更深的思考和理解。

    而技术的深度有利于你更轻易的切换到新的领域,对于我个人而言,这种例子有很多,比如在学习Binder的时候你会发现跨进程通信的本质就是在用户态的进程间无法共享内存实现通信,而在内核态却可以,也就是说在用户态每个进程就像是一个孤岛,联想到几年前团队在做app组件化的时候,解耦完后每个产线组件也像一个孤岛一样互不依赖,那最终是怎么实现通信的呢?我们把每个组件先在一个数据链表里注册,之后要通信,由router中间人先在数据链表找到对应的目标组件,然后调用目标组件里的方法实现通信。那么binder是怎么实现的呢?其实我觉得很相似的,只不过binder将这个所谓的数据链表维护在ServiceManager,当然binder要复杂的多,因为Client、Service、ServiceManager三者处在不同进程,所以两两之间每次通信都要经过将通信数据拷贝入内核态的biner驱动,在binder驱动的数据结构里找到目标进程的对应节点,然后将通信数据放入目标进程节点,之后唤醒目标进程,开始解析并处理,最后将结果返回给Client进程。这其中还涉及到一个点,每次一个进程将通信数据拷贝入内核态后,自己就会开启一个while死循环等待回复,那么这个和handle消息队列里的死循环设计是否一样呢,是否也是在没有消息的时候利用epoll实现阻塞呢?在对一个知识进行比较深入的学习时总是会时不时冒出其他知识的关联和疑问,也有了更多的思考。

    当然以上都是我的个人看法,并不一定准确,都是基于个人的认知的基础上有感而发的,其中有认知上不对或者不认同的地方还望海涵。

三.解决方案 or 技术难点

    这一年,对于技术的看法也发生了一点点改变,有时候做研发做技术会陷入技术怪圈,过分的追求或者陷在技术的难度上,而忽略有时候直接有效的解决方案才是最有价值的,解决方案是最终的目的,而技术只是工具。学会不仅仅是钻在开发角度看问题,跳脱出来,站在用户角度,站在产品角度,或许会有意外的收获,这一点我也在不断努力中

    举几个小例子吧,比如早些时候在做一个主流程复杂页面的TTI(用户可交互)时间优化时,显然目的就是降低用户进入一个重要页面的等待时间,用户等待的时间越少相对来说转化率就越高。所以从技术角度可能马上就开始想:怎么提高Activity启动速度?怎么减少网络耗时?怎么优化UI渲染速度?这些做完还不够,还有没有黑科技?但是如果你跳脱出来,站在用户的角度,他只要我进来能最快的看到内容就行,他不关心你是什么高深技术优化,所以最终其实最有效的方案是在上一个页面加一个预加载,可以实现大部分用户进入下一页面可以直接看到内容,大大降低TTI时间(当然并不是所有页面都适合预加载,要自己平衡和评估,这里只是举个例子)。做个预加载,基本没有技术上的难度,但是对用户的实际体验帮助很大。当然技术上的难度是你需要具备的素质,这些知识到了优化瓶颈的时候都很有用,但是在另一维度也要明白解决方案才是根本,站在更广的地方看问题才能收获更多。

    另一个小例子,相信很多团队都受困于线上的空指针异常问题,这些都是实实在在的崩溃,对你的用户可能造成很差的影响,很实在的损失。所以如果去规避屡禁不止的空指针异常呢?当然Kotlin空安全另说了,如果是java开发,各位一般都是怎么来规避这类问题的?我们团队从早些时候开始,建立了一个服务专门每天跑项目的Lint检查,跑完将警告汇总分配到对应的负责人身上,并邮件告知他,直到上线。几个版本下来,整个团队都养成了上线前清空警告的习惯,空指针异常基本没有再发生。这个方案基本和技术难度没有关系,看起来很简单的一个事情,但是实践下来很切实的解决了问题。

    这是今年我对技术的一些思考吧,仁者见仁,智者见智,主要就是想说希望深入学习技术的同时有时候也要尝试跳脱出来,站在更多的维度上看问题。

四.原生开发 or 跨平台技术

    这一年跟着跨平台技术的浪潮,自己也关注学习了ReactNative和Flutter,当然都不是All in状态,感受一下这个东西,明白他们的原理和运作方式,写一些个人小app感受一下。 这些跨平台技术的层出不穷让市面上原生将死的论调隔几天就会营销一波,但是其实如果你仔细想想或者稍微了解一下原理的话,就算是跨平台做的比较彻底的Flutter(完全自己实现一套闭环的底层UI渲染)最多也只是接管了各平台的View体系,而原生开发需要的知识不仅仅是UI开发,站在Android系统的角度,我们需要学习的东西太多了,而UI部分只是一部分,Flutter View也是放入在Activity里去运作,很多原生开发的知识并不会过时。当然不可否认,有一大部分的需求可能仅仅需要UI开发,所以我觉得原生开发不会死,但是原生UI开发会面临挑战。所以学习更加深入的知识,不停留在浅显的UI开发,会是一种选择。

2020

    对于2020年,自己的很多目标和想法其实在上面的思考与反思中也有提及,比如在技术深度和广度上继续努力,争取成为T型技术人,还有就是希望自己能在关注技术之余,学会站在更多维度上去思考解决问题,特别是一些产品思维。

    当然去年在Github上开源了我用来排查耗时方法进行流畅度优化的工具:MethodTraceMan ,获得了一些star支持,很感谢。做技术有时候是无趣的,所以要多给自己找点乐趣,希望2020年有好的想法还是可以多在Github上分享。

    另外说了这么多技术学习相关的,庄子说吾生也有涯,而知无涯。以有涯随无涯,殆已。新的一年在工作学习之余还是多锻炼身体,多放松心情,多享受生活。