浏览五分钟,每日十点,和您一起一生学习,这里是程序员Android

文章转载网络
原文地址如下:https://juejin.im/post/5d072d...

1.OOM和解体优化

1.2 ANR优化

  • ANR的产生须要满足三个条件

    • 主线程:只有应用程序过程的主线程响应超时才会产生ANR;
    • 超时工夫:产生ANR的上下文不同,超时工夫也会不同,但只有在这个工夫下限内没有响应就会ANR;
    • 输出事件/特定操作:输出事件是指按键、触屏等设施输出事件,特定操作是指BroadcastReceiver和Service的生命周期中的各个函数,产生ANR的上下文不同,导致ANR的起因也会不同;
  • ANR优化具体措施

    • 将所有耗时操作,比方拜访网络,Socket通信,查问大量SQL 语句,简单逻辑计算等都放在子线程中去,然 后通过handler.sendMessage、runonUIThread、AsyncTask 等形式更新UI。无论如何都要确保用户界面作的晦涩 度。如果耗时操作须要让用户期待,那么能够在界面上显示度条。
    • 应用AsyncTask解决耗时IO操作。在一些同步的操作主线程有可能被锁,须要期待其余线程开释相应锁能力继续执行,这样会有肯定的ANR危险,对于这种状况有时也能够用异步线程来执行相应的逻辑。另外,要防止死锁的产生。
    • 应用Handler解决工作线程后果,而不是应用Thread.wait()或者Thread.sleep()来阻塞主线程。
    • Activity的onCreate和onResume回调中尽量避免耗时的代码
    • BroadcastReceiver中onReceive代码也要尽量减少耗时,倡议应用IntentService解决。
    • 各个组件的生命周期函数都不应该有太耗时的操作,即便对于后盾Service或者ContentProvider来讲,利用在后盾运行时候其onCreate()时候不会有用户输出引起事件无响应ANR,但其执行工夫过长也会引起Service的ANR和ContentProvider的ANR

