关于android:Android-性能优化

7次阅读

共计 4530 个字符,预计需要花费 12 分钟才能阅读完成。

Android 性能优化

一:介绍
Android 性能优化次要从以下 4 个方面:
1. 稳固(内存溢出,解体)
2. 晦涩(卡顿)
3. 损耗(耗电,流量)
4. 安装包(APK 瘦身)
影响稳定性的因素有很多,比方内存应用不合理,代码异样场景考虑不周全,代码逻辑不合理等,都会对利用的稳定性造成影响。
最常见的两个场景:Crash 和 ANR, 这两个谬误将会使得程序无奈失常应用。
1. 所以做好 Crash 全局监控,解决闪退同时把解体信息、异样信息收集记录起来,以便后续剖析;
2. 正当应用主线程解决业务,不要在主线程中做耗时操作,避免 ANR 程序无响应产生。
内存是 Android 运行性能至关重要的一项指标,每个过程能应用的内存是无限的。不合理的应用内存会导致频繁的 GC、甚至产生 OOM,过多 GC 会导致 App 卡顿,而内存透露或者内存抖动都能够导致 OOM,这是无奈承受的。

二:稳固(内存溢出,解体)
内存泄露:长时间持有对象的援用,会导致内存占用过大,导致利用卡顿;
指的是那些程序不再应用的对象无奈被 GC 辨认,这样就导致这个对象始终留在内存当中,占用了没来就不多的内存空间。
1. 单例模式引起的内存泄露
因为单例模式的生命周期和 app 生命周期统一

public class Singleton {
    private Context context;
    private static  Singleton mInstance;
    private Singleton(Context context){this.context=context;}
    public static Singleton getInstance(Context context){if (mInstance==null){mInstance=new Singleton(context);
        }
        return mInstance;

    }
}
// 这是一个单例模式的规范写法,外表上看没有任何问题,然而仔细的同学会发现,构建该单例的一个实例时须要传入一个 Context,此时传入的 Context 就十分要害,如果此时传入的是 Activity,因为 Context 会被创立的实例始终持有,当 Activity 进入后盾或者开启设置外面的不保留流动时,Activity 会被销毁,然而单例持有它的 Context 援用,Activity 又没法销毁,导致了内存透露。// 如果此时传入的 Context 是 ApplicationContext,因为 ApplicationContext 的生命周期是和 app 统一的,不会导致内存透露。然而咱们不能指望应用这个单例的用户始终传入冀望的 Context,因而须要对这个单例设计进行调整,能够在构造函数中对 mContext 赋值改为 this.mContext = context.getApplicationContext; 当然,也能够间接不让用户传入 context。

当咱们在 Activity 外面应用这个的时候,把咱们 Acitivty 的 context 传进去,那么,这个单例就持有这个 Activity 的援用,当这个 Activity 没有用了,须要销毁的时候,因为这个单例还持有 Activity 的援用,所以无奈 GC 回收,所以就呈现了内存透露,也就是生命周期长的持有了生命周期短的援用,造成了内存透露。

2. 非动态外部类持有外部类的援用导致内存泄露
下图是动态外部类和非动态外部类的比拟

非动态外部类他会持有他外部类的援用,从图咱们能够看到非动态外部类的生命周期可能比外部类更长,这就是二楼的状况统一了,如果非动态外部类的周明周期长于外部类,在加上主动持有外部类的强援用,我的乖乖,想不透露都难啊。

public class TestActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        new MyAscnyTask().execute();
    }
// 改了这里 留神一下 static
   static  class MyAscnyTask extends AsyncTask<Void, Integer, String>{
        @Override
        protected String doInBackground(Void... params) {
            try {Thread.sleep(100000);
            } catch (InterruptedException e) {e.printStackTrace();
            }
            return "";
        }
    }
}

3.Handler 引起的内存泄露。
咱们晓得,Handler、Message、MessageQueue 是互相关联在一起的,Handler 通过发送音讯 Message 与主线程进行交互,如果 Handler 发送的音讯 Message 尚未被解决,该 Message 及发送它的 Handler 对象将被 MessageQueue 始终持有,这样就可能会导致 Handler 无奈被回收。

将 Handler 申明为动态外部类,就不会持有外部类 SecondActivity 的援用,其生命周期就和外部类无关,如果 Handler 外面须要 context 的话,能够通过弱援用形式援用外部类。
其实 Handler 也是非动态外部类的问题引起的,会持有 Activity 的援用,发送提早音讯,但 activity 曾经敞开,导致始终持有该 activity 援用没法解决音讯

public class ScanActivity extends AppCompatActivity {
// 改成动态外部类,避免内存泄露
    private static Handler mHandler=new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {super.handleMessage(msg);
            String aa= (String) msg.obj;
            Log.d("aa", aa);
            System.out.println(aa);
        }
    };
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_four);
        Message message=Message.obtain();
        message.obj="你好";
      mHandler.sendMessage(message);
       mHandler.sendMessageDelayed(message,10000);// 这样要发送提早音讯的话,同时跳转到其余页面,会导致音讯队列中始终持有该 Activity 援用导致内存泄露
       // 所有改良形式是 Handler 外部类改成动态外部类
    }

}

