为什么在尝试使用片段中的 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

我正在 FragmentonAttach 方法中保存 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);