本文介绍了一些有效的性能优化方法,主要内容包括布局 优化、绘制优化、内存泄漏优化、响应速度 优化、ListView优化、Bitmap优化、线程优化以及一些性能优化的建议。感谢《android开发艺术探索》的指导。
一、布局优化
1,ViewStub 当使用的才会加载
2,include 重用布局
3,merge 减少布局的层级
二、绘制优化
指在View的onDraw中要避免执行大量操作。
三、内存泄漏优化
常出现的场景:
①静态常量无法释放导致的内存泄漏
②单例模式拥有某个类的对象,导致的无法释放,内存泄漏
③属性动画没有销毁,界面已经销毁。但是动画还是在运行。(在onDestroy中销毁动画)
四、响应速度优化
避免在主线程中做耗时工作。activity 5秒无法相应屏幕触摸事件或者键盘输入事件、BroadcastReceiver如果10秒还未完成操作。
五、ListView和Bitmap优化
ListView:
①采用Viewholder并避免在getView中执行耗时工作。
②要根据列表的滑动状态来控制任务的执行频率。(比如快速滑动时不适合开启大量异步任务,可以在用户滑动的时候停止异步加载。)
public void onScrollStateChanged(AbListView view, intscrollState){
if(scrollState==OnScrollListener.SCROLL_STATE_IDLE){
mIsGridViewIdle=true;
mImageAdapter.notifyDataSetChanged();
}else{
mIsGridViewIdle=false;
}
}
if(mIsGridViewIdle&&mCanGetBitmapFromNetWork){
imageView.setTag(uri);
mIMageLoader.bindBitmap(uri,imageView,mImageWidth,mImageWidth);
}
③开启硬件加速使滑动就流畅(通过设置android:hardwareAccelerated="true"可为)。
Bitmap:
BitmapFactory加载的四类方法:
①decodeFile②decodeResource③decodeStream④decodeByteArray
decoderfile和decodeResource间接调用了decodeStream
如何高效记载bitmap:
1)将BitmapFactory.Options的inJustDevodeBounds设为true并加载图片。
2)从BitmapFactory.Options中取出图片的原始宽高信息,他们对应与outWidth和outHeight参数。
3)根据采样率的规则并结合目标View的所需大小计算出采样率inSampleSize。
4)将BitmapFactoryOptions的inJustDecodeBounds参数设为false,然后加载图片。
实现:
public static Bitmap decodeSampledBitmapFromResource(Resources res,int resId,int reqWidth,intreqHeight){
//Firstdecode with inJustDecodeBounds=tru to check dimensions
final BitmapFactory.Options options=new BitmapFactory.Options();
options.inJustDecodeBounds=true;
BitmapFactory,decideResource(res,resId,options);
//colculate inSampleSize
options.inSampleSize=calculateInSampleSize(options,reqWidth,reqHeight);
//Decode bitmap with inSampleSize set
options.inJustDecodeBounds=false;
return BitmapFactory.decodeResource(res,resId,options);
}
//calculateInSampleSize
public static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){
//Raw height and width of image
final int height=options.outHeight;
final int height=options.outWidht;
int inSampleSize=1;
if(height>reqHeight||widht>reqWidth){
final int halfheight=height/2;
final int halfwidth=width/2;
//calculate the largest inSampleSize value that is a power of 2 and keeps both
//height and width larger than the requested height and width.
while((halfHeight/inSampleSize)>=reqHeight&&(halfWidth/inSampleSize)>=reqWidth){
inSampleSize*=2;
}
}
return inSampleSize;
}
使用:
如果期望得到大小为100x100像素的图片。
mImageView.setImageBitmap(decodeSampleBitmapResource(getResources(),R.id.myimage,100,100));
6线程优化
使用线程池,避免程序中存在大量的Thread。线程池可以重用内部的线程。从而避免线程创建和销毁所带来的开销。
同时还能有效的控制线程池的并发数。避免大量线程因互抢资源导致阻塞。还能 对线程进行简单管理,并提供定时执行以及指定间隔循环执行。
线程池分类:
1,FixedThreadPool
2,CachedThreadPool
3,ScheduledThreadPool
4,SingleThreadExecutor
这里就不详细说明这四种类型的功能特性,请大家自行搜索。但他们的都是通过ThreadPoolExecutor实现的。
public ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
解析:
corePoolSize:核心线程数,默认情况下,核心线程会在线程池一直存活,及时他们 处于闲置状态。如果将ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,那么闲置的核心线程在等待新任务到来时会有超时策略,这个时间间隔由keepAliveTime决定。
maximumPoolSize:所能容纳最大线程数,活动线程达到这个值后,后续新任务会呗阻塞。
keepAliveTime:非核心线程闲置时的超时时长,超过就被回收。当allowCoreThreadTimeOut为true的时候,同样会作用于核心线程。
unit:指定keepAliveTime参数的时间单位,值有TimeUnit.MILLISECONDS(毫秒),TimeUnit.SECONDS(秒),TimeUnit.MINUTES(秒)等。
workQueue:线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数中。
threadFactory:线程工厂,为线程池提供创建新线程的功能。ThreadFactory是一个接口.
它只有一个方法:Thread newThread(Runnable r);
最后给大家的优化建议
1)避免创建过多的对象。
2)不要过多使用枚举,枚举占用的内存空间要比整形大。
3)常量请使用static final 来修饰。
4)使用安卓特有的数据结构,比如SparseArray和Pair等,他们都具有更好的性能。
5)适当使用软引用,和弱引用。
6)采用内存缓存和磁盘缓存。
7)尽量采用静态内部类,这样可以避免潜在的由于内部类而导致的内存泄漏。
(内存泄漏工具分析------MAT)
8)尽量用局部变量
10)合理使用浮点类型
11)及时动态回收不用的资源(str=null,btm.Recycle())
12)移除Acitivty默认背景,getWindow().setBackgroundDtawable(null)