https://testerhome.com/topics/4304
一、选择适当的基类和控件
a、选择合适的基类
我们在继承基类时,可能基类中做了很多事情但子类中并不会用到,反而对性能有影响
例子1: Setting的某子页面为了使用SettingsPreferenceFragment中的某一两个方法而继承了SettingsPreferenceFragment,而其他大多数变量和方法并没有使用到,如:在SettingsPreferenceFragment在onCreateView中inflate的布局但在该子类中并不会用到,而且findViewById实例化的变量也用不上.
优化方法:继承普通的Fragment,将用到SettingsPreferenceFragment中的1,2个方法提取出来使用即可.
例子2: Setting主界面LeUIMainSettings的父类是SettingsActivity,在SettingsActivity的 onCreate()函数中调用setContentView(),而在子类的onCreate方法中又重新调用setContentView()设置了不同的view,导致引起无效的inflate布局
优化方法:该情况下由于子类很多地方都对父类有着依赖,必须继承该父类,但我们可以做到:如果子类需要设置不同于父类的layout布局时:添加标志位,在父类调用setContentView()前进行判断,避免无效设置.
b、选择合适的控件
为了使用控件的某一简单的功能引入了三方控件,但该控件中可能进行许多复杂的操作并不会用上.
例子:在setting中系统设置页面listview使用的是PinnedHeaderListView控件,而跟踪代码发现并没有利用到PinnedHeaderListView的特性,反而PinnedHeaderListView在onScroll方法中做了许多判断逻辑和measure操作,导致初始加载和滚动listview时的耗时和丢帧.
优化方法:分析确定没有使用PinnedHeaderListView的必要性后改用原生的listView控件.
适当选择使用preference,preference的使用场景一般是:数据项条目数较为固定,展示比较简单,不会经常刷新的地方,而且当preference为自定义样式的preference时,PreferenceGroupAdapter并不会进行复用,导致每次
更新该条目时都会重新inflate.
例子:在Setting的应用设置页面采用的就是preference(自定义的preference),而该页面的数据会存在几次异步刷新,所以导致多次inflate所有条目,出现丢帧现象.
优化方法:改用listView,编写viewHolder,然后当数据变化后使用NotifyDataSetChange
二、延迟加载策略:
在尽量减少对内容展示的影响的情况下,延迟加载策略能有效的提高页面的进入速度
1.延迟ViewPager缓存页面:在使用ViewPager时,viewPager会自动帮我们缓存加载页面,因此我们在显示page1的时候page2也紧接着创建了.
例子:Setting主界面的主布局就是一个viewPager,左边是系统设置,右边是应用设置.用户最先看到是系统设置页面,因此我们可以让应用设置页面延迟加载以加快主界面进入速度.
优化方法:在应用设置页面采用handler发送一个delay 200ms的message再进行数据获取和列表刷新,这样既加快了启动速度,且当用户点击应用设置时也并感觉不到200ms的延迟.
2.当一个页面需要展示多项内容时,可以将必要和用户常用的内容模块第一时间展示,对于加载缓慢并不是急迫展示的内容延迟加载.
例子:Setting的电池子页面中,加载各App的耗电列表较为耗时,而且该部分模块处于页面较底部位置.
优化方法:将电池页面中的:电池总览和温度之类的信息在主线程同步加载显示,把各App的耗电加载放到子线程中计算之后再异步更新UI界面.
三、listView的优化
App性能优化,有很大一部分的优化都是在对ListView的展现和加载以及滑动卡顿进行优化,对于ListView优化除了使用viewHolder基础的优化,以下还例举了4点优化方向
1.通过性能工具分析发现:listView在obtainview中的inflate布局时耗时较长
例子:设置的系统设置页面使用的ListView就是在其Adapter的执行到getView时才进行view的inflate,因此会占用了一长段主线程的时间.
优化方法:子线程提前进行listview item view的inflate,并存储在内存变量中,在真正到listview的getview的时候可以直接使用内存变量中已经实例化好的View对象.
2.将getView中的耗时操作放到子线程中加载
例子:设置的系统设置页面的listView的每个item中都会展示相应图标,在每次getView时再进行图标的加载会比较耗时.
优化方法:在子线程中将图标先建好缓存,这样在getView显示图标时,可以直接拿来使用,并且有了缓存后不用每次getView的时候都重新加载.
3.简化ListView中item的布局
例子:设置的系统设置页面的listView的item中存在许多累赘的嵌套布局
优化方法:减少累赘布局层次,减轻inflate的工作.
4.ListView处于idle状态再进行更新,减少滑动过程中的丢帧现象
优化方法:通过监听listView的onScroll,我们可以知道当前listView的滚动状态,当判断到listView处于idle状态时我们再在getview中进行某些耗时的操作
例子:见: 六、避免频繁向主线程发送handler message
四、注意内存的使用,减少gc
1.选择变量实例化适当的位置,减少GC次数
例子:Setting中有些成员变量的实例化放到了onResume中,致使申请内存的次数增多,gc次数增加.
优化方法:将成员变量的实例化放到onCreate中.
2.避免每次重新构造listview的list数据
例子:在设置的系统设置页面中,每次跟新listView时,都是重新构造一份新的list数据,然后再更具相应逻辑remove掉一些条目.
优化方法:加载一份原始数据,更新时在copy一份对原始数据的引用,然后在copy后的List数据上进行条目的刷选.
五、合理使用同步锁
1.对于不同的临界区域或临界变量选择合适的锁
例子:LeuiApplicationsState类中所有的临界区域和临界变量所有地方使用了同一把锁,导致调用时出现很多等锁耗时的情况.
优化方法:分析代码逻辑对于不属于同一临界区域的代码和变量在不存在并发访问的问题的前提下,使用不同的同步锁.
2.避免出现主线程调用时,长时间等待子线程的锁资源.
例子1:在主线程调用LeuiApplicationsState中的某些方法时,会出现长时间等待LeuiApplicationsState中子线程中持有的锁资源.
优化方法:最好是能避免竞争同一把锁资源,如果必须使用同一把锁,可以调整调用先后顺序,减少同一时间内竞争同一把锁的情况.
例子2:在listView的优化过程中(子线程中提前inflate view),子线程中使用getSystemService的layoutInflate进行inflate,出现主线程等锁情况(因为:子线程中通过getSystemService获取到的layoutInflate是同一对象,而在inflate()函数中一开始就调用synchronized(mConstructorArgs))
优化方法:子线程中通过new一个新的PhoneLayoutInflate对象,避免出现与主线程竞争同一把锁的情况.
六、避免频繁向主线程发送handler message
避免频繁向主线程发送handler message,以阻塞主线程looper的message queue造成点击,滑动等事件不能得到及时处理,出现卡顿丢帧现象.
例子:设置应用管理中使用了工具类LeuiApplicationsState后台线程加载每个应用的图标和计算占用的空间大小,每加载出一个就发送handler消息到主线程中去,造成一开始滚动listView时卡顿丢帧
优化方法:消息通知在后台线程中回调或者将消息发送到子线程的looper中,然后当图标和空间大小加载一定数量后,并且判断listview处于idle状态后再抛给主线程处理.
七、其他
1.梳理业务逻辑去掉不用了的代码
在优化设置的过程中发现:一些耗时的代码的调用但并没有起到任何作用,确认后发现该部分代码是之前功能的遗留.
2.overDraw的优化:Overdraw概要
减少不必要的background层级,和减少layout布局中的嵌套层级
3.内存泄露的优化:检测内存泄露