Android: абстрактное пользовательское представление и обычная разметка - PullRequest
0 голосов
/ 06 марта 2020

Итак, я провел некоторое исследование, и кажется, что просмотр инфляции в конструкторе init / абстрактного базового класса на самом деле не лучшая практика. Я понимаю, что это потому, что инициализатор базового класса происходит перед конструктором init / производного класса. Так как абстрактный класс не является финальным, есть хорошее сообщение 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) 

Я действительно не хочу добавлять что-либо к инициализации производного класса или делать myView переменную, допускающую переменную / переменную, которая устанавливается позже в абстрактном классе.

Кто-нибудь еще находит это немного расстраивает или есть какой-нибудь совет? Похоже, было бы весьма необычно хотеть раздувать тот же макет из базового класса.

1 Ответ

0 голосов
/ 07 марта 2020

Утечка this в конструкторе опасна, потому что объект, которому вы его пропускаете, может начать получать доступ к его элементам еще до завершения работы конструктора, поэтому он может быть не готов. Вы можете получить NPE даже для ненулевых свойств Kotlin или другого странного поведения.

В случае LayoutInflator.inflate это, похоже, не проблема, хотя бы потому, что построено Android -in-представления часто передают this как родительский метод inflate(). Например, конструктор DatePicker создает экземпляр DatePickerSpinnerDelegate, который передает этот экземпляр DatePicker в inflate(), причем все это происходит до того, как конструктор DatePicker вернулся.

Когда вы передаете представление в качестве родителя в inflate(), следуя вызову Я вижу две вещи, которые происходят с родителем. Он вызывает getContext() для этого родителя и вызывает addView() для этого родителя, если addToRoot имеет значение true. Поэтому я думаю, что утечка this безопасна до тех пор, пока вы не переопределите addView() для выполнения дополнительной работы, которая зависит от членов, которые вы настроили после вызова inflate(). But addView () also internally calls requestLayout () and invalidate () `, поэтому к ним относятся те же проблемы.

В большинстве случаев ваша пользовательская ViewGroup будет подклассом существующего Android класса ViewGroup, поэтому вам не нужно переопределять эти методы.

К сожалению, мы можем вывести это поведение, только проверив код. Не страшно заверять, что документация не гарантирует безопасность, но, насколько я знаю, мы просто должны признать, что это, вероятно, безопасно. Может быть, проблема должна быть открыта на AOSP. Это предупреждение даже не появляется, если вы пишете тот же код на Java, но риск тот же.

Подавление предупреждения не должно означать, что вы игнорируете предупреждение или просто взламываете свой код , Это означает: «Я подтверждаю режим сбоя и проверил, что мой код не будет таким образом сбой». Если бы это было не так, это была бы ошибка компилятора, а не предупреждение. В Kotlin можно использовать аннотацию подавления @Suppress("LeakingThis").

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...