最后
针对于上面的问题,我总结出了互联网公司Android程序员面试涉及到的绝大部分面试题及答案,并整理做成了文档,以及系统的进阶学习视频资料。
(包括Java在Android开发中应用、APP框架知识体系、高级UI、全方位性能调优,NDK开发,音视频技术,人工智能技术,跨平台技术等技术资料),希望能帮助到你面试前的复习,且找到一个好的工作,也节省大家在网上搜索资料的时间来学习。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
b、大量不可见的对象占据内存,这个其实,很常见,只是大家可能一直不太关心罢了,比如,请求接口返回了列表有100项数据,每项数据比如有100个字段,其中你用户展示数据的只有10几个而已,但是,你解析的时候,剩下的99个不知不觉吃了你的内存,当,有个胖子要内存时,呵呵,嗝屁了。
c、还有一种很常见的场景是一个页面多图的场景,明明每个图只需要加载一个100100的,你却使用原始尺寸(10801980)or更大,而且你一下子还加载个几十张,扛得住么?所以了解一下inSampleSize,或者,如果图片归你们上传管理,你可以借助万象优图,他为你做了剪切好不同尺寸的图片,这样省得你在客户端做图片缩放了。
二 以上了解了一些性能问题,这里,简单的串一串导致这些性能问题的原因
1、人为在ui线程中做了轻微的耗时操作,导致ui线程卡顿
嗯,很多小伙伴不以为然,以为在onCreate中读一下pref算什么,解析下json数据算得了什么,可实际情况是并不是这样的,正确的做法是,将这些操作使用异步封装起来,小伙伴可以了解一下rxjava,现在最新版本已经是rxjava2了,如果不清楚使用方式,可以Google一下。
2、layout过于复杂,无法在16ms完成渲染
这个很多小伙伴深有体会了,这里简单的了解下,我们先简单的把渲染大概分为"layout",“measure”"draw"这么几个阶段,当然你不要以为实际情况也是如此,好,层级复杂,layout,measure可能就用到了不该用的时间,自然而然,留给draw的时间就可能不够了,自然而然就悲剧了。那么以前给出的很多建议是,使用RelativeLayout替换LinearLayout,说是可以减少布局层次,然鹅,现在请不要在建议别人使用RelativeLayout,因为ConstraintLayout才是一个更高性能的消灭布局层级的神器。ConstraintLayout 基于Cassowary算法,而Cassowary算法的优势是在于解决线性方程时有极高的效率,事实证明,线性方程组是非常适合用于定义用户界面元素的参数。由于人们对图形的敏感度非常高,所以UI的渲染速度显得非常重要。因此在2016年,iOS和Android都基于Cassowary算法来研发了属于自己的布局系统,这里是ConstraintLayout与传统布局RelativeLayout,LinearLayout实现时的性能对比,不过这里是老外的测试数据,原文可以参考这里。demo中也提供了测试的方法,感兴趣的小伙伴可以尝试一下咯。
测量/布局(单位:毫秒,100 帧的平均值)
3、同一时间执行的动画过多,导致CPU或者GPU负载过重
这里主要是因为动画一般会频繁变更view的属性,导致displayList失效,而需要重新创建一个新的displayList,如果动画过多,这个开销可想而知,如果你想了解得更加详细,推荐看这篇咯,知识点在第5节那里。
4、view过度绘制的问题。
view过度绘制的问题可以说是我们在写布局的时候遇到的一个最常见的问题之一,可以说写着写着一不留神就写出了一个过度绘制,通常发生在一个嵌套的viewgroup中,比如你给他设置了一个不必要的背景。这方面问题的排查不太难,我们可以通过手机设置里面的开发者选项,打开Show GPU Overdraw的选项,轻松发现这些问题,然后尽量往蓝色靠近。
5、gc过多的问题,这里就不在赘述了,上面已经讲的非常直接了。
6、资源加载导致执行缓慢。
有些时候避免不要加载一些资源,这里有两种解决的办法,使用的场景也不相同。
a、预加载,即还没有来到路径之前,就提前加载好,诶,好像x5内核就是酱紫哦。
b、实在是要等到用到的时候加载,请给一个进度条,不要让用户干等着,也不知道什么时候结束而造成不好的用户体验。
7、工作线程优先级设置不对,导致和ui线程抢占cpu时间。
使用Rxjava的小伙伴要注意这点,设置任务的执行线程可能会对你的性能产生较大的影响,没有使用的小伙伴也不能太过大意。
8、静态变量。
嘿嘿,大家一定有过在application中设置静态变量的经历,遥想当年,为了越过Intent只能传递1M以下数据的坑,我在application中设置了一个静态变量,用于两个activity“传递(共享)数据”,然而,一步小心,数据中,有着前一个activity的尾巴,因此泄露了。不光是这样的例子,随便举几个:
a、你用静态集合保存过数据吧?
b、某某单例的Manger,比如管理AudioManger遇到过吧?
三 既然遇到问题分析也有了,那么接下来,自然而然是如何使用各种刀棒棍剑来解决这些问题了
1、GPU过度绘制,定位过度绘制区域
这里直接在开发者选项,打开Show GPU Overdraw,就可以看到效果,轻松发现哪块需要优化,那么具体如何去优化
a、减少布局层级,上面有提到过,使用ConstraintLayout替换传统的布局方式。如果你对ConstraintLayout不了解,没有关系,这篇文章教你15分钟了解如何使用ConstraintLayout。
b、检查是否有多余的背景色设置,我们通常会犯一些低级错误–对被覆盖的父view设置背景,多数情况下这些背景是没有必要的。
2、主线程耗时操作排查。
a、开启strictmode,这样一来,主线程的耗时操作都将以告警的形式呈现到logcat当中。
b、直接对怀疑的对象加@DebugLog,查看方法执行耗时。DebugLog注解需要引入插件hugo,这个是Android之神JakeWharton的早期作品,对于监控函数执行时间非常方便,直接在函数上加入注解就可以实现,但是有一个缺点,就是JakeWharton发布的最后一个版本没有支持release版本用空方法替代监控代码,因此,我这里发布了一个到公司的maven仓库,引用的方式和官网类似,只不过,地址是:com.tencent.tip:hugo-plugin:2.0.0-SNAPSHOT
。
3、对于measure,layout耗时过多的问题
一般这类问题是优于布局过于复杂的原因导致,现在因为有ConstraintLayout,所以,强烈建议使用ConstraintLayout减少布局层级,问题一般得以解决,如果发现还存在性能问题,可以使用traceView观察方法耗时,来定位下具体原因。
4、leakcany
这个是内存泄露监测的银弹,大家应该都使用过,需要提醒一下的是,要注意
dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4'
}
引入方式,releaseImplementation保证在发布包中移除监控代码,否则,他自生不停的catch内存快照,本身也影响性能。
5、onDraw里面写代码需要注意
onDraw优于大概每16ms都会被执行一次,因此本身就相当于一个forloop,如果你在里面new对象的话,不知不觉中就满足了短时间内大量对象创建并释放,于是频繁GC就发生了,嗯,内存抖动,于是,卡了。因此,正确的做法是将对象放在外面new出来。
6、json反序列化问题
json反序列化是指将json字符串转变为对象,这里如果数据量比较多,特别是有相当多的string的时候,解析起来不仅耗时,而且还很吃内存。解决的方式是:
a、精简字段,与后台协商,相关接口剔除不必要的字段。保证最小可用原则。
b、使用流解析,之前我考虑过json解析优化,在Stack Overflow上搜索到这个。于是了解到Gson.fromJson是可以这样玩的,可以提升25%的解析效率。
7、viewStub&merge的使用。
这里merge和viewStub想必是大家非常了解的两个布局组件了,对于只有在某些条件下才展示出来的组件,建议使用viewStub包裹起来,同样的道理,include 某布局如果其根布局和引入他的父布局一致,建议使用merge包裹起来,如果你担心preview效果问题,这里完全没有必要,因为你可以
tools:showIn=""属性,这样就可以正常展示preview了。
8、加载优化
这里并没有过多的技术点在里面,无非就是将耗时的操作封装到异步中去了,但是,有一点不得不提的是,要注意多进程的问题,如果你的应用是多进程,你应该认识到你的application的oncreate方法会被执行多次,你一定不希望资源加载多次吧,于是你只在主进程加载,如是有些坑就出现了,有可能其他进程需要那某份资源,然后他这个进程缺没有加载相应的资源,然后就嗝屁了。
9、刷新优化。
这点在我之前的文章中有提到过,这里举两个例子吧。
a、对于列表的中的item的操作,比如对item点赞,此时不应该让整个列表刷新,而是应该只刷新这个item,相比对于熟练使用recyclerView的你,应该明白如何操作了,不懂请看这里,你将会明白什么叫做recyclerView的局部刷新。
b、对于较为复杂的页面,个人建议不要写在一个activity中,建议使用几个fragment进行组装,这样一来,module的变更可以只刷新某一个具体的fragment,而不用整个页面都走刷新逻辑。但是问题来了,fragment之间如何共享数据呢?好,看我怎么操作。
Activity将数据这部分抽象成一个LiveData,交个LiveDataManger数据进行管理,然后各个Fragment通过Activity的这个context从LiveDataManger中拿到LiveData,进行操作,通知activity数据变更等等。哈哈,你没有看错,这个确实和Google的那个LiveData有点像,当然,如果你想使用Google的那个,也自然没问题,只不过,这个是简化版的。项目的引入
‘com.tencent.tip:simple_live_data:1.0.1-SNAPSHOT’
10、动画优化
这里主要是想说使用硬件加速来做优化,不过要注意,动画做完之后,关闭硬件加速,因为开启硬件加速本身就是一种消耗。下面有一幅图,第二幅对比第一幅是说开启硬件加速和没开启的时候做动画的效果对比,可以看到开启后的渲染速度明显快不少,开启硬件加速就一定万事大吉么?第三幅图实际上就说明,如果你的这个view不断的失效的话,也会出现性能问题,第三图中可以看到蓝色的部曲线图有了一定的起色,这说明,displaylist不断的失效并重现创建,如果你想了解的更加详细,可以查看这里
// Set the layer type to hardware
myView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
// Setup the animation
ObjectAnimator animator = ObjectAnimator.ofFloat(myView,View.TRANSLATION_X, 150);
// Add a listener that does cleanup
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
myView.setLayerType(View.LAYER_TYPE_NONE, null);
}
});
11耗电优化
这里仅仅只是建议;
文末
不管怎么样,不论是什么样的大小面试,要想不被面试官虐的不要不要的,只有刷爆面试题题做好全面的准备,当然除了这个还需要在平时把自己的基础打扎实,这样不论面试官怎么样一个知识点里往死里凿,你也能应付如流啊
小编将自己6年以来的面试经验和学习笔记都整理成了一个**937页的PDF,**以及我学习进阶过程中看过的一些优质视频教程。
其实看到身边很多朋友抱怨自己的工资很低,包括笔者也是一样的,其原因是在面试过程中没有给面试官一个很好的答案。所以笔者会持续更新面试过程中遇到的问题,也希望大家和笔者一起进步,一起学习。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
希望大家和笔者一起进步,一起学习。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!