为什么在尝试使用片段中的 Activity 引用时会出现 NullPointerException?
Why am I getting a NullPointerException when trying to use the reference of Activity from a Fragment?
我有一个方法可以调用 Facebook 来获取用户的个人资料信息。其中一个参数是 Context
。我正在从 Fragment
传递 Activity's context
,但我的一些用户正在为上下文获取 NullPointerException
。
我正在 Fragment
的 onAttach
方法中保存 Activity
的引用。为什么这给我 NPE
?
问题出在这个方法中:
fbLoginButton.registerCallback(mCallbackManager, new FacebookCallback<LoginResult>() {
@Override
public void onSuccess(LoginResult loginResult) {
Log.i("Facebook", "Logged In");
addLoadingFragment();
FacebookHelper.requestUserInfo(mActivity, loginResult.getAccessToken(), new Runnable() {
@Override
public void run() {
if (mEnteredCode != null) {
Log.i(TAG, "Logging in with entered activation code");
new VolleyHelper(mActivity).mLogin(mEnteredCode);
}
else {
Log.i(TAG, "Logging in with existing account");
new VolleyHelper(mActivity).mLogin(null);
}
}
});
}
@Override
public void onCancel() {
Log.i("Facebook", "Login Cancelled");
}
@Override
public void onError(FacebookException e) {
Log.i("Facebook", "Error: " + e.getLocalizedMessage());
ViewHelper.showCustomToast(getActivity(), e.getLocalizedMessage(), null);
addLoginFragment();
}
});
此回调已完成 asynchronously
,我将此异步调用嵌套在 Runnable
中。我怀疑这个问题与此有关。谁能解释为什么这是个问题?
堆栈跟踪
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.SharedPreferences android.content.Context.getSharedPreferences(java.lang.String, int)' on a null object reference
at com.walintukai.lovelup.utils.SharedPrefs.<init>(SharedPrefs.java:60)
at com.walintukai.lovelup.utils.VolleyHelper.<init>(VolleyHelper.java:42)
at com.walintukai.lovelup.fragments.LoginFragment.run(LoginFragment.java:209)
at com.walintukai.lovelup.utils.FacebookHelper.onCompleted(FacebookHelper.java:129)
at com.facebook.GraphRequest.onCompleted(GraphRequest.java:295)
at com.facebook.GraphRequest.run(GraphRequest.java:1243)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:5942)
at java.lang.reflect.Method.invoke(Method.java)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194)
此方法在我的 Facebook 助手 class 中,它请求您的 FB 信息并将信息保存到 SharedPreferences。传入一个runnable作为参数,因为这个方法用到其他地方
public static void requestUserInfo(final Context context, final AccessToken accessToken, final Runnable runnable) {
GraphRequest request = GraphRequest.newMeRequest(accessToken, new GraphRequest.GraphJSONObjectCallback() {
@Override
public void onCompleted(JSONObject jsonObject, GraphResponse graphResponse) {
String id = "";
String name = "";
String email = "";
String gender = "";
String birthdate = "";
String pictureUrl = "";
int timezone = 0;
if (jsonObject != null) {
try {
Log.i(TAG, "FB User: " + jsonObject.toString());
if (jsonObject.has("id")) {
id = jsonObject.getString("id");
pictureUrl = "https://graph.facebook.com/" + id + "/picture";
}
if (jsonObject.has("name")) {
name = jsonObject.getString("name");
}
if (jsonObject.has("email")) {
email = jsonObject.getString("email");
}
if (email.isEmpty()) {
email = id + "@facebook.com";
}
if (jsonObject.has("gender")) {
gender = jsonObject.getString("gender");
}
if (jsonObject.has("birthday")) {
String fbBirthday = jsonObject.getString("birthday");
try {
LocalDate localDate = LocalDate.parse(fbBirthday,
DateTimeFormat.forPattern("MM/dd/yyyy"));
LocalTime localTime = new LocalTime(0, 0, 0);
DateTime dateTime = localDate.toDateTime(localTime, DateTimeZone.UTC);
DateTimeFormatter formatter = ISODateTimeFormat.dateTime()
.withZone(DateTimeZone.UTC);
birthdate = formatter.print(dateTime);
}
catch (IllegalArgumentException e) { e.printStackTrace(); }
}
if (jsonObject.has("timezone")) {
timezone = jsonObject.getInt("timezone");
}
}
catch (JSONException e) { e.printStackTrace(); }
SharedPrefs prefs = new SharedPrefs(context);
prefs.setFbAccessToken(accessToken.getToken());
prefs.setFbId(id);
prefs.setFbFullName(name);
prefs.setFbEmail(email);
prefs.setFbGender(gender);
prefs.setFbBirthdate(birthdate);
prefs.setFbPictureUrl(pictureUrl);
prefs.setFbTimezone(timezone);
// Execute passed in runnable
runnable.run();
}
}
});
request.executeAsync();
}
这是调用上面 Facebook 帮助程序方法的 Fragment。
public class LoginFragment extends Fragment implements View.OnClickListener {
private static final String URL_SIGN_UP = "http://www.lovelup.net";
private static final long ANIMATION_LENGTH = 1000;
private static final String TAG = "LoginFragment";
private ViewPager viewPager;
private CirclePageIndicator circlePageIndicator;
private LinearLayout loginContainer;
private LoginButton fbLoginButton;
private LinearLayout activationContainer;
private CustomEditText etActivationCode;
private CallbackManager mCallbackManager;
private Activity mActivity;
private SharedPrefs mPrefs;
private FragmentManager mFm;
private String mEnteredCode;
private MixpanelHelper mMixpanelHelper;
public static LoginFragment newInstance() {
return new LoginFragment();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_login, container, false);
viewPager = (ViewPager) view.findViewById(R.id.viewpager);
circlePageIndicator = (CirclePageIndicator) view.findViewById(R.id.page_indicator);
loginContainer = (LinearLayout) view.findViewById(R.id.login_container);
fbLoginButton = (LoginButton) view.findViewById(R.id.fb_login_btn);
ImageButton steamLoginButton = (ImageButton) view.findViewById(R.id.steam_login_btn);
activationContainer = (LinearLayout) view.findViewById(R.id.activation_container);
etActivationCode = (CustomEditText) view.findViewById(R.id.et_activation_code);
CustomButton btnActivate = (CustomButton) view.findViewById(R.id.btn_activate);
CustomButton btnAlreadyActivated = (CustomButton) view.findViewById(R.id.btn_already_activated);
CustomTextView btnGetCode = (CustomTextView) view.findViewById(R.id.btn_get_code);
ViewHelper.setupTouchListenerToHideKeyboard(view, getActivity());
steamLoginButton.setOnClickListener(this);
btnActivate.setOnClickListener(this);
btnAlreadyActivated.setOnClickListener(this);
btnGetCode.setOnClickListener(this);
if (mActivity != null) Utils.clearSavedInfo(mActivity);
setupActionBar();
setupFacebookLoginButton();
setupViewPager();
setupKeyboard();
showCorrectContainer();
((MainActivity)getActivity()).showCallToActionBanner();
return view;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mActivity = activity;
}
@Override
public void onDestroy() {
mActivity = null;
super.onDestroy();
}
private void setupFacebookLoginButton() {
FacebookHelper.getFacebookKeyHash(getActivity());
fbLoginButton.setFragment(this);
fbLoginButton.setReadPermissions(Arrays.asList("public_profile", "email", "user_birthday"));
fbLoginButton.registerCallback(mCallbackManager, new FacebookCallback<LoginResult>() {
@Override
public void onSuccess(LoginResult loginResult) {
Log.i("Facebook", "Logged In");
addLoadingFragment();
FacebookHelper.requestUserInfo(mActivity, loginResult.getAccessToken(), new Runnable() {
@Override
public void run() {
if (mEnteredCode != null) {
Log.i(TAG, "Logging in with entered activation code");
new VolleyHelper(mActivity).mLogin(mEnteredCode);
}
else {
Log.i(TAG, "Logging in with existing account");
new VolleyHelper(mActivity).mLogin(null);
}
}
});
}
@Override
public void onCancel() {
Log.i("Facebook", "Login Cancelled");
}
@Override
public void onError(FacebookException e) {
Log.i("Facebook", "Error: " + e.getLocalizedMessage());
ViewHelper.showCustomToast(getActivity(), e.getLocalizedMessage(), null);
addLoginFragment();
}
});
}
}
由于回调是异步的,因此用户可能已经退出片段并且引用 mActivity 现在指向 null。在任何异步请求中,请确保检查 mActivity != null 并防止这种情况发生。
如果您想在片段或 activity 可能关闭时保留数据,请在注册请求时创建 SharedPreference 对象:
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mActivity);
我有一个方法可以调用 Facebook 来获取用户的个人资料信息。其中一个参数是 Context
。我正在从 Fragment
传递 Activity's context
,但我的一些用户正在为上下文获取 NullPointerException
。
我正在 Fragment
的 onAttach
方法中保存 Activity
的引用。为什么这给我 NPE
?
问题出在这个方法中:
fbLoginButton.registerCallback(mCallbackManager, new FacebookCallback<LoginResult>() {
@Override
public void onSuccess(LoginResult loginResult) {
Log.i("Facebook", "Logged In");
addLoadingFragment();
FacebookHelper.requestUserInfo(mActivity, loginResult.getAccessToken(), new Runnable() {
@Override
public void run() {
if (mEnteredCode != null) {
Log.i(TAG, "Logging in with entered activation code");
new VolleyHelper(mActivity).mLogin(mEnteredCode);
}
else {
Log.i(TAG, "Logging in with existing account");
new VolleyHelper(mActivity).mLogin(null);
}
}
});
}
@Override
public void onCancel() {
Log.i("Facebook", "Login Cancelled");
}
@Override
public void onError(FacebookException e) {
Log.i("Facebook", "Error: " + e.getLocalizedMessage());
ViewHelper.showCustomToast(getActivity(), e.getLocalizedMessage(), null);
addLoginFragment();
}
});
此回调已完成 asynchronously
,我将此异步调用嵌套在 Runnable
中。我怀疑这个问题与此有关。谁能解释为什么这是个问题?
堆栈跟踪
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.SharedPreferences android.content.Context.getSharedPreferences(java.lang.String, int)' on a null object reference
at com.walintukai.lovelup.utils.SharedPrefs.<init>(SharedPrefs.java:60)
at com.walintukai.lovelup.utils.VolleyHelper.<init>(VolleyHelper.java:42)
at com.walintukai.lovelup.fragments.LoginFragment.run(LoginFragment.java:209)
at com.walintukai.lovelup.utils.FacebookHelper.onCompleted(FacebookHelper.java:129)
at com.facebook.GraphRequest.onCompleted(GraphRequest.java:295)
at com.facebook.GraphRequest.run(GraphRequest.java:1243)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:5942)
at java.lang.reflect.Method.invoke(Method.java)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194)
此方法在我的 Facebook 助手 class 中,它请求您的 FB 信息并将信息保存到 SharedPreferences。传入一个runnable作为参数,因为这个方法用到其他地方
public static void requestUserInfo(final Context context, final AccessToken accessToken, final Runnable runnable) {
GraphRequest request = GraphRequest.newMeRequest(accessToken, new GraphRequest.GraphJSONObjectCallback() {
@Override
public void onCompleted(JSONObject jsonObject, GraphResponse graphResponse) {
String id = "";
String name = "";
String email = "";
String gender = "";
String birthdate = "";
String pictureUrl = "";
int timezone = 0;
if (jsonObject != null) {
try {
Log.i(TAG, "FB User: " + jsonObject.toString());
if (jsonObject.has("id")) {
id = jsonObject.getString("id");
pictureUrl = "https://graph.facebook.com/" + id + "/picture";
}
if (jsonObject.has("name")) {
name = jsonObject.getString("name");
}
if (jsonObject.has("email")) {
email = jsonObject.getString("email");
}
if (email.isEmpty()) {
email = id + "@facebook.com";
}
if (jsonObject.has("gender")) {
gender = jsonObject.getString("gender");
}
if (jsonObject.has("birthday")) {
String fbBirthday = jsonObject.getString("birthday");
try {
LocalDate localDate = LocalDate.parse(fbBirthday,
DateTimeFormat.forPattern("MM/dd/yyyy"));
LocalTime localTime = new LocalTime(0, 0, 0);
DateTime dateTime = localDate.toDateTime(localTime, DateTimeZone.UTC);
DateTimeFormatter formatter = ISODateTimeFormat.dateTime()
.withZone(DateTimeZone.UTC);
birthdate = formatter.print(dateTime);
}
catch (IllegalArgumentException e) { e.printStackTrace(); }
}
if (jsonObject.has("timezone")) {
timezone = jsonObject.getInt("timezone");
}
}
catch (JSONException e) { e.printStackTrace(); }
SharedPrefs prefs = new SharedPrefs(context);
prefs.setFbAccessToken(accessToken.getToken());
prefs.setFbId(id);
prefs.setFbFullName(name);
prefs.setFbEmail(email);
prefs.setFbGender(gender);
prefs.setFbBirthdate(birthdate);
prefs.setFbPictureUrl(pictureUrl);
prefs.setFbTimezone(timezone);
// Execute passed in runnable
runnable.run();
}
}
});
request.executeAsync();
}
这是调用上面 Facebook 帮助程序方法的 Fragment。
public class LoginFragment extends Fragment implements View.OnClickListener {
private static final String URL_SIGN_UP = "http://www.lovelup.net";
private static final long ANIMATION_LENGTH = 1000;
private static final String TAG = "LoginFragment";
private ViewPager viewPager;
private CirclePageIndicator circlePageIndicator;
private LinearLayout loginContainer;
private LoginButton fbLoginButton;
private LinearLayout activationContainer;
private CustomEditText etActivationCode;
private CallbackManager mCallbackManager;
private Activity mActivity;
private SharedPrefs mPrefs;
private FragmentManager mFm;
private String mEnteredCode;
private MixpanelHelper mMixpanelHelper;
public static LoginFragment newInstance() {
return new LoginFragment();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_login, container, false);
viewPager = (ViewPager) view.findViewById(R.id.viewpager);
circlePageIndicator = (CirclePageIndicator) view.findViewById(R.id.page_indicator);
loginContainer = (LinearLayout) view.findViewById(R.id.login_container);
fbLoginButton = (LoginButton) view.findViewById(R.id.fb_login_btn);
ImageButton steamLoginButton = (ImageButton) view.findViewById(R.id.steam_login_btn);
activationContainer = (LinearLayout) view.findViewById(R.id.activation_container);
etActivationCode = (CustomEditText) view.findViewById(R.id.et_activation_code);
CustomButton btnActivate = (CustomButton) view.findViewById(R.id.btn_activate);
CustomButton btnAlreadyActivated = (CustomButton) view.findViewById(R.id.btn_already_activated);
CustomTextView btnGetCode = (CustomTextView) view.findViewById(R.id.btn_get_code);
ViewHelper.setupTouchListenerToHideKeyboard(view, getActivity());
steamLoginButton.setOnClickListener(this);
btnActivate.setOnClickListener(this);
btnAlreadyActivated.setOnClickListener(this);
btnGetCode.setOnClickListener(this);
if (mActivity != null) Utils.clearSavedInfo(mActivity);
setupActionBar();
setupFacebookLoginButton();
setupViewPager();
setupKeyboard();
showCorrectContainer();
((MainActivity)getActivity()).showCallToActionBanner();
return view;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mActivity = activity;
}
@Override
public void onDestroy() {
mActivity = null;
super.onDestroy();
}
private void setupFacebookLoginButton() {
FacebookHelper.getFacebookKeyHash(getActivity());
fbLoginButton.setFragment(this);
fbLoginButton.setReadPermissions(Arrays.asList("public_profile", "email", "user_birthday"));
fbLoginButton.registerCallback(mCallbackManager, new FacebookCallback<LoginResult>() {
@Override
public void onSuccess(LoginResult loginResult) {
Log.i("Facebook", "Logged In");
addLoadingFragment();
FacebookHelper.requestUserInfo(mActivity, loginResult.getAccessToken(), new Runnable() {
@Override
public void run() {
if (mEnteredCode != null) {
Log.i(TAG, "Logging in with entered activation code");
new VolleyHelper(mActivity).mLogin(mEnteredCode);
}
else {
Log.i(TAG, "Logging in with existing account");
new VolleyHelper(mActivity).mLogin(null);
}
}
});
}
@Override
public void onCancel() {
Log.i("Facebook", "Login Cancelled");
}
@Override
public void onError(FacebookException e) {
Log.i("Facebook", "Error: " + e.getLocalizedMessage());
ViewHelper.showCustomToast(getActivity(), e.getLocalizedMessage(), null);
addLoginFragment();
}
});
}
}
由于回调是异步的,因此用户可能已经退出片段并且引用 mActivity 现在指向 null。在任何异步请求中,请确保检查 mActivity != null 并防止这种情况发生。
如果您想在片段或 activity 可能关闭时保留数据,请在注册请求时创建 SharedPreference 对象:
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mActivity);