2.内存透露优化

  • 内存检测第一种:代码形式获取内存

    /**
 */private void initMemoryInfo() {    ActivityManager activityManager = (ActivityManager) Utils.getApp()            .getSystemService(Context.ACTIVITY_SERVICE);    ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();    if (activityManager != null) {        activityManager.getMemoryInfo(memoryInfo);        LogUtils.d("totalMem=" + memoryInfo.totalMem + ",availMem=" + memoryInfo.availMem);        if (!memoryInfo.lowMemory) {            // 运行在低内存环境        }    }}```
  • 内存检测第二种:leakcanary工具

    • LeakCanary的原理是监控每个activity,在activity ondestory后,在后盾线程检测援用,而后过一段时间进行gc,gc后如果援用还在,那么dump出内存堆栈,并解析进行可视化显示。

2.0 动画资源未开释

  • 问题代码

    public class LeakActivity extends AppCompatActivity {    private TextView textView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_leak);        textView = (TextView)findViewById(R.id.text_view);        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(textView,"rotation",0,360);        objectAnimator.setRepeatCount(ValueAnimator.INFINITE);        objectAnimator.start();    }}
  • 解决办法

    • 在属性动画中有一类有限循环动画,如果在Activity中播放这类动画并且在onDestroy中去进行动画,那么这个动画将会始终播放上来,这时候Activity会被View所持有,从而导致Activity无奈被开释。解决此类问题则是须要早Activity中onDestroy去去调用objectAnimator.cancel()来进行动画。
    @Overrideprotected void onDestroy() {    super.onDestroy();    mAnimator.cancel();}

2.1 谬误应用单利

  • 在开发中单例常常须要持有Context对象,如果持有的Context对象生命周期与单例生命周期更短时,或导致Context无奈被开释回收,则有可能造成内存透露。比方:在一个Activity中调用的,而后敞开该Activity则会呈现内存透露。
  • 解决办法:

    • 要保障Context和AppLication的生命周期一样,批改后代码如下:
    • this.mContext = context.getApplicationContext();
    • 1、如果此时传入的是 Application 的 Context,因为 Application 的生命周期就是整个利用的生命周期,所以这将没有任何问题。
    • 2、如果此时传入的是 Activity 的 Context,当这个 Context 所对应的 Activity 退出时,因为该 Context 的援用被单例对象所持有,其生命周期等于整个应用程序的生命周期,所以以后 Activity 退出时它的内存并不会被回收,这就造成透露了。

2.2 谬误应用动态变量

  • 应用静态方法是非常不便的。然而创立的对象,倡议不要全局化,全局化的变量必须加上static。全局化后的变量或者对象会导致内存透露!
  • 起因剖析

    • 这里外部类AClass隐式的持有外部类Activity的援用,而在Activity的onCreate办法中调用了。这样AClass就会在Activity创立的时候是有了他的援用,而AClass是动态类型的不会被垃圾回收,Activity在执行onDestory办法的时候因为被AClass持有了援用而无奈被回收,所以这样Activity就总是被AClass持有而无奈回收造成内存泄露。

2.3 handler内存透露

  • 造成内存透露起因剖析

    • 通过外部类的形式创立mHandler对象,此时mHandler会隐式地持有一个外部类对象援用这里就是MainActivity,当执行postDelayed办法时,该办法会将你的Handler装入一个Message,并把这条Message推到MessageQueue中,MessageQueue是在一个Looper线程中一直轮询解决音讯,那么当这个Activity退出时音讯队列中还有未解决的音讯或者正在解决音讯,而音讯队列中的Message持有mHandler实例的援用,mHandler又持有Activity的援用,所以导致该Activity的内存资源无奈及时回收,引发内存透露。
  • 解决Handler内存泄露次要2点

    • 有延时音讯,要在Activity销毁的时候移除Messages监听
    • 匿名外部类导致的泄露改为匿名动态外部类,并且对上下文或者Activity应用弱援用。

2.4 线程造成内存透露

  • 早期间的时候解决耗时操作少数都是采纳Thread+Handler的形式,起初逐渐被AsyncTask取代,直到现在采纳RxJava的形式来解决异步。
  • 造成内存透露起因剖析

    • 在解决一个比拟耗时的操作时,可能还没解决完结MainActivity就执行了退出操作,然而此时AsyncTask仍然持有对MainActivity的援用就会导致MainActivity无奈开释回收引发内存透露。
  • 解决办法

    • 在应用AsyncTask时,在Activity销毁时候也应该勾销相应的工作AsyncTask.cancel()办法,防止工作在后盾执行浪费资源,进而防止内存透露的产生。

2.5 非动态外部类

  • 非动态外部类创立动态实例造成的内存透露。有的时候咱们可能会在启动频繁的Activity中,为了防止反复创立雷同的数据资源,可能会呈现这种写法。
  • 问题代码

    private static TestResource mResource = null;@Overrideprotected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);    if(mResource == null){        mResource = new TestResource();    }}class TestResource {     //外面代码援用上下文,Activity.this会导致内存透露}
  • 解决办法

    • 将该外部类设为动态外部类或将该外部类抽取进去封装成一个单例,如果须要应用Context,请依照下面举荐的应用Application 的 Context。
  • 剖析问题

    • 这样就在Activity外部创立了一个非动态外部类的单例,每次启动Activity时都会应用该单例的数据,这样尽管防止了资源的反复创立,不过这种写法却会造成内存透露,因为非动态外部类默认会持有外部类的援用,而该非动态外部类又创立了一个动态的实例,该实例的生命周期和利用的一样长,这就导致了该动态实例始终会持有该Activity的援用,导致Activity的内存资源不能失常回收。

2.6 未移除监听

  • 问题代码
    //add监听,放到汇合外面    tv.getViewTreeObserver().addOnWindowFocusChangeListener(new ViewTreeObserver.OnWindowFocusChangeListener() {        @Override        public void onWindowFocusChanged(boolean b) {            //监听view的加载,view加载进去的时候,计算他的宽低等。        }    });
  • 解决办法
    //计算完后,肯定要移除这个监听    tv.getViewTreeObserver().removeOnWindowFocusChangeListener(this);    
  • 注意事项:

    tv.setOnClickListener();//监听执行完回收对象,不必思考内存透露tv.getViewTreeObserver().addOnWindowFocusChangeListene,add监听,放到汇合外面,须要思考内存透露

2.7 持有activity援用

2.8 资源未敞开

  • 在应用IO、File流或者Sqlite、Cursor等资源时要及时敞开。这些资源在进行读写操作时通常都应用了缓冲,如果及时不敞开,这些缓冲对象就会始终被占用而得不到开释,以至产生内存泄露。因而咱们在不须要应用它们的时候就及时敞开,以便缓冲能及时失去开释,从而防止内存泄露。
  • BroadcastReceiver,ContentObserver,FileObserver,Cursor,Callback等在 Activity onDestroy 或者某类生命周期完结之后肯定要 unregister 或者 close 掉,否则这个 Activity 类会被 system 强援用,不会被内存回收。值得注意的是,敞开的语句必须在finally中进行敞开,否则有可能因为异样未敞开资源,以致activity透露。

2.9 其余起因

  • 动态汇合使用不当导致的内存透露

    • 有时候咱们须要把一些对象退出到汇合容器(例如ArrayList)中,当不再须要当中某些对象时,如果不把该对象的援用从汇合中清理掉,也会使得GC无奈回收该对象。如果汇合是static类型的话,那内存透露状况就会更为严重。因而,当不再须要某对象时,须要被动将之从汇合中移除。
  • 不须要用的监听未移除会产生内存泄露

    • 问题代码

      //add监听,放到汇合外面tv.getViewTreeObserver().addOnWindowFocusChangeListener(new ViewTreeObserver.OnWindowFocusChangeListener() {    @Override    public void onWindowFocusChanged(boolean b) {        //监听view的加载,view加载进去的时候,计算他的宽低等。    }});
    • 解决办法

      //计算完后,肯定要移除这个监听tv.getViewTreeObserver().removeOnWindowFocusChangeListener(this);
    • 注意事项:

      tv.setOnClickListener();//监听执行完回收对象,不必思考内存透露tv.getViewTreeObserver().addOnWindowFocusChangeListene,add监听,放到汇合外面,须要思考内存透露

3.布局优化

3.1 include优化

  • 重用布局文件

    • 标签能够容许在一个布局当中引入另一个布局,那么比如说咱们程序的所有界面都有一个公共的局部,这个时候最好的做法就是将这个公共的局部提取到一个独立的布局中,而后每个界面的布局文件当中来援用这个公共的布局。
    • 如果咱们要在标签中覆写layout属性,必须要将layout_width和layout_height这两个属性也进行覆写,否则覆写成果将不会失效。
    • 标签是作为标签的一种辅助扩大来应用的,它的次要作用是为了避免在援用布局文件时援用文件时产生多余的布局嵌套。布局嵌套越多,解析起来就越耗时,性能就越差。因而编写布局文件时应该让嵌套的层数越少越好。
    • 举例:比方在LinearLayout里边应用一个布局。里边又有一个LinearLayout,那么其实就存在了多余的布局嵌套,应用merge能够解决这个问题。

3.2 ViewStub优化

  • 仅在须要时才加载布局[ViewStub]

    • 某个布局当中的元素不是一起显示进去的,一般状况下只显示局部罕用的元素,而那些不罕用的元素只有在用户进行特定操作时才会显示进去。
    • 举例:填信息时不是须要全副填的,有一个增加更多字段的选项,当用户须要增加其余信息的时候,才将另外的元素显示到界面上。用VISIBLE性能体现个别,能够用ViewStub。
    • ViewStub也是View的一种,然而没有大小,没有绘制性能,也不参加布局,资源耗费非常低,能够认为齐全不影响性能。
    • ViewStub所加载的布局是不能够应用标签的,因而这有可能导致加载进去进去的布局存在着多余的嵌套构造。
  • 自定义全局的状态管理器【充沛应用ViewStub】

    • 针对多状态,有数据,空数据,加载失败,加载异样,网络异样等。针对空数据,加载失败,异样应用viewStub布局,一键设置自定义布局,也是优化的一种。
    • 我的项目地址:

3.3 merge优化

  • 视图层级

    • 这个标签在UI的构造优化中起着十分重要的作用,它能够删减多余的层级,优化UI。然而就有一点不好,无奈预览布局成果!

3.4 其余倡议

  • 缩小太多重叠的背景(overdraw)

    • 这个问题其实最容易解决,倡议就是查看你在布局和代码中设置的背景,有些背景是暗藏在底下的,它永远不可能显示进去,这种没必要的背景肯定要移除,因为它很可能会重大影响到app的性能。如果采纳的是selector的背景,将normal状态的color设置为”@android:color/transparent”,也同样能够解决问题。
  • 防止简单的Layout层级

    • 这里的倡议比拟多一些,首先举荐应用Android提供的布局工具Hierarchy Viewer来检查和优化布局。第一个倡议是:如果嵌套的线性布局加深了布局档次,能够应用绝对布局来取代。第二个倡议是:用标签来合并布局。第三个倡议是:用标签来重用布局,抽取通用的布局能够让布局的逻辑更清晰明了。记住,这些倡议的最终目标都是使得你的Layout在Hierarchy Viewer里变得宽而浅,而不是窄而深。
    • 总结:能够思考多应用merge和include,ViewStub。尽量使布局浅平,根布局尽量少应用RelactivityLayout,因为RelactivityLayout每次须要测量2次。

4.代码优化

  • 都是一些微优化,在性能方面看不出有什么显著的晋升的。应用适合的算法和数据结构是优化程序性能的最次要伎俩。

4.1 倡议应用lint查看去除有效代码

  • lint去除有效资源和代码

    • 如何检测哪些图片未被应用

      • 点击菜单栏 Analyze -> Run Inspection by Name -> unused resources -> Moudule ‘app’ -> OK,这样会搜进去哪些未被应用到未应用到xml和图片,如下:
    • 如何检测哪些有效代码

      • 应用Android Studio的Lint,步骤:点击菜单栏 Analyze -> Run Inspection by Name -> unused declaration -> Moudule ‘app’ -> OK

4.2 代码标准优化

  • 防止创立不必要的对象 不必要的对象应该防止创立:

    • 如果有须要拼接的字符串,那么能够优先思考应用StringBuffer或者StringBuilder来进行拼接,而不是加号连接符,因为应用加号连接符会创立多余的对象,拼接的字符串越长,加号连接符的性能越低。
    • 当一个办法的返回值是String的时候,通常须要去判断一下这个String的作用是什么,如果明确晓得调用方会将返回的String再进行拼接操作的话,能够思考返回一个StringBuffer对象来代替,因为这样能够将一个对象的援用进行返回,而返回String的话就是创立了一个短生命周期的长期对象。
    • 尽可能地少创立长期对象,越少的对象意味着越少的GC操作。
    • nDraw办法外面不要执行对象的创立
  • 动态优于形象

    • 如果你并不需要拜访一个对系那个中的某些字段,只是想调用它的某些办法来去实现一项通用的性能,那么能够将这个办法设置成静态方法,调用速度晋升15%-20%,同时也不必为了调用这个办法去专门创建对象了,也不必放心调用这个办法后是否会扭转对象的状态(静态方法无法访问非动态字段)。
  • 对常量应用static final修饰符

    • static int intVal = 42; static String strVal = "Hello, world!";
    • 编译器会为下面的代码生成一个初始办法,称为办法,该办法会在定义类第一次被应用的时候调用。这个办法会将42的值赋值到intVal当中,从字符串常量表中提取一个援用赋值到strVal上。当赋值实现后,咱们就能够通过字段搜查的形式去拜访具体的值了。
    • final进行优化:
    • static final int intVal = 42; static final String strVal = "Hello, world!";
    • 这样,定义类就不须要办法了,因为所有的常量都会在dex文件的初始化器当中进行初始化。当咱们调用intVal时能够间接指向42的值,而调用strVal会用一种绝对轻量级的字符串常量形式,而不是字段搜查的形式。
    • 这种优化形式只对根本数据类型以及String类型的常量无效,对于其余数据类型的常量是有效的。
  • 在没有非凡起因的状况下,尽量应用根本数据类型来代替封装数据类型,int比Integer要更加无效,其它数据类型也是一样。

    • 根本数据类型的数组也要优于对象数据类型的数组。另外两个平行的数组要比一个封装好的对象数组更加高效,举个例子,Foo[]和Bar[]这样的数组,应用起来要比Custom(Foo,Bar)[]这样的一个数组高效的多。

4.3 View异样优化

  • view自定义控件异样销毁保留状态

    • 常常容易被人疏忽,然而为了谋求高质量代码,这个也有必要加上。举个例子!

      @Overrideprotected Parcelable onSaveInstanceState() {    //异常情况保留重要信息。    //return super.onSaveInstanceState();    final Bundle bundle = new Bundle();    bundle.putInt("selectedPosition",selectedPosition);    bundle.putInt("flingSpeed",mFlingSpeed);    bundle.putInt("orientation",orientation);    return bundle;}@Overrideprotected void onRestoreInstanceState(Parcelable state) {    if (state instanceof Bundle) {        final Bundle bundle = (Bundle) state;        selectedPosition = bundle.getInt("selectedPosition",selectedPosition);        mFlingSpeed = bundle.getInt("flingSpeed",mFlingSpeed);        orientation = bundle.getInt("orientation",orientation);        return;    }    super.onRestoreInstanceState(state);}

4.4 去除淡黄色正告优化

  • 淡黄色正告尽管不会造成解体,然而作为程序员还是要尽量去除淡黄色正告,标准代码

4.5 正当应用汇合

  • 应用优化过的数据汇合

    • Android提供了一系列优化过后的数据汇合工具类,如SparseArray、SparseBooleanArray、LongSparseArray,应用这些API能够让咱们的程序更加高效。HashMap工具类会绝对比拟低效,因为它须要为每一个键值对都提供一个对象入口,而SparseArray就防止掉了根本数据类型转换成对象数据类型的工夫。

4.6 Activity不可见优化

  • 当Activity界面不可见时开释内存

    • 当用户关上了另外一个程序,咱们的程序界面曾经不可见的时候,咱们该当将所有和界面相干的资源进行开释。重写Activity的onTrimMemory()办法,而后在这个办法中监听TRIM_MEMORY_UI_HIDDEN这个级别,一旦触发阐明用户来到了程序,此时就能够进行资源开释操作了。
  • 过后看到这个感觉很离奇的,然而具体还是没有用到,要是那个大神有具体操作计划,能够分享一下。

4.7 节制的应用Service

  • 节制的应用Service

    • 如果应用程序须要应用Service来执行后台任务的话,只有当工作正在执行的时候才应该让Service运行起来。当启动一个Service时,零碎会偏向于将这个Service所依赖的过程进行保留,零碎能够在LRUcache当中缓存的过程数量也会缩小,导致切换程序的时候消耗更多性能。咱们能够应用IntentService,当后台任务执行完结后会主动进行,防止了Service的内存透露。

5.网络优化

5.1 图片分类

  • 图片网络优化

    • 比方我之前看到豆瓣接口,提供一种加载图片形式特地好。接口返回图片的数据有三种,一种是高清大图,一种是失常图片,一种是缩略小图。当用户处于wifi下给控件设置高清大图,当4g或者3g模式下加载失常图片,当弱网条件下加载缩略图【也称与加载图】。
    • 简略来说依据用户的以后的网络品质来判断下载什么品质的图片(电商用的比拟多)。豆瓣开源接口能够参考一下!

5.2 获取网络数据优化

  • 挪动端获取网络数据优化的几个点
  • 连贯复用:节俭连贯建设工夫,如开启 keep-alive。

    • 对于Android来说默认状况下HttpURLConnection和HttpClient都开启了keep-alive。只是2.2之前HttpURLConnection存在影响连接池的Bug,具体可见:Android HttpURLConnection及HttpClient抉择
  • 申请合并:行将多个申请合并为一个进行申请,比拟常见的就是网页中的CSS Image Sprites。如果某个页面内申请过多,也能够思考做肯定的申请合并。
  • 缩小申请数据的大小:对于post申请,body能够做gzip压缩的,header也能够做数据压缩(不过只反对http

    • 返回数据的body也能够做gzip压缩,body数据体积能够放大到原来的30%左右。(也能够思考压缩返回的json数据的key数据的体积,尤其是针对返回数据格式变动不大的状况,支付宝聊天返回的数据用到了)

5.3 网络申请异样拦挡优化

  • 在获取数据的流程中,拜访接口和解析数据时都有可能会出错,咱们能够通过拦截器在这两层拦挡谬误。

    • 1.在拜访接口时,咱们不必设置拦截器,因为一旦呈现谬误,Retrofit会主动抛出异样。比方,常见申请异样404,500,503等等。
    • 2.在解析数据时,咱们设置一个拦截器,判断Result外面的code是否为胜利,如果不胜利,则要依据与服务器约定好的错误码来抛出对应的异样。比方,token生效,禁用同账号登陆多台设施,短少参数,参数传递异样等等。
    • 3.除此以外,为了咱们要尽量避免在View层对谬误进行判断,解决,咱们必须还要设置一个拦截器,拦挡onError事件,而后应用ExceptionUtils,让其依据谬误类型来别离解决。
    • 具体能够间接看lib中的ExceptionUtils类,那么如何调用呢?入侵性极低,不必扭转之前的代码!
    @Overridepublic void onError(Throwable e) {    //间接调用即可    ExceptionUtils.handleException(e);}

6.线程优化

6.1 应用线程池

  • 将全局线程用线程池治理

    • 间接创立Thread实现runnable办法的弊病

      • 大量的线程的创立和销毁很容易导致GC频繁的执行,从而产生内存抖动景象,而产生了内存抖动,对于挪动端来说,最大的影响就是造成界面卡顿
      • 线程的创立和销毁都须要工夫,当有大量的线程创立和销毁时,那么这些工夫的耗费则比拟显著,将导致性能上的缺失
    • 为什么要用线程池

      • 重用线程池中的线程,防止频繁地创立和销毁线程带来的性能耗费;无效控制线程的最大并发数量,避免线程过大导致抢占资源造成零碎阻塞;能够对线程进行肯定地治理。
    • 应用线程池治理的经典例子

      • RxJava,RxAndroid,底层对线程池的封装治理特地值得参考
    • 对于线程池,线程,多线程的具体内容

      • 参考:轻量级线程池封装库,反对异步回调,能够检测线程执行的状态
      • 该我的项目中哪里用到频繁new Thread

        • 保留图片[留神,尤其是大图和多图场景下留神耗时太久];某些页面从数据库查问数据;设置核心革除图片,视频,下载文件,日志,零碎缓存等缓存内容
        • 应用线程池治理库益处,比方保留图片,耗时操作放到子线程中,处理过程中,能够检测到执行开始,异样,胜利,失败等多种状态。

7.图片优化

7.1 bitmap优化

  • 加载图片所占的内存大小计算形式

    • 加载网络图片:bitmap内存大小 = 图片长度 x 图片宽度 x 单位像素占用的字节数【看到网上很多都是这样写的,然而不全面】
    • 加载本地图片:bitmap内存大小 = width height nTargetDensity/inDensity 一个像素所占的内存。留神不要疏忽了一个影响项:Density
  • 第一种加载图片优化解决:压缩图片

    • 品质压缩办法:在放弃像素的前提下扭转图片的位深及透明度等,来达到压缩图片的目标,这样适宜去传递二进制的图片数据,比方分享图片,要传入二进制数据过来,限度500kb之内。
    • 采样率压缩办法:设置inSampleSize的值(int类型)后,如果设为n,则宽和高都为原来的1/n,宽高都缩小,内存升高。
    • 缩放法压缩:Android中应用Matrix对图像进行缩放、旋转、平移、斜切等变换的。性能非常弱小!
  • 第二种加载图片优化:不压缩加载高清图片如何做?

    • 应用BitmapRegionDecoder,次要用于显示图片的某一块矩形区域,如果你须要显示某个图片的指定区域,那么这个类十分适合。

7.2 glide加载优化

  • 在画廊中加载大图

    • 如果你滑动特地快,glide加载优化就显得十分重要呢,具体优化办法如下所示

      recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {    @Override    public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {        super.onScrollStateChanged(recyclerView, newState);        if (newState == RecyclerView.SCROLL_STATE_IDLE) {            LoggerUtils.e("initRecyclerView"+ "复原Glide加载图片");            Glide.with(ImageBrowseActivity.this).resumeRequests();        }else {            LoggerUtils.e("initRecyclerView"+"禁止Glide加载图片");            Glide.with(ImageBrowseActivity.this).pauseRequests();        }    }});

8.加载优化

8.1 懒加载优化

  • 该优化在新闻类app中非常常见

    • ViewPager+Fragment的搭配在日常开发中也比拟常见,可用于切换展现不同类别的页面。
    • 懒加载,其实也就是提早加载,就是等到该页面的UI展现给用户时,再加载该页面的数据(从网络、数据库等),而不是依附ViewPager预加载机制提前加载两三个,甚至更多页面的数据。这样能够进步所属Activity的初始化速度,也能够为用户节俭流量.而这种懒加载的形式也曾经/正在被诸多APP所采纳。
  • 具体看这篇文章

    • www.jianshu.com/p/cf1f4104d…

8.2 启动页优化

  • 启动工夫剖析

    • 零碎创立过程的工夫和利用过程启动的工夫,前者是由零碎自行实现的,个别都会很快,咱们也干涉不了,我感觉能做的就是去优化利用过程启动,具体说来就是从发Application的onCreate()执行开始到MainActivity的onCreate()执行完结这一段时间。
  • 启动工夫优化

    • Application的onCreate()办法
    • MainActivity的onCreate()办法
    • 优化的伎俩也无非三种,如下所示:

      • 提早初始化
      • 后台任务
      • 启动界面预加载
  • 启动页白屏优化

    • 为什么存在这个问题?

      • 当系统启动一个APP时,zygote过程会首先创立一个新的过程去运行这个APP,然而过程的创立是须要工夫的,在创立实现之前,界面是出现假死状态,于是零碎依据你的manifest文件设置的主题色彩的不同来展现一个白屏或者黑屏。而这个黑(白)屏正式的称说应该是Preview Window,即预览窗口。
      • 实际上就是是activity默认的主题中的android:windowBackground为红色或者彩色导致的。
      • 总结来说启动程序就是:app启动——Preview Window(也称为预览窗口)——启动页
    • 解决办法

      • 常见有三种,这里解决办法是给以后启动页增加一个有背景的style款式,而后SplashActivity援用以后theme主题,留神在该页面将window的背景图设置为空!
      • 更多对于启动页为什么白屏闪屏,以及不同解决办法,能够看我这篇博客:App启动页面优化
  • 启动工夫优化

    • IntentService子线程分担局部初始化工作

      • 当初application初始化内容有:阿里云推送初始化,腾讯bugly初始化,im初始化,神策初始化,内存透露工具初始化,头条适配计划初始化,阿里云热修复……等等。将局部逻辑放到IntentService中解决,能够缩短很多工夫。
      • 开启IntentSerVice线程,将局部逻辑和耗时的初始化操作放到这里解决,能够缩小application初始化工夫
      • 对于IntentService应用和源码剖析,性能剖析等能够参考博客:IntentService源码剖析

9.其余优化

9.1 动态变量优化

  • 尽量不应用动态变量保留外围数据。这是为什么呢? - 这是因为android的过程并不是平安的,包含application对象以及动态变量在内的过程级别变量并不会始终呆着内存外面,因为它很有会被kill掉。 - 当被kill掉之后,实际上app不会从新开始启动。Android零碎会创立一个新的Application对象,而后启动上次用户来到时的activity以造成这个app素来没有被kill掉的假象。而这时候动态变量等数据因为过程曾经被杀死而被初始化,所以就有了不举荐在动态变量(包含Application中保留全局数据静态数据)的观点。

9.2 注解代替枚举

  • 应用注解限定传入类型

    • 比方,尤其是写第三方开源库,对于有些裸露给开发者的办法,须要限定传入类型是有必要的。举个例子:
    • 刚开始的代码

      /** * 设置播放器类型,必须设置 * 留神:感激某人倡议,这里限定了传入值类型 * 输出值:111   或者  222
     */    public void setPlayerType(int playerType) {        mPlayerType = playerType;    }        ```*   优化后的代码,无效防止第一种形式开发者传入值谬误    ```    /**     * 设置播放器类型,必须设置     * 留神:感激某人倡议,这里限定了传入值类型     * 输出值:ConstantKeys.IjkPlayerType.TYPE_IJK   或者  ConstantKeys.IjkPlayerType.TYPE_NATIVE     * @param playerType IjkPlayer or MediaPlayer.     */    public void setPlayerType(@ConstantKeys.PlayerType int playerType) {        mPlayerType = playerType;    }    /**     * 通过注解限定类型     * TYPE_IJK                 IjkPlayer,基于IjkPlayer封装播放器     * TYPE_NATIVE              MediaPlayer,基于原生自带的播放器控件     */    @Retention(RetentionPolicy.SOURCE)    public @interface IjkPlayerType {        int TYPE_IJK = 111;        int TYPE_NATIVE = 222;    }    @IntDef({IjkPlayerType.TYPE_IJK,IjkPlayerType.TYPE_NATIVE})    public @interface PlayerType{}        ```
  • 应用注解代替枚举,代码如下所示

    @Retention(RetentionPolicy.SOURCE)public @interface ViewStateType {    int HAVE_DATA = 1;    int EMPTY_DATA = 2;    int ERROR_DATA = 3;    int ERROR_NETWORK = 4;}

9.3 多渠道打包优化

  • 还在手动打包吗?尝试一下python自动化打包吧……

    • 瓦力多渠道打包的Python脚本测试工具,通过该自动化脚本,自须要run一下或者命令行运行脚本即可实现美团瓦力多渠道打包,打包速度很快。配置信息非常简略,代码中曾经正文非常具体。能够自定义输入文件门路,能够批改多渠道配置信息,简略实用。 我的项目地址:github.com/yangchong21…

9.4 TrimMemory和LowMemory优化

  • 能够优化什么?

    • 在 onTrimMemory() 回调中,应该在一些状态下清理掉不重要的内存资源。对于这些缓存,只有是读进内存内的都算,例如最常见的图片缓存、文件缓存等。拿图片缓存来说,市场上,惯例的图片加载库,一般而言都是三级缓存,所以在内存吃紧的时候,咱们就应该优先清理掉这部分图片缓存,毕竟图片是吃内存小户,而且再次回来的时候,尽管内存中的资源被回收掉了,仍然能够从磁盘或者网络上复原它。
  • 大略的思路如下所示

    • 在lowMemory的时候,调用Glide.cleanMemory()清理掉所有的内存缓存。
    • 在App被置换到后盾的时候,调用Glide.cleanMemory()清理掉所有的内存缓存。
    • 在其它状况的onTrimMemory()回调中,间接调用Glide.trimMemory()办法来交给Glide解决内存状况。

9.5 轮询操作优化

  • 什么叫轮训申请?

    • 简略了解就是App端每隔肯定的工夫反复申请的操作就叫做轮训申请,比方:App端每隔一段时间上报一次定位信息,App端每隔一段时间拉去一次用户状态等,这些应该都是轮训申请。比方,电商类我的项目,某个抽奖流动页面,隔1分钟调用一次接口,弹出一些获奖人信息,你应该某个阶段看过这类轮询操作!
  • 具体优化操作

    • 长连贯并不是稳固的牢靠的,而执行轮训操作的时候个别都是要稳固的网络申请,而且轮训操作个别都是有生命周期的,即在肯定的生命周期内执行轮训操作,而长连贯个别都是整个过程生命周期的,所以从这方面讲也不太适宜。
    • 倡议在service中做轮询操作,轮询申请接口,具体做法和留神要点,能够间接看该我的项目代码。看app包下的LoopRequestService类即可。
    • 大略思路:当用户关上这个页面的时候初始化TimerTask对象,每个一分钟申请一次服务器拉取订单信息并更新UI,当用户来到页面的时候革除TimerTask对象,即勾销轮训申请操作。

9.6 去除反复依赖库优化

  • 我置信你看到了这里会有疑难,网上有许多博客作了这方面阐明。然而我在这里想说,如何查找本人我的项目的所有依赖关系树

    • 留神要点:其中app就是我的项目mudule名字。 失常状况下就是app!
    gradlew app:dependencies
  • 对于依赖关系树的结构图如下所示,此处省略很多代码

    |    |    |    |    |    |    \--- android.arch.core:common:1.1.1 (*)|    |    |    |         \--- com.android.support:support-annotations:26.1.0 -> 28.0.0|    +--- com.journeyapps:zxing-android-embedded:3.6.0|    |    +--- com.google.zxing:core:3.3.2|    |    \--- com.android.support:support-v4:25.3.1|    |         +--- com.android.support:support-compat:25.3.1 -> 28.0.0 (*)|    |         +--- com.android.support:support-media-compat:25.3.1|    |         |    +--- com.android.support:support-annotations:25.3.1 -> 28.0.0|    |         |    \--- com.android.support:support-compat:25.3.1 -> 28.0.0 (*)|    |         +--- com.android.support:support-core-utils:25.3.1 -> 28.0.0 (*)|    |         +--- com.android.support:support-core-ui:25.3.1 -> 28.0.0 (*)|    |         \--- com.android.support:support-fragment:25.3.1 -> 28.0.0 (*)\--- com.android.support:multidex:1.0.2 -> 1.0.3
  • 而后查看哪些反复jar

    • [图片上传失败...(image-b9d450-1563928824510)]

      <figcaption></figcaption>

  • 而后批改gradle配置代码

    api (rootProject.ext.dependencies["zxing"]){    exclude module: 'support-v4'    exclude module: 'appcompat-v7'}

9.7 四种援用优化

  • 软援用应用场景

    • 失常是用来解决大图片这种占用内存大的状况

      • 代码如下所示
      Bitmap bitmap = bitmaps.get(position);//失常是用来解决图片这种占用内存大的状况bitmapSoftReference = new SoftReference<>(bitmap);if(bitmapSoftReference.get() != null) {    viewHolder.imageView.setImageBitmap(bitmapSoftReference.get());}//其实看glide底层源码可知,也做了相干软援用的操作
*   **这样应用软援用益处**    *   通过软援用的get()办法,获得bitmap对象实例的强援用,发现对象被未回收。在GC在内存短缺的状况下,不会回收软援用对象。此时view的背景显示    *   理论状况中,咱们会获取很多图片.而后可能给很多个view展现, 这种状况下很容易内存吃紧导致oom,内存吃紧,零碎开始会GC。这次GC后,bitmapSoftReference.get()不再返回bitmap对象,而是返回null,这时屏幕上背景图不显示,阐明在零碎内存缓和的状况下,软援用被回收。    *   应用软援用当前,在OutOfMemory异样产生之前,这些缓存的图片资源的内存空间能够被开释掉的,从而防止内存达到下限,防止Crash产生。
  • 弱援用应用场景

    • 弱援用–>随时可能会被垃圾回收器回收,不肯定要等到虚拟机内存不足时才强制回收。
    • 对于应用频次少的对象,心愿尽快回收,应用弱援用能够保障内存被虚拟机回收。比方handler,如果心愿应用完后尽快回收,看上面代码
    private MyHandler handler = new MyHandler(this);private static class MyHandler extends Handler{    WeakReference<FirstActivity> weakReference;    MyHandler(FirstActivity activity) {        weakReference = new WeakReference<>(activity);    }    @Override    public void handleMessage(Message msg) {        super.handleMessage(msg);        switch (msg.what){        }    }}
  • 到底什么时候应用软援用,什么时候应用弱援用呢?

    • 集体认为,如果只是想防止OutOfMemory异样的产生,则能够应用软援用。如果对于利用的性能更在意,想尽快回收一些占用内存比拟大的对象,则能够应用弱援用。
    • 还有就是能够依据对象是否常常应用来判断。如果该对象可能会常常应用的,就尽量用软援用。如果该对象不被应用的可能性更大些,就能够用弱援用。

9.8 加载loading优化

  • 个别理论开发中会至多有两种loading

    • 第一种是从A页面进入B页面时的加载loading,这个时候特点是显示loading的时候,页面是纯白色的,加载完数据后才显示内容页面。
    • 第二种是在某个页面操作某种逻辑,比方某些耗时操作,这个时候是部分loading[个别用个帧动画或者补间动画],因为应用频繁,因为倡议在销毁弹窗时,增加销毁动画的操作。
  • 自定义loading加载

    • github.com/yangchong21…

9.9 对象池Pools优化

  • 对象池Pools优化频繁创立和销毁对象
  • 应用对象池,能够避免频繁创立和销毁对象而呈现内存抖动

    • 在某些时候,咱们须要频繁应用一些长期对象,如果每次应用的时候都申请新的资源,很有可能会引发频繁的 gc 而影响利用的流畅性。这个时候如果对象有明确的生命周期,那么就能够通过定义一个对象池来高效的实现复用对象。
    • 具体参考案例,能够看该我的项目:github.com/yangchong21…

10.RecyclerView优化

10.1 页面为何卡顿

  • RecyclerView滑动卡顿的起因有哪些?

    • 第一种:嵌套布局滑动抵触

      • 导致嵌套滑动难解决的要害起因在于当子控件生产了事件, 那么父控件就不会再有机会解决这个事件了, 所以一旦外部的滑动控件生产了滑动操作, 内部的滑动控件就再也没机会响应这个滑动操作了
    • 第二种:嵌套布局档次太深,比方六七层等

      • 测量,绘制布局可能会导致滑动卡顿
    • 第三种:比方用RecyclerView实现画廊,加载比拟大的图片,如果疾速滑动,则可能会呈现卡顿,次要是加载图片须要工夫
    • 第四种:在onCreateViewHolder或者在onBindViewHolder中做了耗时的操作导致卡顿。按stackoverflow下面比拟艰深的解释:RecyclerView.Adapter外面的onCreateViewHolder()办法和onBindViewHolder()办法对工夫都十分敏感。相似I/O读写,Bitmap解码一类的耗时操作,最好不要在它们外面进行。
  • 对于RecyclerView封装库

    • github.com/yangchong21…

10.2 具体优化计划

  • 03.SparseArray代替HashMap
  • 04.瀑布流图片错乱问题解决
  • 05.item点击事件放在哪里优化
  • 06.ViewHolder优化
  • 07.间断上拉加载更多优化
  • 08.拖拽排序与滑动删除优化
  • 09.暂停或进行加载数据优化
  • 11.异常情况下保留状态
  • 12.多线程下插入数据优化
  • 14.recyclerView优化解决
  • 15.adapter优化
  • 具体看这篇博客:recyclerView优化
    情谊举荐:

Android 干货分享

至此,本篇已完结,如有不对的中央,欢迎您的倡议与斧正。同时期待您的关注,感谢您的浏览,谢谢!