经过前一阶段的调查,大概对性能优化已经有了初步的解决方案:
先给大家介绍一下UC公司的性能优化指标以及部分方案:
Android性能优化代码规范
l 编码之初准备篇:
单个Activity显示的视图一般情况少于20,层数少于4
对于Adapter控件,如ListView ,item的布局层数一般情况为2,不得超过3.
getWindow().setBackgroundDrawable(null);
android的默认背景不为空。
l 将Activity的背景放到Activity的Theme中设置。同时避免fragment和activity背景重复设置:
Theme设置属性
<item name="android:windowBackground">src_image</item>
l 采用硬件加速:
androidmanifest.xml中application添加
android:hardwareAccelerated="true"。
需要注意的是:android 3.0以上才可以使用。
l 使用ProGuard去除不必要的代码:
#删除无用的类
-assumenosideeffects class android.util.Log {
public static *** d(...);
public static *** v(...);
public static *** e(...);
public static *** i(...);
public static *** w(...);
}
l apk打包签名时,使用zipalign工具对齐:
zipAlignEnabled true
l 后台可以处理的逻辑不要放在前台,这样可能会有预料不到的问题
l 内存泄露引入三方框架LeakCanary :使用超级方便:
http://blog.csdn.net/walid1992/article/details/50470958
l Android程序冷启动优化(第一次启动应用):
1、在logoactivity设置一个theme,设置windowBackground属性,避免黑屏阶段。
2、对app进行延迟启动控制,采用延迟加载技术
private Handler handler = new Handler(); //延迟加载 runnable private Runnable delayLoadRunnable = new Runnable() { @Override public void run() { Logger.d("start delayLoadRunnable "); init(); } }; //优化的DelayLoad : 采用延迟加载策略 window.getDecorView().post(new Runnable() { @Override public void run() { handler.post(delayLoadRunnable); } });
Activity 在启动时,会在第二次执行 performTraversals 才会去真正的绘制,原因在于第一次执行 performTraversals 的时候,会走到 Egl 初始化的逻辑,然后会重新执行一次 performTraversals 。
所以有人问为何在 run 方法里面还要 post 一次,如果在 run 方法里面直接执行 updateText 方法 ,那么 updateText 就会在第一个 performTraversals 之后就执行,而不是在第一帧绘制完成后才去执行,所以我们又 Post 了一次 。所以大概的处理步骤如下:
第一步:Activity.onCreate –> Activity.onStart –> Activity.onResume
第二步:ViewRootImpl.performTraversals –>Runnable
第三步:Runnable –> ViewRootImpl.performTraversals
第四步:ViewRootImpl.performTraversals –> init();
第五步:init();
l 禁止(避免)操作篇:
核心:少的对象创建,意味着少的GC操作。 杜绝引起内存溢出、内存抖动的操作行为;
使用枚举访问速度要比static变量慢4倍,枚举将造成大量的内存浪费;
l 禁止使用异步回调:
异步回调被执行的时间不确定,很有可能发生在activity已经被销毁之后,
这不仅仅很容易引起crash,还很容易发生内存泄露。
例如:context , Activity
对于某些不得不出现static引用context的情况,在onDestroy()方法中,解除Activity与static的绑定关系,
从而去除static对Activity的引用,使Context能够被回收;
l 避免在循环(for、while、listView - getView方法、onDraw)里创建对象:
对于onDraw中 Paint 我们可以这样优化
private Paint paint = new Paint();
public on Draw(){
paint.setColor(mBorderColor);
}
static生命周期过长,对于需要传递的对象,使用(Intent)和(Handler)
浮点数会比整型慢两倍
ScheduledExecutorService, handler.postDelayed, handler.postAtTime , handler.sendMessageDelayed , View.postDelayed, AlarmManager
l 避免加载过大图片。压缩或者使用对象池后再使用
l 避免使用递归
l 避免使用轮询
l 避免长周期内部类、匿名内部类长时间持有外部类对象导致相关资源无法释放。如:Handler, Thread , AsyncTask
l 避免使用三方库,不需要的东西需要剔除
l 避免使用注解框架,毕竟是反射
l 非必要情况下,少用抽象
l 避免频繁网络请求
访问server端时,建立连接本身比传输需要跟多的时间,如非必要,不要将一交互可以做的事情分成多次交互(这需要与Server端协调好)。有效管理Service 后台服务就相当于一个持续运行的Acitivity,如果开发的程序后台都会一个service不停的去服务器上更新数据,在不更新数据的时候就让它sleep,这种方式是非常耗电的,通常情况下,可以使用AlarmManager来定时启动服务。如下所示,第30分钟执行一次。
1. AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALAR M_SERVICE); 2. Intent intent = new Intent(context, MyService.class); 3. PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, 0); 4. long interval = DateUtils.MINUTE_IN_MILLIS * 30; 5. long firstWake = System.currentTimeMillis() + interval; 6. am.setRepeating(AlarmManager.RTC,firstWake, interval, pendingIntent);
l 优化操作建议篇:
l 当数据量在100以内时,使用ArrayMap代替HashMap
l 为了避免自动装箱,当数量在1000以下时,使用如下容器
a)SparseBoolMap <bool , obj>
b)SparseIntMap <int , obj>
c)SparseLongMap <long , obj>
d)LongSparseMap <long ,obj>
l 字符串拼接用StringBuilder或StringBuffer
//这种string第一次初始化的情况下,下面得效率更高
String str1 = "abc"+“def”+"hij";
//非并发情况 , StringBuilder效率更优
StringBuilder str2 = str3 + str1 + "builder" ;
//并发情况使用 StringBuffer
StringBuffer str2 = str1 + "buffer" ;
BufferedInputStream替代InputStream
BufferedReader替代Reader
BufferedReader替代BufferedInputStream.
png虽能提供无损的图片,但相对于JPEG过大。Webp是既保持png优点,又能减少图片大小的新型格式.
基本类似指:int , double , char等。
反复使用的变量,保存到本地成为临时变量活成员变量后进行操作。尤其是在循环中
例:多次比较目标时间和当前时间差。
例如: Object ob = new Object(); int value; if(i>0) value = ob.getVlaue(); 改写为:int value; if(i>0){ Object ob = new Object(); //用到时加载 value = ob.getVlaue(); }
a = new Object(); 当a不为空时,应改写为: a = null; a = new Object();
通常对于对象成员如此使用,局部变量不需要
this.object = null;
l 对bitmap进行恰当的操作:
读取图片之前先查看其大小: 1. BitmapFactory.Options opts = new BitmapFactory.Options(); 2. opts.inJustDecodeBounds = true; 3. Bitmap bitmap = BitmapFactory.decodeFile(imageFile, opts); 使用得到的图片原始宽高计算适合自己的smaplesize: 1. BitmapFactory.Options opts = new BitmapFactory.Options(); 2. opts.inSampleSize = 4 ;// 4就代表容量变为以前容量的1/4 Bitmap bitmap = BitmapFactory.decodeFile(imageFile, opts); 对于过时的Bitmap对象一定要及时recycle,并且把此对象赋值为null: 1. bitmap.recycle(); 2. bitmap = null;
l 布局用Java完成比XML快
<ViewStub android:id="@+id/network_error_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:layout="@layout/network_error" /> //非显示的转换ViewStub 获取 View viewStub = findViewById(R.id.network_error_layout); viewStub.setVisibility(View.VISIBLE); // ViewStub被展开后的布局所替换 networkErrorView = findViewById(R.id.network_error_layout); // 获取 展开后的布局
界面绘制尽量使用fragment代替activity,fragment根据情况使用hide与add方式,还是replace if (!showFragment.isAdded()) { // 先判断是否被add过 transaction.hide(currentFragment).add(R.id.fl_content, showFragment) .commitAllowingStateLoss(); // 隐藏当前的fragment,add下一个到Activity中 } else { // 隐藏 当前的fragment,显示下一个 transaction.hide(currentFragment).show(showFragment).commitAllowingStateLoss(); } this.currentFragment = showFragment;
<include layout="@layout/foot.xml" />
要求不高标准:非复杂结构布局,无Background,padding等属性,且子View数量较少 <merge xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <Button android:id="@+id/button" android:layout_width="match_parent" android:layout_height="@dimen/dp_40" android:layout_above="@+id/text"/> <TextView android:id="@+id/text" android:layout_width="match_parent" android:layout_height="@dimen/dp_40" android:layout_alignParentBottom="true" android:text="@string/app_name" /> </merge>
l 数据压缩:
传输数据经过压缩 目前大部门网站都支持GZIP压缩,所以在进行大数据量下载时,尽量使用GZIP方式下载,可以减少网络流量,一般是压缩前数据大小的30%左右。
1. HttpGet request = new HttpGet("http://example.com/gzipcontent"); 2. HttpResponse resp = new DefaultHttpClient().execute(request); 3. HttpEntity entity = response.getEntity(); 4. InputStream compressed = entity.getContent(); 5. InputStream rawData = new GZIPInputStream(compressed);
---------------希望对大家能有帮助,如果有不好的地方希望大家给予建议------------------