android 性能、内存优化

Android 设备一种移动设备,不管是内存还是CPU都受到限制,必然要考虑性能的设计。文章将从以下方面给出一些建议。

1.布局优化

减少布局文件之间的层级。首先要删除布局文件中无用的控件和层级,再者选择使用想能较低的控件。在选择布局控件时,LinearLayout/FrameLayout优先,RelativaLayout其次,最后是ViewGroup。还有在布局文件中采用<include> 标签、  <merge>标签、ViewStub。

a.  <include> 标签:将一个指定的布局文件加载到当前的布局文件,其他布局复用时需要时可以直接通过<include>复用。

b.  <merge>标签:<merge>标签一般是和<include> 标签一起使用的。假设现在当前布局是一个竖直方向的LinearLayout,抽取的<include> 标签内也是一个竖直方向的LinearLayout,无疑<include> 标签内的竖直方向的LinearLayout是多余的,通过<merge>标签替换,就可以出去多余的LinearLayout。

<merge xmlns:android="http://schemas.android.com/apk/res/android"
       android:layout_width="match_parent"
       android:layout_height="match_parent">

    <Button
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_marginTop="20dp"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_marginTop="20dp"
        />

</merge>

c.  ViewStub : viewStub集成了View轻量级,宽高都是0,本身不会参于任何布局和绘制,它会在需要的时候去加载。举例:在网络异常的时候一般有一个显示界面,正常情况下不会显示。如果在开始的时候把它加载进来,很有可能是用不到的,这时可以使用ViewStub.  

<ViewStub
        android:id="@+id/text_viewStub"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout=“@layout/hide_layout"/>

text_viewStub是ViewStub的id,hide_layout是隐藏布局的id,在需要加载隐藏布局的时候,我们可以在以下两种方法中任选其一

 ((ViewStub)findViewById(R.id.text_viewStub)).setVisibility(View.VISIBLE);

((ViewStub)findViewById(R.id.text_viewStub)).inflate();

ViewStub不支持<merge>标签

2. 绘制优化

在View的onDraw方法中避免执行大量的操作。一是在onDraw中避免大量创建临时对象,可以把这部分对象初始化在构造函数中,比如 Paint类,String 类,以免频繁触发 GC。二是在onDraw不要做耗时操作,也不能执行很多的循环操作。

3. 内存优化

内存泄露:在实际开发中,不在用到的对象因为被错误的引用导致无法回收的问题。

内存溢出 OOM(OutOfMemory):每个 APP 的堆(heap)内存大小有硬性限制,如果您的 APP 已达到堆内存限制,并尝试分配更多的内存,系统会抛出 OutOfMemoryError 。

内存泄露出现的几种常用方法:

a.静态类持有导致的内存泄露:

一般来说是当前的一个静态变量如Context、View、数组等,持有了一个Activity的引用,导致在回收Activity时回收失败,这种例子比较极端。

public class TextActivity extends Activity {

    private static View view;
    private static Context mContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recyclerview_layout);
        mContext = this;
        view = new View(this);
    }
}

b. 单例模式导致的内存泄露

最常应用的一个场景是我们写了一个单利模式,

和一个List<Activity>来管理所有Activity的退出问题。如果在finish当前界面的时候我们没有去主动调用单例模式删除当前的Activty实例,必然会造成内存泄露。在单例模式中持有单例模式的类的生命周期是和Application保持一致的。

public class ActivityManager {

    static ActivityManager mActivityManager;
    List<Activity> mActivities = new ArrayList<>();

    public static synchronized ActivityManager getInstance() {
        if (null == mActivityManager) {
            mActivityManager = new ActivityManager();
        }

        return mActivityManager;
    }

    public void addActivity(Activity activity) {
        if (null != activity) {
            mActivities.add(activity);
        }
    }


    public void removeActivty(Activity activity) {
        if (null != activity) {
            mActivities.remove(activity);
        }
    }
}
public class TextActivity extends Activity {

    private static View view;
    private static Context mContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recyclerview_layout);
        ActivityManager.getInstance().addActivity(this);
    }
    
    public void back(){
        finish();
    }

    //遗漏,造成内存溢出
    @Override
    protected void onDestroy() {
        super.onDestroy();
        ActivityManager.getInstance().removeActivty(this);
    }
}

c. 属性动画导致的内存泄露

在android3.0以后属性动画添加了一种无限循环的动画。如果在Activity中播放并且没有在onDestory中去停止动画,动画会一直播放下去。Activity中的View被动画持有,View又持有Activity.

ObjectAnimator animator;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recyclerview_layout);
        TextView mButton = (TextView) findViewById(R.id.tv_app_name);
        animator = ObjectAnimator.ofFloat(mButton, "rotation", 0, 360).setDuration(1000);
        animator.setRepeatMode(ValueAnimator.RESTART);
        animator.setRepeatCount(ValueAnimator.INFINITE);//无线循环
        animator.start();
    }


    //遗漏,造成内存溢出
    @Override
    protected void onDestroy() {
        super.onDestroy();
        animator.cancel();
    }
