安卓性能优化1——启动优化

说到安卓性能优化,这个话题实在是很广,之前在上上一家公司专门搞了一段时间的优化,发现APP的各个方面都得到性能上的提升,为此老板还给我奖励了1000块钱。时间久了不去回顾,加上技术的日新月异,打算把自己积累的经验重新整理回顾,不放在为知笔记了,太难找了,暂且分几个专题详解:

1.启动的优化
2.布局的优化
3.内存的优化
4.电量的优化
5.卡顿的优化
6.流量的优化

启动的优化

启动缓慢导致的黑屏,白屏问题,大部分的答案都是做一个透明的主题,或者是做一个Splash界面,这就是我在之前公司的临时方案。

APK启动原理

https://www.jianshu.com/writer#/notebooks/11604270/notes/27710179/preview

APK的启动方式

1.冷启动:后台没有该应用的进程,这时系统会首先会创建一个新的进程分配给该应用
由于冷启动过程中,系统和APP的许多工作都要重新开始,所以一般而言这种启动方式是最慢且最具有挑战性的。除了创建和初始化Application和MainActivity之外,冷启动还要加载主题样式Theme,inflate布局,setContentView ,测量、布局、绘制以后显示,我们才看到了屏幕的第一帧

也就是说当用户点击你的app那一刻到系统调用Activity.onCreate()之间的这个时间段内,WindowManager会先加载app主题样式中的windowBackground做为app的预览元素,然后再真正去加载activity的layout布局。
为什么会出现黑屏白屏呢?
系统进程在创建Application的过程中会产生一个BackgroudWindow,等到App完成了第一次绘制,系统进程才会用MainActivity的界面替换掉原来的BackgroudWindow
很显然,如果你的application或activity启动的过程太慢,导致系统的BackgroundWindow没有及时被替换,就会出现启动时白屏或黑屏的情况(取决于你的主题是Dark还是Light)

冷启动优化

1.主题替换

我们在style中自定义一个样式Lancher,在其中放一张背景图片,或是广告图片之类的


1.  <style name="AppTheme.Launcher">  
2.  <item name="android:windowBackground">@drawable/bg</item>  
3.  </style>  

把这个样式设置给启动的Activity

1.  <activity  
2.  android:name=".activity.SplashActivity"  
3.  android:screenOrientation="portrait"  
4.  android:theme="@style/AppTheme.Launcher"  
5.  >  

然后在Activity的onCreate方法,把Activity设置回原来的主题

1.  @Override  
2.  protected void onCreate(Bundle savedInstanceState) {  
3.  //替换为原来的主题,在onCreate之前调用  
4.  setTheme(R.style.AppTheme);  
5.  super.onCreate(savedInstanceState);  
6.  }  

这样在启动时就通过给用户看一张图片或是广告来防止黑白屏的尴尬。

还一种方式,就是把windowBackground属性设为null,这样在启动时,backgroundWindow的背景就会变成透明的,给人的感觉就是点了应用图标以后,延迟了一会儿然后加载第一个activity的界面。

1.  <style name="AppTheme.Launcher">  
2.  <item name="android:windowBackground">@null</item>  
3.  </style>

第二种方式可能会出现黑屏,第一种的图要选取好,不然也会有突兀感,所以很多公司都选择就让他白屏,改变主题实际上是一种伪优化,因为它实质上并没有真正减少App启动的时间
另外还可以设置style

<style name="LaunchStyle" parent="AppTheme">
    <item name="android:windowIsTranslucent">true</item>
    <item name="android:windowNoTitle">true</item>
</style>
或者
<item name="android:windowDisablePreview">true</item>
这个就是直接甩锅个系统

其他方法 就是进入后直接动画,或者是MD模式的效果, GitHub 上的开源项目 saulmm/onboarding-examples-android

Application是程序的主入口

延迟初始化,后台任务,界面预加载
在Application的构造器方法、attachBaseContext()、onCreate()方法中不要进行耗时操作的初始化,一些数据预取放在异步线程中。

数据库,IO操作,密集网络请求不要放在Application的构造方法中,能使用工作线程的尽量使用工作线程,不要在Application的onCreate中创建线程池,因为那样会有比较大的开销,可以考虑延后再创建。
第三方SDK如果主线程中没有立即使用,可以考虑延迟几秒再初始化,总之一句话,尽早让用户看到应用的界面,其他操作都可以先让路。

对于MainActivity,由于在获取到第一帧前,需要对contentView进行测量布局绘制操作,尽量减少布局的层次,考虑StubView的延迟加载策略,当然在onCreate、onStart、onResume方法中避免做耗时操作

  • MultiDex以及Tinker的初始化,最先执行;关于MultiDex的优化本文不再赘述,参考我之前Multidex的系列文章
  • Application中主要做了各种三方组件的初始化;

项目中除听云之外其余所有三方组件都抢占先机,在Application主线程初始化。这样的初始化方式肯定是过重的:

  • 考虑异步初始化三方组件,不阻塞主线程;
  • 延迟部分三方组件的初始化;实际上我们粗粒度的把所有三方组件都放到异步任务里,可能会出现WorkThread中尚未初始化完毕但MainThread中已经使用的错误,因此这种情况建议延迟到使用前再去初始化;
  • 而如何开启WorkThread同样也有讲究,这个话题在下文详谈。

