Android性能优化

          性能优化是一个大的范畴,曾问过一些学员在Android中如何做性能优化的,都不知道从哪开始说起,三三两两说出来的也很不全面。在此总结如下:

1.View的优化

     1)ListView的优化

  • 复用convertview , 历史的view对象

  • 减少子孩子查询的次数 viewholder

  • 异步加载数据(把图片缓存)

  • 条目多时分页加载数据

  • 加载时显示进度条让用户等待

  • Item的布局层次结构尽量简单,避免布局太深或者不必要的重绘

  • 避免在 getView 方法中做耗时的操作:
    例如加载本地 Image 需要载入内存以及解析 Bitmap ,都是比较耗时的操作,如果用户快速滑动listview,会因为getview逻辑过于复杂耗时而造成滑动卡顿现象。用户滑动时候不要加载图片,待滑动完成再加载,可以使用这个第三方库glide

  • 应该尽量避免 static 成员变量引用资源耗费过多的实例,比如 Context。

  • 尽量使用 getApplicationContext:如果为了满足需求下必须使用 Context 的话:Context 尽量使用 Application Context,因为Application 的 Context 的生命周期比较长,引用它不会出现内存泄露的问题

  • 在一些场景中,ScollView内会包含多个ListView,可以把listview的高度写死固定下来。
    由于ScollView在快速滑动过程中需要大量计算每一个listview的高度,阻塞了UI线程导致卡顿现象出现,如果我们每一个item的高度都是均匀的,可以通过计算把listview的高度确定下来,避免卡顿现象出现

  • 使用 RecycleView 代替listview:
    每个item内容的变动,listview都需要去调用notifyDataSetChanged来更新全部的item,太浪费性能了。RecycleView可以实现当个item的局部刷新,并且引入了增加和删除的动态效果,在性能上和定制上都有很大的改善

  • ListView 中元素避免半透明:
    半透明绘制需要大量乘法计算,在滑动时不停重绘会造成大量的计算,在比较差的机子上会比较卡。 在设计上能不半透明就不不半透明。实在要弄就把在滑动的时候把半透明设置成不透明,滑动完再重新设置成半透明。

  • 尽量开启硬件加速:
    硬件加速提升巨大,避免使用一些不支持的函数导致含泪关闭某个地方的硬件加速。当然这一条不只是对 ListView。

  2)viewPager的优化

  • viewpager会默认加载左右俩个页面,有时候我们并不想看,会浪费用户的流量,可以在setOnPageChangeListener的onPageSelected的方法里选中哪个页面,初始化哪个页面

  • 由于viewpager会默认销毁第三页面,可以强制让viewpager加载所有的页面pagerView.setOffscreenPageLimit(pageCount);,但是如果页面多的话就不能这样干了

  • 可以定义一个集合将页面缓存起来,在destroyItem的时候保存起来,在instantiateItem读取集合,有就用,没有的话再创建,就像listview的convertView似的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class HomeAdapter extends PagerAdapter {
     // 当前viewPager里面有多少个条目
     LinkedList<ImageView> convertView= new LinkedList<ImageView>();
     @Override
     public int getCount() {
         return  Integer.MAX_VALUE;
     }
     /* 判断返回的对象和 加载view对象的关系 */
     @Override
     public boolean isViewFromObject(View arg0, Object arg1) {
         return arg0 == arg1;
     }
     @Override
     public void destroyItem(ViewGroup container, int position, Object object) {
         ImageView view=(ImageView) object;
         convertView.add(view); // 把移除的对象 添加到缓存集合中
         container.removeView(view);
     }
     @Override
     public Object instantiateItem(ViewGroup container, int position) {
         ImageView view;
         if (convertView.size()> 0 ){
             view=convertView.remove( 0 );
         } else {
             view= new ImageView(UiUtils.getContext());
         }
         bitmapUtils.display(view, HttpHelper.URL + "image?name="
                 + datas.get(index));
         container.addView(view); // 加载的view对象
         return view; // 返回的对象
     }
}

  

  

