我有一个由NPE导致崩溃的以下堆栈跟踪:
Fatal Exception: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.daybreak.my.app/com.daybreak.my.app.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ViewSwitcher.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2430)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2490)
at android.app.ActivityThread.access$900(ActivityThread.java:153)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1358)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5456)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:735)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:625)
Caused by java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ViewSwitcher.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
at com.daybreak.my.app.TimesFragment.onLocationChange(TimesFragment.java:446)
at com.daybreak.my.app.MainActivity.onLocationChange(MainActivity.java:289)
at com.daybreak.my.app.MainActivity.onCreate(MainActivity.java:112)
at android.app.Activity.performCreate(Activity.java:6302)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1108)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2383)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2490)
at android.app.ActivityThread.access$900(ActivityThread.java:153)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1358)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5456)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:735)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:625)
我设置应用的方式如下:
MainActivity
public class MainActivity extends AppCompatActivity implements LocationChangeListener {
@Override
public void onCreate(Bundle savedInstanceState) {
//...
onLocationChange(LocationManager.getSavedLocation(this)); // Manually calling onLocationChange() method
if (findViewById(R.id.fragment_container) != null) {
if (savedInstanceState != null) return;
showFragment(new TimesFragment(), TimesFragment.TAG);
}
}
@Override
public void onLocationChange(Locatin location) {
if (location == null) return;
//...
// Call attached onLocationChange() if it implements LocationChangeListener
Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
if (f instanceof LocationChangeListener)
((LocationChangeListener) f).onLocationChange(location);
}
}
TimesFragment
public class TimesFragment extends Fragment implements LocationChangeListener {
private ViewSwitcher viewSwitcher;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//...
viewSwitcher = (ViewSwitcher) view.findViewById(R.id.view_switcher);
}
@Override
public void onLocationChange(Location location) {
this.location = location;
viewSwitcher.setOnClickListener(null); //<-- NPE Cause here
updateContent();
}
}
我的期望
根据我的理解,Activity.onCreate()
只会在重新启动后或用户在应用程序被杀死后导航回应用程序之后被调用(当用户或其他应用程序需要内存时由内存管理人员明确指出)。如果发生这种情况,片段也将被破坏并且需要被创建,即片段的onCreateView()
将被调用。因此,在附加片段之前从onLocationChange()
调用MainActivity.onCreate()
是安全的,因为findFragmentById()
中的onLocationChange()
找不到任何片段。
现实
从堆栈跟踪中我们可以看到从MainActivity.onCreate()
发起的呼叫。但令我感到困惑的是,在onLocationChange()
内调用MainActivity.onCreate()
时,findFragmentById()
中的onLocationChange()
会在视图容器中找到片段并调用片段onLocationChange()
。发生这种情况时,viewSwitcher
为NULL
并导致应用崩溃。
显然,片段已经添加到视图容器中,片段onCreateView()
尚未被调用。
问题
我无法重新创建此崩溃,也无法确定导致此崩溃的生命周期过程。
所以有人能告诉我
答案 0 :(得分:0)
这是由设备旋转引起的。可以通过旋转设备重新创建堆栈跟踪。
注意:即使应用程序的方向被锁定(如我的情况)也会发生这种情况;如果用户所在的应用程序的方向与应用程序锁定的方向不同,并且他们切换回应用程序,则会触发应用程序的方向生命周期。
<强>解强>
在从片段调用方法之前添加f != null && f.isResumed()
。如果片段在重新播放后尚未恢复,则isResumed()
将返回false
。