项目修改:

  1. 将友盟、Bugly、听云、GrowingIO、BlockCanary等组件放在WorkThread中初始化;
  2. 延迟地图定位、ImageLoader、自有统计等组件的初始化:地图及自有统计延迟4秒,此时应用已经打开;而ImageLoader
    因为调用关系不能异步以及过久延迟,初始化从Application延迟到SplashActivity;而EventBus因为再Activity中使用所以必须在Application中初始化

2.温启动:台已有该应用的进程,但是启动的入口Activity被干掉了,比如按了back键,应用虽然退出了,但是该应用的进程是依然会保留在后台
3.热启动:后台已有该应用的进程,比如按下home键,这种在已有进程的情况下,这种启动会从已有的进程中来启动应用

应用的启动时间统计

在现在的公司最近还在搞启动优化,提供2种方式

1.查看启动时间adb 命令
adb shell am start -W [PackageName]/[PackageName.XX.XXXActivity]

这个PackageName 需要你到你自己项目的清单文件去自己找,输入adb命令后,程序会等待你启动然后在统计这个时间,


image.png

ThisTime:最后一个启动的Activity的启动耗时; ThisTime:一般和TotalTime时间一样,我们的是中间开了一个
TotalTime:自己的所有Activity的启动耗时;包括创建进程+Application初始化+Activity初始化到界面显示
WaitTime: ActivityManagerService启动App的Activity时的总时间(包括当前Activity的onPause()和自己Activity的启动)一般比TotalTime大点,包括系统影响的耗时

2.使用Android Studio 的日志,在过滤框输入display

然后清空日志,然后启动如下:


image.png

Application开始到首页显示出来

Application的构造器方法——>attachBaseContext()——>onCreate()——>Activity的构造方法——>onCreate()——>配置主题中背景等属性——>onStart()——>onResume()——>测量、布局、绘制显示在界面上

上面这些阶段全部都是在主线程中执行的,任何不经意的操作都可能拖慢应用的启动速度。所以我们不应在Application以及Activity的生命周期回调中做任何费时操作,具体指标大概是你在onCreate,onResume,onStart等回调中所花费的总时间最好不要超过400ms,否则用户在桌面点击你的应用图标后,将感觉到明显的卡顿

针对上面的需求,一般处理是将任务分优先级启动,应用启动开始加载 ;首页渲染后加载;渲染后延迟加载
具体延迟多久 在性能好的手机上面启动非常快,很短的延迟就行了,但是在低端手机上缺很慢,还要为了兼容久手机,一般延长很长的时间,

  第一种写法:直接PostDelay 300ms.
  myHandler.postDelayed(mLoadingRunnable, DEALY_TIME);
  第二种写法:优化的DelayLoad
  getWindow().getDecorView().post(new Runnable() {
    @Override
   public void run() {
        myHandler.post(mLoadingRunnable);
   }
  });

第二种,在窗口完成以后进行加载,这里面的run方法是在onResume之后运行的具体参考这个:http://www.androidperformance.com/2015/11/18/Android-app-lunch-optimize-delay-load.html

启动优化思路

1、UI渲染优化,去除重复绘制,减少UI重复绘制时间,打开设置中的GPU过度绘制开关,各界面过度绘制不应超过2.5x;也就是打开此调试开关后,界面整体呈现浅色,特别复杂的界面,红色区域也不应该超过全屏幕的四分之一;
2、根据优先级的划分,KoMobileApplication的一些初始化工作能否将任务优先级划分成3,在首页渲染完成后进行加载,比如:PaySDKManager。
3、主线程中的所有SharedPreference能否在非UI线程中进行,SharedPreferences的apply函数需要注意,因为Commit函数会阻塞IO,这个函数虽然执行很快,但是系统会有另外一个线程来负责写操作,当apply频率高的时候,该线程就会比较占用CPU资源。类似的还有统计埋点等,在主线程埋点但异步线程提交,频率高的情况也会出现这样的问题。
4、检查BaseActivity,不恰当的操作会影响所有子Activity的启动。
5、对于首次启动的黑屏问题,对于“黑屏”是否可以设计一个.9图片替换掉,间接减少用户等待时间。
6、对于网络错误界面,友好提示界面,使用ViewStub的方式,减少UI一次性绘制的压力。
7、任务优先级为2,3的,通过下面这种方式进行懒加载的方式

getWindow().getDecorView().post(new Runnable() {
@Override
public void run() {
myHandler.post(mLoadingRunnable);
}
});
8、Multidex的使用,也是拖慢启动速度的元凶,必须要做优化。后面有空专门写一篇Multidex

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • 请保持淡定,分析代码,记住:性能很重要。 启动时间优化 毫无疑问,应用的启动速度越快越好。 本文可以帮助你优化应用...
    Mupceet阅读 11,254评论 5 19
  • 转载自一触即发 App启动优化最佳实践 一触即发 App启动优化最佳实践 文中的很多图都是Google性能优化指南...
    CP9阅读 597评论 0 3
  • 图/网络 这几天微博朋友圈就被刷屏了。吃瓜群众都在看林丹出轨的消息,这个话题已经登上了微博热搜。 犹记得前不久还看...
    夏至星辉阅读 183评论 0 1
  • 我站在高高的后山岗上, 看霓虹在眼前闪着缥缈的光, 这城市的聒噪, 缠绵在凄冷的夜里, 竟没有一点声响。 是不是这...
    俗人阿久阅读 195评论 0 1