У меня такой код:
view.setPressed(true);
view.postDelayed(new Runnable() {
@Override
public void run() {
view.setPressed(false);
}
}, 50);
Объявление переменной просто:
private View view;
Единственное место, где я присваиваю ему значение, находится внутри onInterceptTouchEvent:
view = parentView.findChildViewUnder(event.getX(), event.getY());
Из Crashlytics я получаю исключение нулевого указателя при вызове setPressed (false), стек:
Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.view
.View.setPressed(boolean)' on a null object reference
at com.MyApp.common.ui.RecyclerItemClickListener$GestureListener$1.run(SourceFile:119)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:158)
at android.app.ActivityThread.main(ActivityThread.java:7229)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
Я добавил нулевую проверку, и все выглядит нормально, никаких предупреждений в Android Studio.
Переместил код в какую-то функцию и отправил представление в качестве аргумента. В объявлении функции объявлено final View. Теперь Android Studio подчеркивает, что условие "view! = Null" всегда ложно (ну, в этой строке оно разбилось, так что .. :)
Интересно, связано ли это со сборкой мусора и как он работает с конечными переменными, или это просто ложная ошибка проверки.
3 ответа
Вы не предоставляете достаточно контекста для точного диагноза. Однако вот один возможный сценарий, в котором view
может быть null
в методе run()
.
Рассмотрим этот код:
public class MyClass {
private View view = ... // not null
public void someMethod() {
view.setPressed(true);
view.postDelayed(new Runnable() {
@Override
public void run() {
view.setPressed(false);
}
}, 50);
}
public void someOtherMethod() {
view = null;
}
}
Теперь предположим, что создается экземпляр MyClass
и вызывается someMethod()
, за которым следует someOtherMethod()
. Если последний вызов происходит до завершения задержки, тогда метод run()
будет видеть view
как null
.
Причина в том, что анонимный класс не захватывает view
. Скорее это захват ссылки на экземпляр MyObject
, членом которого является view
.
Предположительно, ваша проверка того, что view
не null
, выполняется в вызове someMethod()
(или аналоге в вашем коде)
Если вы объявите view
как final
, назначение в someOtherMethod()
будет предотвращено, а view
останется ненулевым.
Другой подход таков:
public void someMethod() {
final View localView = view;
localView.setPressed(true);
localView.postDelayed(new Runnable() {
@Override
public void run() {
localView.setPressed(false);
}
}, 50);
}
Здесь мы принудительно захватываем локальную переменную вместо (или так же) this
. Если затем присвоить поле this.view
, это не изменит захваченное значение localView
.
Или, что еще проще, поместите тест null
в метод run()
... хотя тогда вам нужно будет подумать, может ли быть состояние гонки, включающее то, что присваивает null
объекту Поле view
.
Интересно, связано ли это со сборкой мусора и как он работает с конечными переменными
Нет. Сборщик мусора не будет мешать доступным объектам. Экземпляр MyClass
доступен, потому что он был захвачен, а захватывающий объект (экземпляр анонимного класса) доступен через инфраструктуру postDelay
.
или это просто ложная ошибка проверки.
Это правдоподобно ... но (ИМО) не самое вероятное объяснение.
run()
.)
Если view
является окончательным, а строка postDelayed
отсутствует в конструкторе, view
гарантированно уже инициализирован. Он никогда не может быть нулевым, поэтому view != null
всегда принимает значение true. Предупреждает, что это бессмысленная проверка.
Единственное исключение из этого было бы, если бы у вас было что-то вроде
private final Integer myInt;
MyCtor()
{
myInt = null;
}
Что было бы совершенно бессмысленно (но почему-то возможно).
view.postDelayed(new Runnable() {
@Override
public void run() {
if (view != null) {
view.setPressed(false);
}
}
}, 50);
В вашем коде, если view
будет null
, у вас будет NPE в вызове метода view.postDelayed()
, и поскольку ссылка является окончательной, она не может быть изменена, поэтому в Runnable
она не может быть нулевым никогда, два варианта:
View
- этоnull
и вылетает наview.postDelayed
, поэтому вам не нужно проверять его черезRunnable
View
неnull
, поэтому вам не нужно проверять его черезRunnable
view
пока что не может быть установлен на null
, так как он должен быть установлен для вызова final
во "внутреннем объявлении" (не могу вспомнить имя JLS для этого).
Похожие вопросы
Новые вопросы
java
Java — это высокоуровневый объектно-ориентированный язык программирования. Используйте этот тег, если у вас возникли проблемы с использованием или пониманием самого языка. Этот тег часто используется вместе с другими тегами для библиотек и/или фреймворков, используемых разработчиками Java.
view
захватывается анонимным внутренним классом, поэтому не может иметь значение NULL в методеrun
.view
имеет в вашем редакторе 2 разных цвета. Возможно, одна из них - поле, а другая - локальная переменная?