我有以下代码:
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)调用中得到空指针异常,stack:
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中没有警告。
将代码移动到某个函数并将视图作为参数发送。 在函数声明中,它声明了最终视图。现在,Android Studio突出显示条件" view!= null"总是假的(好吧,它在这条线上崩溃了,所以......:)
我想知道它是否与垃圾收集有关,以及它如何处理最终变量,或者它只是一个错误的检查错误。
答案 0 :(得分:1)
view.postDelayed(new Runnable() {
@Override
public void run() {
if (view != null) {
view.setPressed(false);
}
}
}, 50);
在您的代码中,如果view
为null
,您将在view.postDelayed()
方法调用中使用NPE,并且作为参考是最终的,它不能更改,因此在{{1它永远不能为null,有两个选项:
Runnable
为View
并在null
时崩溃,因此您无需在view.postDelayed
中查看Runnable
不是View
,因此您无需在null
答案 1 :(得分:1)
您没有提供足够的背景来明确诊断。但是,这是一种可能的情况,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()
方法中......但是你需要考虑是否存在涉及分配{{1}的任何内容的竞争条件到null
字段。
我想知道它是否与垃圾收集有关,以及它如何处理最终变量
没有。 GC不会干扰可到达的对象。 view
实例是可访问的,因为它已被捕获,并且捕获对象(匿名类实例)可通过MyClass
基础结构访问。
或者只是一个错误的检查错误。
这似乎是合理的......但(IMO)并非最可能的解释。
答案 2 :(得分:0)
如果view
是最终版并且postDelayed
行不在构造函数中,则view
保证已经初始化。它永远不会为空,因此view != null
将始终解析为true。它警告你这是一个毫无意义的检查。
唯一的例外是如果你有像
这样的东西private final Integer myInt;
MyCtor()
{
myInt = null;
}
这完全没有意义(但出于某种原因可能)。