2.UI的优化

 布局的优化

  • 尽量重用一个布局文件,使用include标签,多个相同的布局可以复用

  • 减少一个布局的不必要节点

  • 尽量使用view自身的参数,例如:Button,有一个可以把图绘制在左边的参数:android:drawableLeft

  • 使用< ViewStub />标签来加载一些不常用的布局;使用< merge />标签减少布局的嵌套层次


3.图片的优化

  • 使用异步加载图片

  • 使用内存缓存

  • 使用磁盘缓存

  • 要考虑图片是否需要压缩

3.内存的优化

  •  回收已经使用的资源,比如游标cursor 、I/O、Bitmap(close并且引用置为null)

  •   合理的使用缓存,比如图片是很耗内存的,使用lru缓存图片和压缩

  • 合理设置变量的作用范围

  • 节制的使用服务,后台任务运行完,即使它不执行任何操作,服务也会一直运行,这些是十分消耗内存的,可以用intentservice

  • 当界面不可见时释放内存,在activity的onTrimMemory方法里与ui的相关资源,在onstop里释放与组件相关的资源

  • 合理的使用多进程,如果后台任务和前台界面是相互独立在,可以在组件标签下写process,这样这个组建就在另一个进程里了。而服务的话更倾向于开启自己所依赖的进城,而那个进程可能很多东西都不需要,比如ui

  • 使用线程池、对象池

  • Bitmap对象在不使用时,应该先调用recycle()释放内存,然后才它设置为null。

 

4.代码优化

      1.避免创建不必要的对象

              这部分就是是细微的优化,但是细微多了也就内存节约了

              任何一个Java类,包括内部类、匿名类,都要占用大概500字节的内存空间。

              任何一个类的实例要消耗12-16字节的内存开支,因此频繁创建实例也是会一定程序上影响内存的,所以要避免创建不必要的对象

      2.字符串拼接优先考虑使用StringBuffer或者StringBuilder来进行拼接

              如果有一个需要拼接的字符串,那么可以优先考虑使用StringBuffer或者StringBuilder来进行拼接,而不是加号连接符,因为使用加号连接符会创建多余的对象,拼接的  

              字符串越长,加号连接符的性能越低

      3.尽量使用基本数据类来代替封装数据类型

              尽量使用基本数据类来代替封装数据类型,int比Integer要更加高效,其它数据类型也是一样

      4.使用静态

              1.使用枚举通常会比使用静态常量要消耗两倍以上的内存,在Android开发当中应当尽可能地不使用枚举。

              2. 如果你并不需要访问一个对象中的某些字段,只是想调用它的某个方法来去完成一项通用的功能,那么可以将这个方法设置成静态方法,这会让调用的速度提升

                 15%-20%,同时也不用为了调用这个方法而去专门创建对象了,这样还满足了上面的一条原则。另外这也是一种好的编程习惯,因为我们可以放心地调用静态方法,

                 而不用担心调用这个方法后是否会改变对象的状态(静态方法内无法访问非静态字段)

              3.对常量使用static final修饰

      5.使用增强for语法

               for (String string : args) {
            
               }

      6.多使用系统封装好的API

           比如:indexOf(),System.arraycopy()

      7.尽量使用drawable对象保存图片而不是bitmap

             drawable = Drawable.createFromStream(new URL(url).openStream(), "image.png");

             ui组件需要用到的图片是apk包自带的,那么一律用setImageResource或者setBackgroundResource,而不要根据resourceid 

             注意:get(getResources(),R.drawable.btn_achievement_normal)该方法通过resid转换为drawable,需要考虑回收的问题,

             如果drawable是对象私有对象,在对象销毁前是肯定不会释放内存的。


      8.高效地利用线程

                1.在后台取消一些线程中的动作

                   我们知道App运行过程中所有的操作都默认在主线程(UI线程)中进行的,这样App的响应速度就会受到影响。会导致程序陷入卡顿、死掉甚至会发生系统错误。

                   为 了加快响应速度,需要把费时的操作(比如网络请求、数据库操作或者复杂的计算)从主线程移动到一个单独的线程中。最高效的方式就是在类这一级完成

                   这项操作,可以使用AsyncTask或者IntentService来创建后台操作。如果选择使用IntentService,它会在需要的时候启动起 来,然后通过一个工作线程来处理请

                   求(Intent)。

                2. 使用IntentService时需要注意以下几点限制:

                    1. 这个类不要给UI传递信息,如果要向用户展示处理结果信息请用Activity;

                    2.每次只能处理一个请求;

                    3.每一个处理请求过程都不能中断;

               3.保持响应不发生ANR

                  从UI线程中移除费时操作这个方式还可以防止用户操作出现系统不响应(ANR)对话框。需要做的就是继承AsyncTask来创建一个后台工作线程,

                  并实现doInBackground()方法。

                  还有一种方式就是自己创建一个Thread类或者HandlerThread类。需要注意这样也会使App变慢,因为默认的线程优先级和主线程的优先级是一样的,

                  除非你明确设定线程的优先级。

               4.在线程中初始化查询操作

                  当查询操作正在后台处理时,展示数据也不是即时的,但是你可以使用CursorLoader对象来加快速度,这个操作可以使Activity和用户之间的互动不受影响。

                  使用这个对象后,你的App会为ContentProvider初始化一个独立的后台线程进行查询,当查询结束后就会给调用查询的Activity返回结果。

               5.其它需要注意的方面

                  1.使用StrictMode来检查UI线程中可能潜在的费时操作;

                  2.使用一些特殊的工具如Safe.ijiami、Systrace或者Traceview来寻找在你的应用中的瓶颈;

                  3.用进度条向用户展示操作进度;

                  4.如果初始化操作很费时,请展示一个欢迎界面。

