繁体   English   中英

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

[英]Android: Abstract custom view and common layout inflation

所以,我做了一些研究,似乎在抽象基类的init / 构造函数中查看膨胀并不是真正的最佳实践。 我明白这是因为基类初始化器发生在派生类的init /constructor 之前。 由于抽象类非最终有大约一个不错的IDE消息this在泄漏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))
    }
}
class Bar @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : Foo(context, attrs, defStyleAttr) 

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

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

在构造函数中泄漏this是危险的,因为你泄漏它的对象可能在构造函数完成之前就开始访问它的成员,所以它可能还没有准备好。 即使对于不可为空的 Kotlin 属性或其他奇怪的行为,您也可以获得 NPE。

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

当您将视图作为父视图传递给inflate() ,通过遵循调用链,我看到父视图发生了两件事。 它在该父级上调用getContext() ,如果addToRoot为真,则在该父addToRoot上调用addView() 因此,我认为只要您不覆盖addView()以执行依赖于您在调用addView()后设置的成员的其他工作,泄漏this是安全的inflate(). But inflate(). But addView() also internally calls requestLayout() and invalidate()`,所以同样的问题也适用于它们。

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

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

抑制警告不应该意味着您忽略警告或只是对您的代码进行黑客攻击。 这意味着,“我承认失败模式并检查我的代码不会以这种方式失败。” 如果不是这种情况,那将是编译器错误,而不是警告。 在 Kotlin 中,您可以使用的抑制注释是@Suppress("LeakingThis")

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM