继续创作,加快生长!这是我参加「日新计划 10 月更文挑战」的第4天,点击检查活动概况

前言

从网上汇总收集许多大佬的功能优化文章,整理出来部分知识点,首要包含:

UI优化/启动优化/溃散优化/卡顿优化/安全性优化/弱网优化/APP深度优化等等等~

本篇是第一篇:UI优化! [非商业用途,如有侵权,请奉告我,我会删去]

一、UI优化

UI优化知识点首要分为三部分

  • 第一部分,体系为咱们做的优化。因为前端中UI展现的特殊性和重要性,Android团队也是在不断想办法进步UI方面的烘托速度,所以也是更新了许多体系优化计划,比方:

硬件加快、黄油计划、RenderThread。

  • 第二部分,咱们能够详细施行的优化计划。首要包含:

java代码布局、View重用、异步创立View、xml布局优化、异步布局结构Litho、屏幕适配、Flutter、Jetpack Compose

  • 第三部分,东西运用,首要包含:

Choreographer、monitor、Systrace

1.1 体系做的优化

1.1.1 硬件加快

之前咱们说过,一个图形的制作是CPU,GPU和屏幕三方协作的成果。

Android3.0之前,还没有硬件加快,都是经过CPU进行数据核算,然后经过Skia库进行软件制作,可是CPU关于图形处理并不高效。

所以从3.0开端,Android支撑了硬件加快,到Android4.0默许敞开硬件加快。

敞开硬件加快后,便是由CPU进行图形缓存数据的制作。这样CPU和GPU就能比较好的分工,各司其职了。CPU用于控制复杂制作逻辑、构建或更新DisplayList(根底元素)GPU用于完结图形核算、烘托DisplayList(根底元素)

这儿也找了一张各种场景下,硬件加快前后的流程与加快作用(Android6.0背景):

Android性能优化系列篇(一):UI优化

可是硬件加快也是有缺点的:

  • 启用硬件加快需求更多资源,因此运用会占用更多内存。
  • 比较低的版本,因为有些Canvas API还没有支撑,所以运用硬件加快或许会有问题。那么咱们就能够手动封闭某个view的硬件加快:
  myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null)

Project Butter

黄油计划,你有或许没怎么听说,可是其实之前两章内容都提到过,Android4.1之后,Google提出了黄油计划,首要包含两个内容:

  • VSYNC
  • Triple Buffering(三重缓存)

这些都了解了吧,上两节都说过的,这儿再简略提一下:

  • VSYNC

垂直同步信号,每逢收到这个信号后,CPU就开端准备Buffer数据,并在16ms之内和GPU把屏幕需求的缓存数据准备好。

  • Triple Buffering(三重缓存)

Android4.1之前,是双缓存机制,大部分是没问题的。可是当CPU/GPU制作进程较长,超越一个vsync信号周期,一般是16ms,就会导致丢帧,CPU无法运用被GPU或者屏幕占用的缓存区。假如下一帧制作假如又超时,那么又会丢帧。

所以再加上一个缓存区,这样,CPU、GPU、Display三者都有各自的缓存区,互不影响,就能确保时刻的最大化利用,也就能削减上述的状况了。

RenderThread

RenderThread是在Android5.0提出的,从这个姓名就能知道,它是一个线程,一个专门执行GL指令的线程,也便是一部分的制作作业。

有了它之后,当CPU处理数据给GPU后,就不需求等GPU烘托完毕了,而是将一些制作使命交给RenderThread,这样就能削减主线程的作业,确保画面的流畅。一起还供给了RenderNode,用来做view的特点封装,烘托帧的信息等等。

1.2 优化计划

1.2.1 java代码布局

咱们一般都是用XML文件进行布局,可是XML解析也是很耗时的,并在这个解析进程在主线程进行。

所以咱们有的时分或许能够经过Java代码或者kotlin进行View的创立?

理论中,这样的确能削减布局加载的消耗时刻,可是Java代码创立View太麻烦了,而且无法可视化。

当然,也有一些库能够协助咱们将xml代码转换成java代码,比方X2C(github.com/iReaderAndr… ),可是它并不支撑一切的状况,比方merge标签,appCompat兼容控件等等。

所以咱们需求在这之中找到平衡点,有的时分,UI简略而且要求高功能的前提下,咱们能够试试用这样的办法,即用java代码替代XmL代码。

1.2.2 View重用

参照Recyclerview的做法,咱们也能够将一些常用的view保存到缓存池中,这样在不同的界面中就能复用缓存池里面的view。

1.2.3 异步创立view

这是Google提出的一个计划——AsyncLayoutInflater。它能够异步加载布局文件,而且回调给主线程,然后削减主线程耗时。简略贴下首要代码:

