likes
comments
collection
share

Epoxy - 在RecyclerView中构建复杂界面 - 9

作者站长头像
站长
· 阅读数 15

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

有必要了解一下 Epoxy 核心的一些内部原理.

Diffing

对于复杂数据结构支持的多个视图类型展示在屏幕上, Epoxy此时是尤其有用的. 在这些场景中, 数据可能会被网络请求, 异步 Observable, 用户输入, 或者别的来源更新, 这些源会请求更新 Model 并且通知适配器恰当变更.

手动追踪这些变更很困难, 且直接增加了显著的开销. 在这些场景下, 你可以充分利用Epoxy的自动 diffing 机制来减少开销, 同时也高效地更新变更的视图.

EpoxyController类自动使用 diffing 机制, 并且在EpoxyAdapter类中可以作为选项开启.

追踪 Model 状态

Epoxy在每个 Model 上调用equalshashCode来判断 Model 的当前状态以及 Model 何时发生了变更. 对于每个 Model 而言, 正确地实现这些方法对于 diffing 正常工作很重要.

Epoxy生成 Model 子类来避免手动实现equalshashCode的开销.

异步支持

EpoxyController支持在后台线程执行 diff 算法来提高性能. 查看这里获取更多细节.

最佳实践

在使用 diffing 时, 有一些性能隐患需要明白.

首先, diffing 必须处理列表中的全部 Model, 有成百上千个 Model 的话, 可能会影响 Controller 的性能. 大多数情况下 diffing 算法线性时间执行, 但依然必须处理列表中的全部 Model. 无论如何, 项的移动很慢, 在最坏情况下, 比如洗牌列表中的全部 Model, 性能是 (n^2)/2.

其次, 每一次 diff 必须通过equals/hashCode计算 Model 的状态, 来决定项变更. 在这些实现中避免导入不必要的计算会显著地拖慢 diff.

还有, 要清楚无意间修改 Model 状态, 例如点击监听器. 举个例子, 在 Model 设置点击监听器是很普遍的做法, 监听器会在 View 绑定的时候设置. 容易犯的错误是使用匿名内部类作为点击监听器, 这会影响 Model 状态, 并且在 Model 更新或者重建的时候请求视图重新绑定. 在这个场景下可以使用DoNotHash选项(@EpoxyAttribute(DoNotHash)告知生成的 Model 将该字段从状态的计算中排除出来). 另一个常见错误是在 Model 绑定调用期间修改 Model 状态.

注意该算法 - Android支持库类DiffUtil用于在EpoxyController中执行diffing. 由于历史原因, 更早的EpoxyAdapter使用了自定义方案进行diffing.

配置

有几个配置选项用来控制Epoxy的注解处理器的行为.

可用选项

  1. 要求抽象 Model - 如果启用了该选项, 那么任何包含Epoxy注解的Epoxy Model类必须是抽象的, 否则编译期将抛出错误. 拥有抽象模型有助于防止意外使用基本模型类而不是生成的类. 该选项默认设置为false.
  2. 要求Epoxy Model 的属性字段实现 equals和hashCode - 如果该选项启用的话, 那么注解处理器将会检查每一个注解了@EpoxyAttribute的字段, 并且确保该类型的字段实现了equals和hashCode. 如果没有实现的话, 编译期错误将会抛出. 为了 Model 状态能够正确决定diffing正常工作, 属性类型实现equals和hashCode很有必要. 启用该选项在意外使用了未实现equals/hashCode的类型时能够帮助捕获异常. 该选项默认是禁用的.
  3. 验证生成的Epoxy Model的使用 - 如果该选项启用的话, 生成的Epoxy Model会添加验证以确保该Model在EpoxyController中正确地使用. 包括: 确保 Model 没有在 Controller 中添加多次, Model 在添加之后没有被修改, 以及风格一致性保证(如果使用了Paris的话). 如果这些条件被违反的话, 将会抛出运行时异常. 这将强制将 Model 当作不可变对象对待, 一旦它被添加进Controller, 这将极大地鼓励EpoxyController的恰当使用, 并且防止 Model 在 diff 之后, 意外的 View 状态发生. 该选项默认是开启的, 但你也许喜欢在生产环境禁用掉它, 防止运行时验证的开销, 毕竟运行时验证开销挺大的.
  4. 隐式添加AutoModels - 如果启用该选项的话, 那么该注解创建的 Model 在被修改之后将自动地添加到 Controller. 点击文档查看详情. 该选项默认是禁用的.
  5. 禁止生成Kotlin扩展函数 - 默认情况下, 在使用 kapt 时, Model扩展函数 会自动生成. 可以通过设置disableEpoxyKotlinExtensionGenerationfalse全局禁用.

控制这些选项

In gradle

所有的这些选项在build.gradle文件中能够设置为注解处理器选项.

project.android.buildTypes.all { buildType ->
  buildType.javaCompileOptions.annotationProcessorOptions.arguments =
      [
          // Validation is disabled in production to remove the runtime overhead
          validateEpoxyModelUsage     : String.valueOf(buildType.name == 'debug'),
          requireHashCodeInEpoxyModels: "true",
          requireAbstractEpoxyModels  : "true",
          implicitlyAddAutoModels     : "true"  
      ]
}

对于拥有许多 Model 和贡献者的大型项目, 使用这些选项尤其有用. 在这些场景下, Model拥有标准模型极大地缩减了创建和使用 Model 时的错误.

In Java

候选情况下, 可使用@PackageEpoxyConfig包注解为每一个包下的 Model 指定配置选项. 在该包下创建package-info.java. 不过这种方式不能修改validateEpoxyModelUsage选项.

@PackageEpoxyConfig(
    requireAbstractModels = true,
    requireHashCode = true,
    implicitlyAddAutoModels = true
)
package com.example.app;

import com.airbnb.epoxy.PackageEpoxyConfig;

如果包下没有找到配置选项, 那么来自最近父包下的配置将被使用. 如果全部父包都没有声明配置, 那么设置在build.gradle文件里面的注解处理器将被使用. 如果build.gradle文件中也没有配置, 那么将使用默认值.

转载自:https://juejin.cn/post/7144173582698938381
评论
请登录