内存管理的工具:

1)LeakCanary监测内存泄露:https://github.com/square/leakcanary

2)Memory Monitor工具:Android Studio 中的 Android Monitor ,选择其中的 Memory,跟踪整个 APP 的内存变化情况

3)Heap Viewer 工具:实时查看App分配的内存大小和空闲内存大小,发现 Memory Leaks 。

详细使用教程:https://www.kancloud.cn/digest/itfootballprefermanc/100907

4)Allocation Tracker:追踪内存对象的来源,详细使用教程:https://www.kancloud.cn/digest/itfootballprefermanc/100908

内存优化的建议:

1)检查使用内存的数量,做一个阀值:

调用系统 getMemoryInfo() 查询,返回 一个ActivityManager.MemoryInfo 对象,它提供该设备当前存储器的状态信息,包括可用的存储器,总存储器,和低于该阈值存储器。

2)界面不可见时释放一些内存:

实现 ComponentCallbacks2 API 中 onTrimMemory()) ,当回调参数 level 为 TRIM_MEMORY_UI_HIDDEN ,是用户点击了Home键或者Back键退出应用,所有UI界面被隐藏,这时候应该释放一些不可见的时候非必须的资源。

3)谨慎使用服务

离开了 APP 还在运行服务是最糟糕的内存管理错误之一,当 APP 处在后台,我们应该停止服务,除非它需要运行的任务。我们可以使用 JobScheduler 替代实现,JobScheduler 把一些不是特别紧急的任务放到更合适的时机批量处理。如果必须使用一个服务,最佳方法是使用 IntentService ,限制服务寿命,所有请求处理完成后,IntentService 会自动停止。

4)使用一些优化后的数据容器

优化过数据的容器 SparseArray / SparseBooleanArray / LongSparseArray 代替 HashMap 等传统数据结构,通用 HashMap 的实现可以说是相当低效的内存,因为它需要为每个映射一个单独的条目对象。

5)不要过多的使用枚举,枚举占用的内存空间要比整形大。

6)减少使用静态变量,静态常量。

7)cursor关闭:如查询数据库的操作,使用到Cursor,也要对Cursor对象及时关闭。

8)监听器的注销:Android程序里面存在需要register与unregister的监听器,手动添加的listener,要及时删除这个listener。

9)删除剔除不需要的代码,可以使用ProGuard。

10)适当使用软引用和弱引用。

11)采用内存缓存和磁盘缓存。

12)帧动画使用注意尺寸的大小和图片的数量,避免高频的调用。

4. 响应速度优化和ANR日志分析

相应优化速度是避免在主线程中做耗时操作,可以将耗时操作放在线程中去执行,采用异步的方式执行耗时操作。如Activity启动时有太多事情处理,导致速度太慢会出现黑屏现象,甚至ANR。Activity是5秒钟无法响应屏幕触摸事件或键盘输入就会出现ANR,BroadCastReceiver是十秒钟。出现了ARN,系统会在/data/anr目录下创建一个文件traces.txt,可以对它分析,找出问题所在。在实际开发中,会出现的一种情况是在子线程与主线程中同时调用了持有同步锁的方法,导致ANR,需要注意。

5.ListView/GridView/RecyclerView优化

a. 采用ViewHolder。ViewHolder 的关键好处是缓存了显示数据的视图(View),加快了 UI 的响应速度。ViewHolder模式通过getView()方法返回的视图的标签(Tag)中存储一个数据结构,这个数据结构包含了指向我们要绑定数据的视图的引用,从而避免每次调用getView()的时候调用findViewById())。(在ListView/GridView重用缓存convertView传递给getView()方法来避免填充不必要的视图)

b. 在ListView/GridView中的getView/RecyclerView的onBindViewHolder方法中避免耗时操作,比如在要加载图片,可以通过异步的方法实现。

c. 根据列表的滑动状态来控制执行频率。监听控件的滑动状态,当控件滑动停止时,在发送新的请求加载图片,滑动过程中不处理。

d. 开启硬件加速来使ListView/GridView/RecyclerView滑动更流畅。android:hardwareAccelerated=“true”

6.Bitmap优化

Bitmap是内存消耗的大头,当使用时要及时回收,同时采用BitmapFactory.Options来加载所需要尺寸的图片。BitmapFactory.Options可以按照一定的采样率来加载缩小后的图片,降低内存,避免OOM。

7.线程优化

采用线程池,避免在程序中存在大量的Thread。线程池可以重用内容的线程,避免线程的创建和销毁带来的性能开销,同时线程池还能有效的控制线程的最大并发数,避免大量的线程因互相抢占资源从而阻塞。

=================================>

参考:1.http://wuxiaolong.me/2017/04/15/memory/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io

2.android 开发艺术与探索。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值