Android:抽象自定义视图和常见布局膨胀

isu*_*hes 2 android android-custom-view android-view kotlin

因此,我做了一些研究,似乎在init抽象基类的/构造函数中查看膨胀并不是真正的最佳实践。我明白这是因为基类初始值设定项发生在init派生类的 /构造函数之前。由于抽象类是非最终类,所以有一个关于块this中泄漏的不错的 IDE 消息init。

这就是我所追求的:

abstract class Foo @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {

    private val myView: View

    init {
        // todo@patches fix leaking "this"
        View.inflate(context, R.layout.view_foo, this)
        myView = requireNotNull(findViewById(R.id.my_view))
    }
}
Run Code Online (Sandbox Code Playgroud)
class Bar @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : Foo(context, attrs, defStyleAttr) 
Run Code Online (Sandbox Code Playgroud)

我真的不想向派生类的 init 添加任何内容,或者创建myView稍后在抽象类中设置的可为空/可变变量。

还有其他人觉得这有点令人沮丧或有什么建议吗?似乎想要从基类膨胀相同的布局并不罕见。

Ten*_*r04 5

构造函数中的泄漏this是危险的,因为您将其泄漏到的对象可能在构造函数完成之前就开始访问其成员,因此它可能尚未准备好。即使对于不可为 null 的 Kotlin 属性或其他奇怪的行为,您也可能会获得 NPE。

在 的情况下LayoutInflator.inflate,这似乎不是问题,因为 Android 的内置视图经常this作为父级传递给该inflate()方法。例如,DatePicker 的构造函数实例化一个 DatePickerSpinnerDelegate,它将该 DatePicker 实例传递给inflate(),所有这些都发生在 DatePicker 的构造函数返回之前。

当您将视图作为父级传递给 时inflate(),通过跟踪调用链,我看到父级发生了两件事。它调用该父级,如果为 true,getContext()则调用addView()该父级。addToRoot因此,我认为this只要您不重写以执行依赖于您在调用addView() requestLayout() invalidate()`addView()后设置的成员的额外工作,泄漏就是安全的,因此同样的问题也适用于这些。inflate(). Butalso internally callsand

在大多数情况下,您的自定义 ViewGroup 将是现有 Android ViewGroup 类的子类,因此您不需要重写这些方法。

不幸的是,我们只能通过检查代码来推断这种行为。文档不能保证它的安全性,这并不能令人非常放心,但据我所知,我们只能接受它可能是安全的。也许应该在 AOSP 上提出一个问题。如果您用 Java 编写相同的代码,则该警告甚至不会出现,但风险是相同的。

抑制警告并不意味着您忽略警告或只是破坏您的代码。这意味着,“我承认故障模式并已检查我的代码不会以这种方式失败。” 如果不是这种情况,则将是编译器错误,而不是警告。在 Kotlin 中,您可以使用的抑制注释是@Suppress("LeakingThis").