Android 性能调优
TPS(TransactionPer Second)每秒处理的事物数,是系统吞吐量的指标。响应时间,用户操作开始到系统给用户正确反馈的时间。一般包括系统处理时间+网络传输时间+展现时间
同步改异步
耗时操作放在线程中执行防止占用主线程,一定程度上解决anr,注意线程和service结合(防止activity被回收后线程也被回收)以及线程的数量。
线程池特点:
1.重用存在的线程,减少对象创建,消亡的开销
2.刻有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞
3.提供定时执行,定期执行,单线程,并发数控制等功能
四种线程池:
1. newCachedThreadPool创建一个可以缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程,pool.execute(runnale)
2. newFiaedThreadPool创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待,定长线程池的大小最好根据系统资源进行设置如Runtime.getRuntime().availableProcessors(), pool.execute(runnable)
3. newScheduledThreadPool创建一个定长线程池,支持定时及周期性任务执行
定时执行pool.schedule(runnable,3, TimeUnit.SECONDS);
定期执行pool.scheduledAtFixedRate(runnable,1, 3, TimeUnit.SECONDS),延迟一秒后每三秒执行一次,比Timer更安全
4. newSingleThreadExecutor创建一个单线程化的线程池,他只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO,LIFO,优先级)执行
缓存
Java的对象创建需要分配资源较消耗时间,加上创建的对象越多会造成越频繁的gc影响系统响应。主要使用单例模式、缓存(图片缓存、线程池、View缓存、IO缓存、消息缓存、通知栏notification缓存)及其它方式减少对象创建。
单例模式
私有化构造函数,定义静态的instance对象和getInstance()方法,getInstance()方法需要使用同步锁synchronized(Singleton.class)防止多线程同时进入造成instance多次实例化,判断是否实例化不必每次执行synchronized获取对象锁。
public classSingleton{
private static Singleton instance =null;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null){
synchronized(Singleton.class){
if(instance ==null){
instance = newSingleton();
}
}
}
}
}
图片缓存
ImageCache
ImageSdCache
View缓存
ListView缓存机制,通过convertView是否为空减少layout inflate次数,通过Viewholder减少findviewbyid的次数,inflate很费时。
@Override
public ViewgetView(int position, View convertView, ViewGroup parent){
ViewHolder holder;
if(convertView == null){
convertView =inflater.inflate(R.layout.list_item, null);
holder = newViewHolder();
convertView.setTag(holder);
}else{
holder =(ViewHolder)convertView.getTag();
}
}
private staticclass ViewHolder{
private ImageView appIcon;
private TextView appName;
}
IO缓存
使用具有缓存策略的输入流,BufferedInputStream替代InputStream,BufferedRead替代Reader, BufferedReader替代BufferedInputStream,对文件,网络IO皆适用。
消息缓存
通过handler的obtainMessage回收Message对象,减少Message对象的创建开销hander.sendMessaage(handler.obtainMessage(1))。
通知栏缓存
下载中需要不断改变通知栏进度条状态,如果不断新建Notification会导致通知栏很卡,Map<Sting, Notifiction> notificationMap = newHashMap<String, Notification>();如果notificationMap中不存在,则新建notification并put into map。
其他
能创建基类解决问题就不用具体子类:除需要设置优先级的线程使用new Thread创建外,其余线程使用new Runnable, 因为子类会有自己的属性创建需要更多开销
控制最大并发数量:使用java的Executors类,通过Executors.newFixedThreadPool(nThreads)控制线程池最大线程并发。
http请求增加timeout,数据库缓存http response ,根据http投信息中得Cache-Control域确定缓存过期时间。
频繁访问,或一次访问消耗较大的数据缓存。
Layout
使用抽象布局标签(include, viewstub, merge)去除不必要的嵌套和view节点。减少不必要的inflate,布局调优相关工具(hierarchy viewer 和 lint)。
TextView属性优化 android:ellipsize=”marquee”跑马灯效果极耗性能。
a. 抽象布局标签:
<include>布局模块化
<includelayout=”@layout/layout.xml”>
<viewstub>同include标签一样引入外部布局,viewstub引入的布局默认不会扩张,即不会占用显示也不会占用位置,常用作引入默认不会显示,只在特殊情况下显示(如进度布局,网络失败显示的刷新布局,信息出错的提示布局)。
ViewStub stub =(ViewStub)findViewById(R.id.network_error_layout);
ViewnetworkErrorView = stub.inflate();
ButtonnetworkSetting = (Button)networkErrorView.findViewById(R.id.network_setting);
<merge>布局顶节点是FrameLayout且不需要设置background或padding等属性,可以用merge代替,某布局作为子布局被其他布局include(外层为RelativeLayout)时,使用merge当作该布局节点。
b. 去除不必要的嵌套和View节点,首次不需要使用的节点设置GONE或者使用Viewstub, LinearLayout比例时层级较深使用RelativeLayout。
c. 减少不必要的inflate
上面stub.inflate(),可以先判断networkErrorView是否为空。
d. 用SurfaceView或TextureView代替普通View,SurfaceView或TextureView可以通过将绘图操作移动到另外一个单独线程上提高性能,普通View绘制过程都是在主线程(UI线程)中完成,SurfaceView在常规视图系统之外,所以无法像常规视图一样移动,缩放或旋转,TextureVIew不仅在单独线程绘制,还可以像常规视图一样操作。
e. 尽量为所有分辨率创建资源,减少不必要的硬件缩放,会降低UI的绘制速度。
GPU过渡绘制
过渡绘制是指一个像素被绘制了多次。
原因主要有:
1. 太多的View叠加
2. 复杂的层级叠加
3. 更长的inflation
颜色标识: 从好到差:蓝-绿-淡红-红
1. 蓝色1x过度绘制
2. 绿色2x过度绘制
3. 淡红色3x过度绘制
4. 红色超过4x过度绘制
验收标准:
1. 控制过度绘制为2x
2. 不允许存在4x过度绘制
3. 不允许存在面积超过屏幕1/4区域的3x过度绘制(淡红色区域)
数据库
主要包括索引和事物及针对Sqlite的优化:
1. 索引,加快了数据库检索的速度,难维护
2. 事务,原子提交,同一事务内的修改要么完成要么失败
3. 语句拼接使用StringBuilder代替String
4. 查询时返回更少的结果集及更少的字段
5. 少用cursor.getColumnIndex,建表时使用static 变量记住某列的index,
6. 异步线程,并发,大小,网络局限性,并且为表级锁,不用多线程操作,数据量大时也会有延时卡顿,使用单线程池,在任务中执行db操作,通过handler返回结果和ui线程交互。
算法
使用hashMap代替arrayList
使用SpqrseArray代替key为int型的hashmap
HanshMap循环,既需要key也需要value
Map<Srting, String> map = newHashMap<String, String>();
for(Entry<String, String> entry :map.entrySet()){
entry.getKey();
entry.getValue();
}
只遍历key
for(String key : map.keySet()){}
延迟执行
对于很多耗时逻辑没必要立即执行,可以将其延迟执行;
//线程延迟执行
ScheduledExecutorServicescheduledThreadPool = Executors.newScheduledThreadPool(10);
//消息延迟发送
handler.sendMessageDelayed(handler.obtainMessage(0),1000);
网络优化
a. 图片必须缓存,最好根据机型做图片适配
b. 所有的http请求必须添加httptimeout
c. 开启gzip压缩
d. 接口数据以json格式返回,而不是xml或html
e. 根据http头信息中得Cache-Control及expires域确定是否缓存请求结果
f. 确定网络请求的connection是否keep-alive
g. 减少网络请求次数,服务器端适当做请求合并
h. 减少重定向次数
i.接口服务器端响应时间不超过100ms
其他
Bitmap
在Java的设计中没有释放内存这个概念的,但实际使用中,大对象除外。Android中的Bitmap就是这么一个例外。Bitmap的不及时回收会造成内存的彪升。Recycle方法不是必须调用,就算调用了GC也不会立即回收java层的bitmap对象,这跟把一个对象手动置空一个道理。
Recycle方法就是把c层的像素内存给释放。C层的内存还是算到当前进程的内存里的,而构成一个bitmap对象的内存大部分都是c层的像素数组,所以,手动调用bitmap。Recycle并把bitmap对象的java层引用手动置null可以瞬间释放像素所占的内存,并让虚拟机下次运行时回收bitmap对象所占的内存。
AlarmManager
不要不做限制地使用定时器,要根据场景选择性地注册并及时注销定时器。比如,依赖网络条件的应该使用SyncAdapter;用户注销后,记得注销定时器等。另外,定时器的周期最好控制在15分钟以上,这个是Android官方给出的经验值,再频繁的话,对待机电量消耗的影响就会明显变大了。关于定时器的模式。wakeup的好处是可以对用户关心的状态及时更新,但会打断机器休眠,如果很频繁的话,会使用机器睡不下去,耗电量明显上升。非wakeup的好处就是比较省电,但会把所有的任务集中到用户唤醒机器的那一刹那来执行,从而造成用户唤醒机器时的卡顿。
WakeLock
除了像视频播放这种即使用户长时间不操作也需要保持在前台的程序,实质上很少有申请Wake Lock的需求。另外,一旦程序退出前台,记得释放。
Broadcast
通过AndroidManifest.xml静态声明的全局广播接收器是耗电大户。可能会有很多程序注册像“网络状态改变”、“电话状态”、“设置解锁”等常见事件。那么,在这些事件发生时会触发多个进程创建,频繁的GC,卡顿也就来了。所以,请尽量使用动态注册。另外,“网络状态改变”可以使用Android提供的SyncAdapter来实现低成本的监听。
Receiver的进程配置也有些技巧。如果Receiver需要唤醒UI进程,就把它和UI配置到一个进程当中,如果需要唤醒后台服务,就把它和后台服务配置到一个进程当中。可以减少系统创建进程的开销。
后台服务
把后台服务单独做为一个进程,可以大大减少UI进程的内存压力。如果把它们放到一个进程中,只要它们中的任意一个没有完成任务,它们俩都会一直存在于内存中。
长连接
长连接意味着需要保持网络连接、周期性的心跳数据传输、设备无法休眠,使用需谨慎。
其他
64位类型long, double的处理比32位int慢
final类型存储在常量区中读取效率更高
LocalBroadcastManager代替普通BroadcastReceiver,效率和安全性都更高