new AsyncLayoutInflater(MainActivity.this).inflate(R.layout.activity_main, null, new AsyncLayoutInflater.OnInflateFinishedListener() {
    @Override
    public void onInflateFinished(@NonNull View view, int i, @Nullable ViewGroup viewGroup) {
    //回调给主线程
      setContentView(view);
   }
});

1.2.4 xml布局优化

在写xml布局文件的时分,咱们要做的也有许多,比方:

  • 削减布局嵌套。多运用ViewStub、Merge、ConstraintLayout来替代。
  • 优化开支。RelativeLayout和 运用weight的LinearLayout 开支比较大,建议运用ConstraintLayout,LinearLayout替代。

1.2.5 异步布局结构Litho

Litho是Facebook开源的一款在Android上高效树立UI的声明式结构。

首要有以下特点:

1)声明式:它运用了声明式的API来界说UI组件。

2)异步布局:它把 measurelayout 都放到了后台线程,只留下了必须要在主线程完结的 draw,这大大降低了 UI 线程的负载

3)视图扁平化:因为 Litho 运用了自有的布局引擎(Yoga),在布局阶段就能够检测不必要的层级、削减 ViewGroups,来完成 UI 扁平化。

4)优化 RecyclerView:Litho 还优化了 RecyclerView 中 UI 组件的缓存和回收办法。

1.2.6 屏幕适配

关于屏幕适配问题,也是陈词滥调了。首要有以下几种计划:

  • dp适配计划。

这是体系自带的适配单位,dp是根据屏幕物理分辨率一个笼统的单位,用于阐明与密度无关的尺度和方位。所以它能在不同分辨率的手机上有相对巨细的适配性。核算公式是:px=dp * (dpi/160)。可是dpi有或许会被人为调整(比方几部相同分辨率不同尺度的手机的ppi或许分别是是430,440,450,那么在Android体系中,或许dpi会全部指定为480),所以仍是有或许在一些设备上呈现适配问题。

  • 宽高限定符适配计划。

简略地说,这个计划便是穷举市面上一切的Android手机的宽高像素值。然后找到对应的文件夹运用下面的资源文件所对应的px值。

可是这计划有个缺点,便是必须精确命中才行。比方1920x1080的手机就一定要找到1920x1080的限定符,否则就只能用统一的默许的dimens文件了。

所以容错性太低,不引荐。

  • smallestWidth适配计划。

这个计划便是经过手机的宽度值来找到对应限定符文件夹下的资源文件,能够看做宽高限定符屏幕适配计划的升级版。

假如咱们的设计图宽为360dp,那么就创立values-sw360dp文件夹,并添加资源文件:

<?xml version="1.0" encoding="UTF-8"?>
<resources>
  <dimen name="dp_1">1dp</dimen>
  <dimen name="dp_2">2dp</dimen>
  <dimen name="dp_3">3dp</dimen>
  ...
  <dimen name="dp_359">359dp</dimen>
  <dimen name="dp_360">360dp</dimen>
</resources>

很简略,分为360份,然后咱们实践写布局文件的时分,直接引用对应的dimen值即可。

然后新建其他设备宽度的文件夹,并在每个文件夹里添加对应的资源文件,这儿以400dp为例:

├── src/main
│  ├── res
│  ├── ├──values
│  ├── ├──values-sw320dp
│  ├── ├──values-sw400dp
│  ├── ├──...
│  ├── ├──values-sw640dp
<?xml version="1.0" encoding="UTF-8"?>
<resources>
  <dimen name="dp_1">1.1111dp</dimen>
  <dimen name="dp_2">2.2222dp</dimen>
  <dimen name="dp_3">3.3333dp</dimen>
  <dimen name="dp_4">4.4444dp</dimen>
  ...
  <dimen name="dp_359">398.8889dp</dimen>
  <dimen name="dp_360">400.0000dp</dimen>
</resources>

也便是说,一切的设备都分为360份了,这样就能确保在不同宽度设备上都能有差不多的作用。

假如咱们的设备宽度为400dp,那么就会调用values-sw400dp对应的资源文件,假如找不到,就会向下查找。比方咱们宽度是402dp,找不到对应的,就会向上找到400dp对应的资源文件,所以也有比较好的容错性。也是一个比较好的适配计划。

当然这种重复性作业必定不需求咱们自己手动去完成,有专门的插件能够生成相应的文件和文件夹,这儿也引荐一个:github.com/ladingwu/di…

  • 今天头条适配计划。

这个咱们应该都很了解了,首要是经过动态修正density值来确保一切设备的屏幕宽度都是固定的dp值。用到的公式便是px = density * dp

比方设计图宽为360dp,咱们只要确保一切设备的宽度都是360dp就能适配了。而宽度的px值咱们是已知的,所以便是要修正这个 density 值来完结咱们的适配意图。详细代码我就不贴了,网上许多。

这种计划侵入性低,运用方便,是个不错的适配计划。

1.2.7 Flutter

Flutter是 Google 推出并开源的移动运用开发结构,开发者能够经过 Dart 言语开发 App,一套代码一起运行在 iOS 和 Android 渠道。

Flutter结构现在也是特别火,实践运用到许多的大厂项目,比方闲鱼今天头条。它相关于Android其实是别的一套APP架构了,它没有根据体系本身的烘托引擎,而是app中自带Skia引擎,虚拟机也是运用的Dart虚拟机。

首要有以下几个特点:

  • 跨渠道:现在flutter至少能够跨5种渠道,常见的渠道:MacOS,Windows ,Linux ,Android ,iOS ,到目前为止,Flutter算是支撑渠道最多的结构了。杰出的跨渠道性,大大削减了开发成本。
  • 丝滑般的体验:运用Flutter内置的Material Design(android风格)和Cupertino(ios风格)风格组件,以及丰富的motion API,平滑而自然的滑动作用和渠道感知,为用户带来全新的体验。
  • 呼应式结构:运用一系列根底组件和呼应式结构,能够轻松构建用户界面。运用功能强壮且灵敏的API能够完成复杂的界面作用。
  • 支撑插件:运用插件能够拜访渠道本地API,如相机,蓝牙,WIFI等等。凭借现有的Java,swift ,object c , c++代码完成对原生体系的调用。
  • 60fps超高功能:Flutter编写的运用能够到达60fps(每秒传输帧数)。Flutter选用GPU烘托技术,所以功能很好。完全能够胜任游戏开发。

1.2.8 Jetpack Compose

Jetpack Compose 是用于构建原生 Android 界面的新东西包

本来咱们的布局文件都是写在xml文件中的,现在供给了一种新的view构建方法,也便是Compose

它是一种声明式UI,不再运用xml,而是运用kotlin进行UI布局。其实就跟咱们之前提到的一点,用java代码去构建view一样的作用。这样就削减了xml解析的时刻,进步了功率。

声明式UI。指的是只需求把界面声明出来,不需求手动更新。比方咱们这儿的Compose只需求写一遍,后续的UI改变会随着变量自动更新。而传统的xml布局方法就无法做到这一点,归于指令式UI,需求咱们手动指令纸牌屋UI的修正。

官方也是声称有以下几点优势

更少更直观的代码,更强壮的功能,能进步开发速度。

最终贴一段代码,感触下Compose的写法:

class MainActivity : AppCompatActivity() {
 override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)
  setContent {
   Greeting("Android")
   }
  }
}
​
@Composable
fun Greeting(name: String) {
  Text (text = "Hello $name!")
}
 

仿制

1.3 东西篇

1.3.1 Choreographer

Choreographer其实也是一个监控运用帧率的东西。它首要有以下特性:

  • 能获取整体的帧率。
  • 能在线上运用。
  • 获取的帧率几乎是实时的。

首要原理便是利用postFrameCallback核算两次制作的间隔时刻,简略贴下代码:

private long mLastFrameTime;
Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
  @Override
  public void doFrame(long frameTimeNanos) {
    if (mLastFrameTime == 0) {
      mLastFrameTime = frameTimeNanos;
     }
    float diff = (frameTimeNanos - mLastFrameTime) / 1000000.0f;//得到毫秒,正常是 16.66 ms
    if (diff > 500) {
      double fps = (((double) (mFrameCount * 1000L)) / diff);
      mFrameCount = 0;
      mLastFrameTime = 0;
      Log.d("doFrame", "doFrame: " + fps);
     } else {
      ++mFrameCount;
     }
    Choreographer.getInstance().postFrameCallback(this);
   }
});

想细细研讨的能够看看这个库(github.com/friendlyrob… )

1.3.2 LayoutInspector/Android Device Monitor

LayoutInspectorAndroidStudio种的一个布局检查器,能够经过Tools > Layout Inspector找到,他能够检查运用中的某个界面的视图结构,可是无法检查非调式状况的运用。

假如要看其他运用的布局状况,能够运用Android Device Monitor,在Android Studio 3.1 以后,需求独自从文件夹打开了:

android-sdk/tools/monitor

1.3.3 Systrace

Systrace是分析Android功能问题的神器,获取Systrace文件的方法有两种:

  • 一是AndroidSDK/tools目录下,经过monitor.batAndroid Device Monitor可视化东西得到。
  • 二是经过python脚本获取。