4. 资源对象没有敞开引起内存泄露
在 android 中,资源性对象比方 Cursor、File、Bitmap、视频等,零碎都用了一些缓冲技术,在应用这些资源的时候,如果咱们确保本人不再应用这些资源了,要及时敞开,否则可能引起内存透露。因为有些操作不仅仅只是波及到 Dalvik 虚拟机,还波及到底层 C /C++ 等的内存治理,不能齐全寄心愿虚拟机帮咱们实现内存治理
5. 注册 / 反注册未成对应用引起的内存透露

在 andorid 开发中,咱们常常会在 Activity 的 onCreate 中注册播送接受器、EventBus 等,如果遗记成对的应用反注册,可能会引起内存透露。开发过程中应该养成良好的相干,在 onCreate 或 onResume 中注册,要记得相应的在 onDestroy 或 onPause 中反注册

内存透露就是持有对象的援用,那么援用分为强援用,软援用,弱援用,虚援用
强援用
咱们平时不做非凡解决的个别都是强援用,如果一个对象具备强援用,GC 宁肯 OOM 也绝不会回收它。看出多强硬了吧。

软援用 (SoftReference)
如果内存空间足够,GC 就不会回收它,如果内存空间有余了,就会回收这些对象的内存。

弱援用(WeakReference)
弱援用要比软援用, 更弱一个级别,内存不够要回收他,GC 的时候不论内存够不够也要回收他,几乎是弱的一匹。不过 GC 是一个优先级很低的线程,也不是太频繁进行,所以弱援用的生存还过得去,没那么胆战心惊。

虚援用
用的甚少,我没有用过,如果想理解的敌人,能够自行谷歌百度。

内存泄露剖析工具
1.leakcanary 傻瓜式操作,哪里有透露主动给你显示进去,很间接很暴力。(待钻研)
2. 咱们平时也要多应用 Memory Monitor 进行内存监控,这个剖析就有些难度了,能够上网搜一下具体怎么应用。

三:快(晦涩,卡顿)
1. 布局优化
屏幕上的某个像素在同一帧的工夫内被绘制了屡次。在多层次的 UI 构造外面,如果不可见的 UI 也在做绘制的操作,这就会导致某些像素区域被绘制了屡次。这就节约大量的 CPU 以及 GPU 资源。

  • 如果父控件有色彩,也是本人须要的色彩,那么就不用在子控件加背景色彩
  • 如果每个自控件的色彩不太一样,而且能够齐全笼罩父控件,那么就不须要再父控件上加背景色彩
  • 尽量减少不必要的嵌套
  • 能用 LinearLayout 和 FrameLayout,就不要用 RelativeLayout,因为 RelativeLayout 控件绝对比较复杂,测绘也想要耗时。
  • 应用 include 和 merge 减少复用,缩小层级
  • ViewStub 按需加载,更加轻便
  • 简单界面可抉择 ConstraintLayout,可无效缩小层级
    2. 绘制优化
    这个是 Android 的渲染机制造成的,Android 零碎每隔 16ms 收回 VSYNC 信号,触发对 UI 进行渲染,然而渲染未必胜利,如果胜利了那么代表一切顺利,然而失败了可能就要延误工夫,或者间接跳过去,给人视觉上的体现,就是要么卡了一会,要么跳帧。
    View 的绘制频率保障 60fps 是最佳的,这就要求每帧绘制工夫不超过 16ms(16ms = 1000/60),尽管程序很难保障 16ms 这个工夫,然而尽量升高 onDraw 办法中的复杂度总是切实有效的。

那么 ondraw()办法中绘制
onDraw 中不要创立新的部分对象
onDraw 办法中不要做耗时的工作

四:耗电优化
在定位精度要求不高的状况下,应用 wifi 或挪动网络进行定位,没有必要开启 GPS 定位。

先验证网络的可用性,在发送网络申请,比方,当用户处于 2G 状态下,而此时的操作是查看一张大图,下载下来可能都 200 多 K 甚至更大,咱们没必要去发送这个申请,让用户始终期待那个菊花吧。

适当的做本地缓存,防止频繁申请网络数据,这里,说起来容易,做起来并非三刀两斧就能搞定,要配合良好的缓存策略,辨别哪些是一段时间不会变更的,哪些是相对不能缓存的很重要。

五:安装包(Apk 瘦身)
1.so 库的优化
大多数状况下只须要反对 armabi 与 x86 的架构即可。如果 非必须,能够思考拿掉 x86 的局部


ndk     {
            // 设置反对的 so 库架构
            abiFilters "armeabi"
        }

2. 去除无用资源

END: 前路漫漫

正文完
 0