5.电量优化

如果应用很费电,请不要责怪用户卸载了你的应用。对于电池使用来说,主要费电情况如下:

  • 更新数据时经常唤醒程序;

  • 用EDGE或者3G来传递数据;

  • 文本数据转换,进行非JIT正则表达式操作。

6.优化网络

  • 如果没有网络连接,请让你的应用跳过网络操作;只在有网络连接并且无漫游的情况下更新数据;

  • 选择兼容的数据格式,把含有文本数据和二进制数据的请求全部转化成二进制数据格式请求;

  • 使用高效的转换工具,多考虑使用流式转换工具,少用树形的转换工具;

  • 为了更快的用户体验,请减少重复访问服务器的操作;

  • 如果可以的话,请使用framework的GZIP库来压缩文本数据以高效使用CPU资源。

7.优化应用在前端的工作

  • 如果考虑使用wakelocks,尽量设置为最小的级别;

  • 为了防止潜在的bug导致的电量消耗,请明确指定超时时间;

  • 启用 android:keepScreenOn属性;

  • 除了系统的GC操作,多考虑手动回收Java对象,比如XmlPullParserFactory和BitmapFactory。还有正则表达式的Matcher.reset(newString)操作、StringBuilder.setLength(0)操作;

  • 要注意同步的问题,尽管在主线程中是安全的;

  • 在Listview中要多采用重复利用策略;

  • 如果允许的话多使用粗略的网络定位而不用GPS,对比一下GPS需要1mAh(25s * 140 mA),而一般网络只用0.1mAh(2s * 180mA);

  • 确保注销GPS的位置更新操作,因为这个更新操作在onPause()中也是会继续的。当所有的应用都注销了这个操作,用户可以在系统设置中重新启用GPS而不浪费电量;

  • 请考虑在大量数理运算中使用低精度变量并在用DisplayMetrics进行DPI任务时缓存变量值;

8.优化工作在前台的应用

  • 请确保service生命周期都是短暂的,因为每个进程都需要2MB的内存,而在前台程序需要内存时也会重新启动;

  • 保持内存的使用量不要太大;

  • 如果要应用每30分钟更新一次,请在设备处于唤醒状态下进行;

  • Service在pull或者sleep状态都是不好的,这就是为什么在服务结束时要使用AlarmManager或者配置属性stopSelf()的原因。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值