公司的新需求终于解决完了,离测试和发布还有段时间,第一次体验了下没需求没bug的感受,真是舒爽~而后翻了翻有什么能够学的。无心翻到了Android后期发展的五大趋势。1、性能优化。2、高级UI。3、JNI/NDK开发。4、架构师。5、RN开发。这也许将会是个人进阶趋势。早已知道在瓶颈期的我,彷佛看到了突破的但愿的。html
其实,关注个人或者在群里的小伙伴也知道,UI那块我问题不大。可是高级UI就有难度了。咱们先无论他,一个一个来。先从性能优化来。其实我是拒绝写这篇文章的。为何?性能优化的分类不少,一个分类写一篇感受篇幅量很小,结合在一块儿写有感受很大。而我目前打算总体的整理一下。java
那么咱们先分析下性能优化有那几个方面:1、内存优化。2、UI优化(布局优化和绘制优化)。3、速度的优化(线程优化/网络优化)。4、电量优化。5、启动优化。应该就这些了。那么这只是五大方面,里面还结合了各类细节方面的。不急,咱们下面一个个的介绍。android
关于性能优化咱们能够不知道其余的,但必定要知道内存优化。由于内存泄漏能够Android的常客。那么什么是内存泄漏呢?内存不在GC的掌控范围以内了。那么java的GC内存回收机制是什么?某对象不在有任何引用的时候才会进行回收。那么GC回收机制的原理是什么?又或者说能够做为GC Root引用点的是啥?或许有人听不懂我在讲啥。咱们先来看张图。面试
当咱们向上寻找,一直寻找到GC Root的时候,此对象不会进行回收,例如,一个Activity。那么若是咱们向上寻找,直到找到GC Root对象的时候,就说明它是不能够回收的,例如,我定义了一个int a;可是这个数据,我整个页面或者说整个项目都没有用到,则这个对象会被GC掉。shell
java栈中引用的对象缓存
方法静态引用的对象性能优化
方法常量引用的对象bash
Native中JNI引用的对象微信
Thread——“活着的”线程网络
那么咱们如何判断一个对象是一个垃圾对象,能够讲他进行回收呢?举了小例子教大家如何区分:
通常在学校吃饭,咱们有两种状况,第一:吃完饭就直接走人,碗筷留给阿姨来收拾处理。
第二:吃完以后把碗筷放到收盘处直接进行回收。
但咱们是个有素质的人,通常采用第二种状况,但根据想法,咱们更倾向于第一种。
那么通常在饭店或者KFC中,都是第一种状况。
那么此时,问题来了,若是我已经吃完饭,而后我并无离开饭店,作在位置上和朋友吹吹牛逼,谈谈理想,聊聊人生。
那么桌上那一堆碗筷是收仍是不收?讲道理是不能收的。虽然实际也是不能收的。由于顾客是上帝~~~复制代码
So,咱们如何判断一个对象是一个可回收的垃圾对象呢?这是咱们的一个主观的判断。可是有种状况咱们是必需要考虑到的,没错,就是内存过多没法释放的时候,会直接致使OOM。整个项目boom炸了。什么鬼?outofmemory。没错就是它。
咱们须要分析内存溢出的缘由,咱们先来看一张图:
内存泄漏通常致使应用卡顿,极端状况会致使项目boom。Boom的缘由是由于超过内存的阈值。缘由主要有两方面:
代码存在泄漏,内存没法及时释放致使oom(这个咱们后面说)
一些逻辑消耗了大量内存,没法及时释放或者超过致使oom
所谓消耗大量的内存的,绝大多数是由于图片加载。这是咱们oom出现最频繁的地方。我前面有写过图片加载的方法,一个是控制每次加载的数量,第二,保证每次滑动的时候不进行加载,滑动完进行加载。通常状况使用先进后出,而不是先进先出。不过通常咱们图片加载都是使用fresco或者Glide等开源库。咱们来看下下面两张图:
对比两张图,咱们能够在第一张的状况出现了oom状况,咱们经过log打印发现,处理的好像没什么问题,换句话说,若是我不放那0.8M的图片。而后继续不停的操做一样会出现OOM,然而咱们就蒙了。没什么图片加载怎么就这么崩掉了。
首先,咱们肯定咱们项目或者某几个类里面是否存在内存溢出的问题。咱们能够经过以下方法:
Android-->System Information-->MemoryUsage查看Object里面是否有没有被释放的Views和Activity
命令行模式:adb shell dumpsys meminfo 包名 -d
就那我公司的项目举例把。首先,咱们在这边能够看到memory。CPU和net的使用状况。咱们找到Object。看看咱们内存的消耗状况。
随便这么一看,尼玛蛋,1300左右的view和一个Activity。还有3个context。可怕。。能够理解为一个Activity里面使用了将近1300个view。。。想都不敢想。。。
咱们能够经过看Memory Monitor工具。 检查一个一个的动做。(好比Activity的跳转)。反复屡次执行某一个操做,不断的经过这个工具查看内存的大概变化状况。 先后两个内存变化增长了很多。
咱们能够更仔细的查找泄漏的位置,在AS里面使用 Heap SnapShot工具(堆栈快照)。如图所示:咱们点击后,他会进行一段时间的监控,而后会生成一个文件。咱们点击咱们package tree view。
咱们找到本身项目的包名。而后进行进一步的分析。首先看一下2个列表的列名到底指的什么。
实例化对象的详细信息:
咱们来随便的看一下内存中的数量:
这仍是咱们刚进手机,一个bean就被调用了这么屡次。简直可怕。这个咱们能够经过内存分析工具解决的。
性能优化工具:
Heap SnapShot工具
Heap Viewer工具
LeakCanary工具
MAT工具
TraceView工具(Device Monitor)
第三方分析工具:
MemoryAnalyzer
GT Home
iTest
由于我没有这些工具,没法进行演示。
咱们尽可能不要使用Activity的上下文,而是使用application的上下文,由于application的生命周期长,进程退出时才会被销毁。因此,单例模式是最容易形成内存溢出的本来所在,由于单例模式的生命周期的应该和application的生命周期同样长,而不是和Activity的相同。
Animation也会致使内存溢出,为何?由于咱们是经过view来进行演示的,致使view被Activity持有,而Activity又持有view。最后由于Activity没法释放,致使内存泄漏。解决方法是在Activity的ondestory()方法中调用Animation.cancle()进行中止,固然一些简单的动画咱们能够经过自定义view来解决。至少我如今已经不多使用Animation了。没有一个动画是自定义view解决不了的。如何有,那就是两个~~~。
UI优化主要包括布局优化以及view的绘制优化。不急,咱们接下来一个一个慢慢看~~。先说下UI的优化究竟是什么?有些时候咱们打开某个软件,会出现卡顿的状况。这就是UI的问题。那么咱们想一下,什么状况会致使卡顿呢?通常是以下几种状况:
人为在UI线程中作轻微耗时操做,致使UI线程卡顿;
布局Layout过于复杂,没法在16ms内完成渲染;
同一时间动画执行的次数过多,致使CPU或GPU负载太重;
View过分绘制,致使某些像素在同一帧时间内被绘制屡次,从而使CPU或GPU负载太重;
View频繁的触发measure、layout,致使measure、layout累计耗时过多及整个View频繁的从新渲染;
内存频繁触发GC过多(同一帧中频繁建立内存),致使暂时阻塞渲染操做;
冗余资源及逻辑等致使加载和执行缓慢;
臭名昭著的ANR;
能够看见,上面这些致使卡顿的缘由都是咱们平时开发中很是常见的。有些人可能会以为本身的应用用着还蛮OK的,其实那是由于你没进行一些瞬时测试和压力测试,一旦在这种环境下运行你的App你就会发现不少性能问题。
咱们对于UI性能的优化还能够经过开发者选项中的GPU过分绘制工具来进行分析。在设置->开发者选项->调试GPU过分绘制(不一样设备可能位置或者叫法不一样)中打开调试后能够看见以下图(对settings当前界面过分绘制进行分析):
这图看着太乱,咱们来一张简洁明了的图:
咱们的目标就是尽可能减小红色Overdraw,看到更多的蓝色区域。
能够发现,开启后在咱们想要调试的应用界面中能够看到各类颜色的区域,具体含义以下:
Overdraw有时候是由于你的UI布局存在大量重叠的部分,还有的时候是由于非必须的重叠背景。例如某个Activity有一个背景,而后里面的Layout又有本身的背景,同时子View又分别有本身的背景。仅仅是经过移除非必须的背景图片,这就可以减小大量的红色Overdraw区域,增长蓝色区域的占比。这一措施可以显著提高程序性能。
若是布局中既能采用RealtiveLayout和LinearLayout,那么直接使用LinearLayout,由于Relativelayout的布局比较复杂,绘制的时候须要花费更多的CPU时间。若是须要多个LinearLayout或者Framelayout嵌套,那么可采用Relativelayout。由于多层嵌套致使布局的绘制有大部分是重复的,这会减小程序的性能。
咱们依旧打开设置-->开发者选项-->GPU呈现模式分析-->在屏幕上显示为条形图,如图所示:
固然,也能够在执行完UI滑动操做后在命令行输入以下命令查看命令行打印的GPU渲染数据(分析依据:Draw + Process + Execute = 完整的显示一帧时间 < 16ms):
adb shell dumpsys gfxinfo [应用包名]复制代码
随着界面的刷新,界面上会以实时柱状图来显示每帧的渲染时间,柱状图越高表示渲染时间越长,每一个柱状图偏上都有一根表明16ms基准的绿色横线,每一条竖着的柱状线都包含三部分(蓝色表明测量绘制Display List的时间,红色表明OpenGL渲染Display List所须要的时间,黄色表明CPU等待GPU处理的时间),只要咱们每一帧的总时间低于基准线就不会发生UI卡顿问题(个别超出基准线其实也不算啥问题的)。就简单的看下咱们公司项目刚启动的时候:
忽然就有那么一种想吐槽的感受.....我记得以前我作了瘦身的优化,可是要让我作性能优化,我以为应该没那么简单........
Android Studio和IntellJ idead都有自带的代码检查工具。打开Analyze->Run Inspection by Name… –>unused resource 点击开始检测,等待一下后会发现以下结果:
咱们还能够这样,将鼠标放在代码区点击右键->Analyze->Inspect Code–>界面选择你要检测的模块->点击确认开始检测,等待一下后会发现以下结果:
固然,我这只是截取了少一部分,咱们看下下面那个提示:@param v tag description is missing 。意味着v的类型缺乏了,要么补上介绍,要么直接删除。
上面那两种方法是最容易找到代码缺陷以及无用代码的地方。因此尽情的入坑去填坑把~~~
那么什么是绘制优化?绘制优化主要是指View的Ondraw方法须要避免执行大量的操做。我将分为了2个方面。
ondraw方法不须要建立新的局部对象,这是由于ondraw方法是实时执行的,这样会产品大量的临时对象,致使占用了更多内存,而且使系统不断的GC。下降了执行效率。
Ondraw方法不须要执行耗时操做,在ondraw方法里少使用循环,由于循环会占用CPU的时间。致使绘制不流畅,卡顿等等。Google官方指出,view的绘制帧率稳定在60dps,这要求每帧的绘制时间不超过16ms(1000/60)。虽然很难保证,但咱们须要尽量的下降。
60dps是目前最合适的图像显示速度,也是绝大部分Android设备设置的调试频率,若是在16ms内顺利完成界面刷新操做能够展现出流畅的画面,而因为任何缘由致使接收到VSYNC信号的时候没法完成本次刷新操做,就会产生掉帧的现象,刷新帧率天然也就跟着降低(假定刷新帧率由正常的60fps降到30fps,用户就会明显感知到卡顿)。So,前面咱们说GPU的时候也谈到了这个。总的而言,感受仍是蛮重要的.....
线程是咱们项目中不可缺乏的重要部分,由于咱们大多数数据都是从网络获取的。So,线程这个是必备用品。咱们依旧能够经过Memory下面的Net进行网络的监听:
相信这个问题在座的各类没少遇到过,那么什么是ANR?application not responding。应用程序无响应。那么通常何时会出现ANR。Android官方规定:activity若是5s内无响应事件(屏幕触摸事件或者键盘输入事件)。BroadcastReceiver若是在10s内没法处理完成。Service若是20s内没法处理完成。这三种状况会致使ANR。用张简洁的图来介绍把。看起来方便~~
上面说的三种致使ANR的状况,绝大多数就是由于线程阻塞致使的。那么咱们应该如何处理呢?Android系统为咱们提供了若干组工具类来解决此问题。
Asynctask:为UI线程与工做线程之间进行快速处理的切换提供一种简单便捷的机制。适用于当下当即须要启动,可是异步执行的生命周期短暂的场景。
HandlerThread:为某些回调方法或者等待某些执行任务的执行设置一个专属的线程,并提供线程任务的调度机制。
ThreadPool:把任务分解成不一样的单元,分发到各个不一样的线程上,进行同时并发处理。
IntentService:适合执行由Ui触发的后台任务。并能够把这些任务执行的状况经过必定的机制反馈给UI。
网络请求耗时会给用户带来卡顿的产品体验,虽然可使用Loading提高用户体验,但属于治标不治本。例如,当网络差的时候咱们公司的项目一个loading就是10多s。甚至更多.....我就记得我当时面试以前下了一次咱们公司的项目,由于网差的问题...一个loading一分多钟。。当时砸手机的冲动都有了,别说卸软件了....
通常多线程的状况咱们能够经过Asynctask处理。(这玩意我真没怎么用过- -)我前面有说过annotation。这是google官方推出的注解。比bufferknife强大不少。这个能够快捷方便的处理多线程并且不会致使线程阻塞,并且你也能够控制线程的顺序,例如我要执行完线程A后,根据线程A的某个参数来执行线程B。以此类推.....
至于线程池么,最多的仍是要说道图片加载了~~。图片加载用三方就好了~想看详细介绍,我前面有说,固然除了这个还有下载操做。这就和IntentService有关联了。通常下载我不多涉及到。。用过几回android原生的downloadmanager。。感受略坑。
如今讲网络优化的重点了...重点..重点...,通常用到网最最最主要的是什么?时间!!速度!!成功率!!,时间!!速度!!成功率!!,时间!!速度!!成功率!!重要的事说三遍哈。
这已经不是第一次在此文提到图片了。可见图片的重要性!!
使用WebP格式;一样的照片,采用WebP格式可大幅节省流量,相对于JPG格式的图片,流量能节省将近 25% 到 35 %;相对于 PNG 格式的图片,流量能够节省将近80%。最重要的是使用WebP以后图片质量也没有改变。So,去和后台的小伙伴们商量吧~~~
使用缩略图,我在前面写图片加载有说过,就是控制他的inside和option。而后进行图片缩放。压缩?讲道理....我并不知道网络图片怎么压缩,but,我会缩放啊~~反正也不会失真。啦啦啦~咬我啊?
咱们能够对服务端返回数据进行缓存,设定有效时间,有效时间以内不走网络请求,减小流量消耗。对网络的缓存能够参见HttpResponseCache。
在某些状况,咱们尽可能少使用GPS定位,若是条件容许,尽量使用网络定位。
下载、上传,咱们尽量使用断点,说个简单的,我在公司,准备下一个500M的游戏,可是下到200M的时候我下班了,此时没有了无线网,咱们能够回家后用无线继续下载。So,断点续传,断点下载也是咱们的必修课~,因此我前面单独提了一篇断点续传的文章。
刷新数据时,尽量使用局部刷新,而不是全局刷新,第1、界面会闪屏一下,网差的界面直接白屏一段时间也不是不可能。第2、流量的使用!!我又要拿咱们公司项目搞事情了。一个闪屏的缓存60+M。。。没错,就是60+M。简直可怕,我清个三、5次缓存,在打开个三、5次。好了,2分钟时间,我一个月流量就没了。。。So,我前面提到的网络缓存很重要,至于会不会加在项目中,我仍是要看了在说- - 一个不当心,整个项目炸了都有可能。。。
众所周知,一个好的产品,除了功能强大,好的性能也必不可少。有调查显示,近50%的受访者由于apk太大而拒绝使用,近40%的受访者会由于APP性能差而卸载,性能也是形成APP用户沮丧的头号缘由。
安卓应用的启动方式分为三种:冷启动、暖启动、热启动,不一样的启动方式决定了应用UI对用户可见所须要花费的时间长短。顾名思义,冷启动消耗的时间最长。基于冷启动方式的优化工做也是最考验产品用户体验的地方。谈及优化以前,咱们先看看这三种启动方式的应用场景,以及启动过程当中系统都作了些什么工做。
为何说冷启动是耗时最长的。冷启动是在启动应用前,系统没有获取到当前app的activity、Service等等。例如,第一次启动app。又或者说杀死进程后第一次启动。那么对比其余两种方式。冷启动天然是耗时最久的。
应用发生冷启动时,系统必定会执行下面的三个任务:
开始加载并启动应用
应用启动后,显示一个空白的启动窗口(启动闪屏页)
建立应用信息
那么建立应用信息,系统就须要作一屁股的事:
application的初始化
启动UI线程
建立Activity
导入视图(inflate view)
计算视图大小(onmesure view)
获得视图排版(onlayout view)
绘制视图(ondraw view)
这其中有两个 creation 工做,分别为 Application 和 Activity creation。他们均在 View 绘制展现以前。因此,在应用自定义的 Application 类和 第一个 Activity 类中,onCreate() 方法作的事情越多,冷启动消耗的时间越长。
当应用中的 Activities 被销毁,但在内存中常驻时,应用的启动方式就会变为暖启动。相比冷启动,暖启动过程减小了对象初始化、布局加载等工做,启动时间更短。但启动时,系统依然会展现闪屏页,直到第一个 Activity 的内容呈现为止。
相比暖启动,热启动时应用作的工做更少,启动时间更短。热启动产生的场景不少,常见如:用户使用返回键退出应用,而后立刻又从新启动应用。
咱们先对比下三种启动的时间对比:冷启动:
暖启动 :
热启动:
咱们能够看到三者的明显的差距,一个冷启动将近一分钟,反正我是不想看,每次跑项目都好慢~那么咱们应该怎么作?看到有些人介绍说改变项目的theme。把它改为launcher的theme。但我以为,这种作测试的确没问题。可是通常项目都会有闪屏页。而后从闪屏跳转到首页。咱们能够按照大多数的项目来改善。怎么说的,咱们能够看到通常项目都有倒计时显示。也就是说倒计时结束就自动进入首页。或者能够直接跳过进入首页。也就是说咱们能够经过此方法来进行,也就是说只要他倒计时结束,无论请求是否所有获取完咱们都直接进入首页。咱们能够在闪屏页进行一些必要的加载,例如用户信息,定位等等,那么至于其余的,咱们能够进入主页进行预加载。就和热更新同样,在用户不知情的状况下,默默的更新bug。So,对于一些网络请求,例如广告之类的。咱们能够经过此方法进行预加载。
咱们还能够这样,闪屏页咱们把他看成一个fragment嵌套在MainActivity中,那么咱们能够在进入闪屏时直接预加载主页的view。倒计时咱们把闪屏页remove掉直接显示首页。
经过上面的介绍,咱们对启动优化有了必定的了解,其实总结的话很简单。就是减小耗时操做,总结以下:
主线程中涉及到Shareperference可否在非UI线程执行。
Application的建立过程当中尽可能少的进行耗时操做。
减小布局的层次,而且生命周期回调的方法中尽可能减小耗时的操做。
有了UI优化、内存优化、代码优化、网络优化以后咱们在来讲说应用开发中很重要的一个优化模块—–电量优化。
其实大多数开发者对电量优化的重视程度极低,其实提到性能优化想到的就是内存优化,但咱们不能忽视其余的优化,电量优化其实仍是必要的,例如爱奇艺、优酷等等的视频播放器以及音乐播放器。众所周知,音乐和视频实际上是耗电量最大的。若是用户一旦发现咱们的应用很是耗电,很差意思,他们大多会选择卸载来解决此类问题。为此,咱们须要进行优化。
其实咱们把上面那四种优化解决了,就是最好的电量优化。So,对于电量优化,我在此提一些建议:
须要进行网络请求时,咱们需先判断网络当前的状态。
在多网络请求的状况下,最好进行批量处理,尽可能避免频繁的间隔网络请求。
在同时有wifi和移动数据的状况下,咱们应该直接屏幕移动数据的网络请求,只有当wifi断开时在调用,由于,wifi请求的耗电量远比移动数据的耗电量低的低。
后台任务要尽量少的唤醒CPU。(比方说,锁屏时,QQ的消息提示行就是唤醒了CPU。可是它的提示只有在你打开锁屏或者进行充电时才会进行提示。)
性能优化是咱们进阶的毕竟之路。So,咱们必需要会,至于“会”到什么程度,就要看我的理解了。其实,上面介绍的只是性能问题的冰山一角,真正的优化,咱们是在项目中总结出来的。但,咱们不能一味的追求优化,就例如我,如今只是在进行优化的总结,而对于真正的实行,并无开始,由于,优化是有风险的,一个不当心,整个项目均可能炸了。因此这就须要你的经验,以及各类总结,在改进行优化的地方先进行优化,看看效果如何,例如,UI的优化以及代码的优化。能够先拿一些网上的开源项目进行优化等等。最后,尽情的享受优化把~~~
《Android艺术探索》
第一时间获取本人的技术文章请关注微信公众号!