简体   繁体   中英

Why am I getting a NullPointerException when trying to use the reference of Activity from a Fragment?

I have a method that does a Facebook call for a user's profile info. And one of parameters is a Context . I am passing in the Activity's context from a Fragment , but some of my users are getting a NullPointerException for the context.

I am saving the reference of the Activity in the onAttach method of my Fragment . Why is this giving me a NPE ?

The issue lies within this method:

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();
        }
    });

This callback is done asynchronously and I am nesting this async call inside a Runnable . I suspect the issue has to do with this. Can anyone explain why is this an issue?

Stacktrace

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$1$1.run(LoginFragment.java:209)
   at com.walintukai.lovelup.utils.FacebookHelper$1.onCompleted(FacebookHelper.java:129)
   at com.facebook.GraphRequest$1.onCompleted(GraphRequest.java:295)
   at com.facebook.GraphRequest$5.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)

This method is in my Facebook helper class that requests your FB info and saves the info to SharedPreferences. A runnable is passed in as a parameter because this method is used to other places.

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();
}

This is the Fragment that is calling the Facebook helper method above.

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();
        }
    });
}

}

Since the callback is asynchronous, it is possible the user has exited the fragment and the reference mActivity now points to null. On any asynchronous request make sure you check that mActivity != null and you will prevent this situation from happening.

If you want to persist the data even though the fragment or activity may be closed, create the SharedPreference object when you register the request:

    final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mActivity);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM