美文网首页
常见卡顿来源及其优化方法

常见卡顿来源及其优化方法

作者: CrazyOrr | 来源:发表于2020-03-03 14:00 被阅读0次

1. 可滚动列表

1.1 RecyclerView: notifyDataSetChanged

非必要不要全量更新,可以考虑使用DiffUtil实现差量更新。

void onNewDataArrived(List<News> news) {
    List<News> oldNews = myAdapter.getItems();
    DiffResult result = DiffUtil.calculateDiff(new MyCallback(oldNews, news));
    myAdapter.setNews(news);
    result.dispatchUpdatesTo(myAdapter);
}

MyCallback需要实现DiffUtil.Callback

1.2 RecyclerView: 嵌套RecyclerView

例如一个纵向的RecyclerView嵌套多个横向的RecyclerView时,如果这些被嵌套的RecyclerView的itemView都是相似的,
那么我们可以在这些被嵌套的RecyclerView之间共享RecyclerView.RecycledViewPool
使得这些被嵌套的RecyclerView的itemView跨RecyclerView复用。

更进一步优化,你还可以对被嵌套的RecyclerViewLinearLayoutManager调用setInitialPrefetchItemCount(int)
来预加载itemView。

1.3 RecyclerView: Inflation太多

减少非必要的 view type(同一view type的itemView才能复用)

1.4 ListView: Inflation太多

getView里的convertView复用

1.5 RecyclerView 或者 ListView: layout / draw 太慢

见后文 2.2 Layout2.3 Rendering

2. Layout

2.1 Layout: 耗时长

如果layout的耗时大于几毫秒,你有可能碰到了嵌套RelativeLayout或者带有weight的LinearLayout的最坏情况。

例如:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:orientation="horizontal"
  tools:context=".jankoptim.ConstraintLayoutActivity">

  <LinearLayout
      android:layout_width="0dp"
      android:layout_height="100dp"
      android:layout_weight="1"
      android:orientation="horizontal">

      <com.example.playground.view.CustomView
          android:layout_width="20dp"
          android:layout_height="20dp"
          android:background="@color/colorAccent" />

      <View
          android:layout_width="30dp"
          android:layout_height="10dp"
          android:background="@color/colorPrimary" />

  </LinearLayout>

  <View
      android:layout_width="0dp"
      android:layout_height="10dp"
      android:layout_weight="1"
      android:background="@color/colorPrimaryDark" />

</LinearLayout>
第1层 第2层 第3层
View元素 LinearLayout LinearLayout CustomView
measure次数 1 1 x 2 = 2 1 x 2 x 2 = 4

可以看到,在这种情况下,每增加1层嵌套,CustomView的measure次数就多乘1次2,
也就是说,被嵌套的View的measure次数m随着嵌套深度n的增长呈指数级增长(m=2^n)。

优化要点:

  • 尽量只在最低层级的叶子节点上使用RelativeLayout或者带有weight的LinearLayout,也就是只有1层嵌套
  • 尝试使用ConstraintLayout实现类似的效果

2.2 Layout: 过于频繁

Layout应当在屏幕上显示新的内容时发生,比如当RecyclerView的一个新的item滚动进入可见区域。
如果每一帧都发生大量的layout,那么有可能是你对layout做了动画处理。
一般来说,动画应该运行在view的drawing properties(例如setTranslationX/Y/Z(), setRotation(), setAlpha()等)上,
而不是改变起来代价更大的layout properties(例如padding, margin)。

3. Rendering

Android UI分两步完成rendering:

  1. Record View#draw,在UI线程执行,调用每个invalidated Viewdraw(Canvas)方法。
  2. DrawFrame,在RenderThread根据上一步的结果执行。

3.1 Rendering: UI线程

3.1.1 bitmap

避免在UI线程绘制bitmap。

3.1.2 避免过度绘制

过度绘制就是在同一帧情况下对同一块像素区域进行重复绘制。这样会加重GPU跟CPU的渲染压力,导致渲染时间过长。

优化思路:

  • 移除(不可见的)window的背景

    <item name="android:windowBackground">@null</item>
    
  • 移除控件中不需要的背景

    • 对于子控件,如果其背景颜色跟父布局一致,那么就不用再给子控件添加背景了
    • 如果子控件背景五颜六色,且能够完全覆盖父布局,那么父布局就可以不用添加背景了
  • 减少透明度的使用

    比如:在TextView上设置带透明度alpha值的黑色文本可以实现灰色的效果。但是,直接通过设置灰色的话能够获得更好的性能

  • 减少布局的嵌套层级

  • 使用merge标签减少布局层级

  • 使用ViewStub标签懒加载

  • 减少自定义View的过度绘制,使用clipRect()只绘制可见区域

3.2 Rendering: RenderThread

  • 有些Canvas操作虽然在第一步record时很廉价,但会在第二步触发昂贵但计算。
  • Bitmap是作为OpenGL texture显示的,首次显示时,它会被上传到GPU。因此,如果Bitmap明显的大于显示所需,就会浪费上传时间和内存。

目前用的比较少,理解不深入,详情请见官方说明

4. 频繁GC

尽管在Android 5.0引入ART后此问题已经大大缓解,但是我们还是需要注意,
避免在View#onDrawRecyclerView.Adapter#onBindViewHolder等这类会被连续反复调用的方法中new局部对象。
因为这会在短时间内生成大量生命周期短暂的对象,导致频繁GC,引发UI卡顿。

5. 耗时操作

使用AsyncTaskThreadHandlerThreadThreadPoolExecutorIntentService等手段将耗时操作移出UI线程。

注意:如果你自己开启线程,你应该调用Process.setThreadPriority()
并传入THREAD_PRIORITY_BACKGROUND
设置线程的priority为"background"。如果不这样做,你开启的线程仍然有可能拖慢你的app,因为默认情况下它与UI线程的优先级相同。

详情请见Thread priority

优化思路:

  • 使用成员变量保存引用
  • 使用对象池进行复用

6. 线程过多

显然,这对于app的性能是有负面影响的。线程再多,CPU的资源是有限的,CPU在同一时间能够运行的线程数是不多的,
其他所有的线程都只能等待,同时,每个线程至少需要占用64K的内存。过多的线程只会带来对内存和CPU资源的激烈竞争。

优化思路:建立线程池统一管理

参考文档

相关文章

  • 常见卡顿来源及其优化方法

    1. 可滚动列表 1.1 RecyclerView: notifyDataSetChanged 非必要不要全量更新...

  • Activity 性能优化方案

    Activity 性能优化方案UI 卡顿原理UI卡顿常见原因优化手段 UI 卡顿原理人类大脑与眼睛对一个画面的连贯...

  • iOS UITableView性能优化 - 进阶篇(附实例)

    目录 卡顿起因避免卡顿的常见优化方法按需加载异步绘制延时加载图片 序言 UITableView 是我们开发中常用的...

  • 卡顿优化

    卡顿优化 - CPU 卡顿优化 - GPU 离屏渲染 卡顿检测 耗电优化

  • 21-性能优化

    一、CPU和GPU 二、卡顿产生的原因和优化 卡顿优化-CPU 卡顿优化-GPU 卡顿监测 监控卡顿的demo:推...

  • iOS 性能优化

    iOS的性能优化主要可提现在以前的几个方面:卡顿优化、耗电优化、启动优化、安装包的瘦身。 1、卡顿优化 在了解卡顿...

  • Android开发页面帧率优化有感

    Android APP 优化工具分析Android App优化之消除卡顿Android性能优化:卡顿优化Andro...

  • 性能优化

    面试题 CPU和GPU 屏幕成像原理 卡顿产生的原因 卡顿优化 - CPU 卡顿优化 - GPU 离屏渲染 卡顿检...

  • Android-性能优化

    性能优化 卡顿 如何衡量卡顿 "卡顿" 产生的原因 Profile GPU Rendering 通用优化流程第一步...

  • 无标题文章

    APP性能优化 UI卡顿优化 View的绘制原理 UI卡顿原理分析 UI卡顿检测分析 BlockCanary原理分...

网友评论

      本文标题:常见卡顿来源及其优化方法

      本文链接:https://www.haomeiwen.com/subject/ywmclhtx.html