你是否有个疑惑,为什么iphone那么好用,android努力了这么就,还是没追上。除了参差不齐的硬件问题,系统机制,平台历史遗留等问题,开发者的水平可以说是占了很大锅。为啥呢,这里用我一个亲身经历的例子看来告诉你。
本人今年壕了一把,买了一台小米10至尊纪念版银色顶配(android系统10.0,16g运行内存,512机身存储),除开系统等其他开销,可用运行内存11g,可说机皇中的机皇,单看硬件,别的手机,一个能打的都没有。
我心想,android不老是运行内存不够用,老是容易卡死么,这下子我就不信运行内存还不够用,而且从android6.0以来,android的系统也越来越趋于完善了,应该可以愉快地玩耍,好好做主力机了吧。但是打脸的事情还是发生了。
事情发生在一个做竞品分析的下午,有一款能看小视频的软件(这里就不点名批评了),其中有一块功能有点类似抖音刷视频方式,总是经常性出现卡顿,无响应问题,有时候竟然还直接闪退,气我呀怀疑人生?我手机卡?我啥都没开,就只开了这个软件。我骁龙865,11g运行内存的面子往哪搁?我这么好的硬件配置,cpu会不够用?内存会不够用?老资玩大型游戏,特效、画质、帧率最高,都能玩得杠杠的,你不行?如果你跟我一样是一个稍微有点格调的android开发,那么一定知道:这个扑街仔,肯定是在主线程做了耗时操作,内存也没管理好,出现了内存泄露,还不用多进程,只有android系统限制分配的那么一小点可怜的内存,然后让硬件优势一点都没有发挥出来,白白浪费了机皇性能。
所以,兄弟们,android被人骂,90%原因,就是那些低水平的开者导致的。兄弟啊,功能不是实现了就完事啊,这种最基础的优化还是要有的。以后用户说android不好用,经常卡,作为开发者应该觉得惭愧,都2020年了,还不优化!!
下面让我们一起走进科学,探索优化的世界。
世界那么大,我想去看看从性能使用的角度来看,多线程的合理使用能提高应用的性能;采用多进程,能让应用获得更多的系统资源,例如内存等,让应用更流畅;
从用户关心的角度看,优化可以分为4个方面: 稳定,流畅,电量流量损耗,内存占用等;
下面主要从用户的角度来展开
一、稳定(无响应、崩溃)
ANR,俗称无响应
ANR问题本质是一个性能问题。ANR机制实际上对应用程序主线程的限制,要求主线程在限定的时间内处理完一些最常见的操作(启动服务、处理广播、处理输入), 如果处理超时,则认为主线程已经失去了响应其他操作的能力。主线程中的耗时操作,譬如密集CPU运算、大量IO、复杂界面布局等,都会降低应用程序的响应能力。
主线程被阻塞时间耐受度,activity中为5秒,BroadcastReceiver中为10秒,服务中为20秒,超过这些限制就会抛出ANR
常见导致无响应的问题:主线程做了等耗时的操作,例如IO、出现锁竞争、出现死锁
Crash,俗称崩溃
程序抛出了未捕获的异常导致的应用闪退。
常见异常:空指针异常,数组越界,内存溢出等,可以通过腾讯bugly捕获上传ANR,Crash及时处理
二、流畅(卡顿)
卡顿一般会体现在这几个方面:UI 绘制、应用启动、页面跳转、事件响应等
UI 绘制丢帧卡顿
Android的绘制需要经过onMeasure、onLayout、onDraw等几个步骤,所以布局的层级越深、元素越多、耗时也就越长。还有就是Android 系统每隔 16ms 发出 VSYNC 信号,触发对 UI 进行渲染,如果每次渲染都成功,这样就能够达到流畅的画面所需的 60FPS。如果某个操作花费的时间是 24ms ,系统在得到 VSYNC 信号时就无法正常进行正常渲染,这样就发生了丢帧现象。 因此绘制的层级深、页面复杂、刷新不合理,容易导致卡顿 。
那么如何优化呢?通过减少刷新次数,缩小刷新区域,减少页面层级,简化页面布局等来优化,例如:
在布局优化方面,从以下几个方面进行优化:
布局复用,使用<include>标签重用layout;
提高显示速度,使用<ViewStub>延迟View加载;
减少层级,使用<merge>标签替换父级布局;
减少使用wrap_content(wrap_content会增加measure计算成本);
删除控件中无用属性;
在绘制方面,从以下几个方面进行优化:
减少重复绘制,不要重复设置背景,减少颜色堆叠,移除 XML 中非必须的背景,移除 Window 默认的背景、按需显示占位背景图片等。
减少页面刷新的区域,善用invalidate(),fragment等,不要某一部分页面变化就刷新整个页面,自定义View优化,使用 canvas.clipRect()来帮助系统识别那些可见的区域,只有在这个区域内才会被绘制。
关于动画效果,需要根据不同机型的性能来确定是否要使用复杂的动画特效,动画绘制的方法里不要使用耗时操作。采用硬件加速方式来提供流畅度。
适当使用surface及其子类(例如surfaceview)来加载大图
应用启动:
冷启动:当启动应用时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用,这个启动方式就是冷启动。
热启动:当启动应用时,后台已有该应用的进程(例:按back键、home键,应用虽然会退出,但是该应用的进程是依然会保留在后台,可进入任务列表查看),所以在已有进程的情况下,这种启动会从已有的进程中来启动应用,这个方式叫热启动。
启动优化:
不要在Application进行耗时操作,不要onResume之前的生命周期里做过多耗时初始化或者其他工作,可以采用分布加载、异步加载、延期加载策略来提高应用启动速度。减少页面层级,简化页面。
秒开优化:
1、把Theme里的windowBackground背景图设置成我们APP的Logo图,或者使用layer-list,作为APP启动的引导
2、将背景颜色设置为透明色,这样当用户点击桌面APP图片的时候,并不会"立即"进入APP,而且在桌面上停留一会,其实这时候APP已经是启动的了,只是我们把Theme里的windowBackground的颜色设置成透明的,这样就看看起来像是秒开了
页面跳转:
不要在onResume之前的生命周期里做过多耗时初始化或者其他工作,可以采用可见再加载,或者分布延迟加载
给activity、fragment的跳转添加转场动画
采用尽量少activity,多fragment的架构,fragment的切换比activcity的切换代价更小
事件响应:
事件的响应不要被耗时的异步操作阻塞,例如网络请求,不要等到网络返回了结果才给用户点击的反馈,而是应该现在ui层面给用户反馈成功点击了该事件的即时反馈,当结果返回了才真正对结果的做处理。
数据处理卡顿,这种卡顿场景的原因是数据处理量太大,一般分为三种情况,一是数据在处理 UI 线程,二是数据处理占用 CPU 高,导致主线程拿不到时间片,三是内存增加导致 GC 频繁,从而引起卡顿。
三、电量、流量耗损
电量优化:
及时关闭无用后台服务,减少后台服务数量等。
尽量集中地批量执行网络请求,尽量避免频繁的间隔网络请求,慎用心跳包,长连接等。
尽量在wifi状态下请求,获取网络状态(wifi或流量),因为每次唤醒蜂窝信号进行数据传递,都会消耗很多电量,它比WiFi操作更加的耗电。
获取WakeLock后及时释放WakeLock,不要频繁唤醒手机来做一些消息同步,特别是在应用低电量的时候。例如
使用WakefulBroadcastReceiver,它是BroadcastReceiver的一种特例。它会为你的APP创建和管理一个PARTIAL_WAKE_LOCK 类型的WakeLock。WakefulBroadcastReceiver把工作交接给service(通常是IntentService),并保证交接过程中设备不会进入休眠状态,执行完毕自动释放。通过 startWakefulService(Context, Intent) 启动 ,调用 completeWakefulIntent(intent) 释放唤醒锁。
获取到手机的充电状态,得到充电状态信息之后,我们可以有针对性的对部分代码做优化,选择充电时执行任务等。
使用Job Scheduler,对于一些不紧急的任务,可以交给Job Scheduler来处理,Job Scheduler集中处理收到的任务,会选择合适的时间,合适的网络,再一起进行执行。(需要android api >= 21)
根据实际情况,选择gps定位还是网络定位,降低电量消耗(gps更耗电)。定位完成,及时关闭。如果需要实时定位,则减少更新频率。
ps:
PowerManager powerManager=(PowerManager)getSystemService(POWER_SERVICE);
PowerManager.WakeLock wakeLock=powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"myPartialWakeLock");
//唤醒
wakeLock.acquire();
//执行任务
doJob();
//释放锁
if(wakeLock.isHeld()){
wakeLock.release();
}
网络优化:
节省流量:
减少请求数据的大小:使用json,protobuffer等更高效数据格式进行传输,对于post请求,body可以做gzip压缩的,header也可以做数据压缩。返回数据的body也可以做gzip压缩,okhttp就默认做了gzip压缩。
合理使用缓存策略,减少请求次数。
获取网络状态(wifi或流量),在wifi状态下在做一些耗流量的加载,例如加载视频,高清大图等
加载速度:
针对网络情况,返回不同的图片数据,一种是高清大图,一种是正常图片,一种是缩略小图。当用户处于wifi下给控件设置高清大图,当4g或者3g模式下加载正常图片,当弱网条件下加载缩略图,当然所有能减少数据传输量的策略都能有效提高加载速度。
合理使用缓存优先展示
连接复用:节省连接建立时间,如使用okhttp就有复用连接池。
请求合并:即将多个请求合并为一个进行请求
四、内存占用
用户关心的内存占用,可以分为三个方面:安装包占用,应用使用过程中在本地磁盘产生文件占用,运行内存占用
安装包占用优化(apk瘦身)
代码混淆:使用proGuard 代码混淆器工具,它包括压缩、优化、混淆等功能。
资源优化:比如使用 Android Lint 删除冗余资源,资源文件最少化等。
图片优化:对 PNG 格式的图片做压缩处理,icon使用矢量图,降低图片色彩位数等。
功能重复的库优化:避免使用重复,交叉功能的库。
插件化:比如功能模块放在服务器上,按需下载,可以减少安装包大小。
本地磁盘占用优化
提供统一的文件管理清除功能
规范本地磁盘的使用,创建的文件有放到一个与app名相关的文件夹下
运行内存占用
注意避免产生内存泄露
注意内存对象的复用,避免创建大量的重复对象
慎用静态内存对象,它会伴随整个生命周期,导致一直占用内存
更多请查阅android内存的三座大山
网友评论