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

标签 android kotlin android-view android-custom-view

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

这就是我所追求的:

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

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

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

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

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

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

关于Android:抽象自定义 View 和常见布局膨胀,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60567981/

相关文章:

android - 调用 BroadcastReceiver 时如何解锁屏幕?

kotlin - Unresolved reference :jvmName

java - 错误: The system cannot find the file specified. Jenkins

android - 如何获得我的 TextView 的精确位置(以像素为单位)?

Android 警报对话框 : TYPE_SYSTEM_ALERT

java - 使用 JNI 从 Android 应用程序向 C++ 发送字符串

android - 退出对话框 fragment 后如何更新 MainActivity 中的 TextView?

android - RecyclerView 是否有等效的 addHeaderView?

android - 如何检查android中是否安装了日历应用程序?

postgresql - Kotlin Exposed/Postgresql 在查询中将我的表名小写;如何使用大写的表名?