关于android:Android优化总结

4次阅读

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

浏览五分钟,每日十点,和您一起一生学习,这里是程序员 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()来进行动画。
    @Override
    protected 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;
    @Override
    protected 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 自定义控件异样销毁保留状态

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

      @Override
      protected Parcelable onSaveInstanceState() {
          // 异常情况保留重要信息。//return super.onSaveInstanceState();
          final Bundle bundle = new Bundle();
          bundle.putInt("selectedPosition",selectedPosition);
          bundle.putInt("flingSpeed",mFlingSpeed);
          bundle.putInt("orientation",orientation);
          return bundle;
      }
      
      @Override
      protected 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 类,那么如何调用呢?入侵性极低,不必扭转之前的代码!
    @Override
    public 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 干货分享

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

正文完
 0