简体   繁体   中英

Facebook Android SDK 3.0 always returns OPENING when logging in

I followed the instructions at https://developers.facebook.com/docs/howtos/androidsdk/3.0/login-with-facebook/ . When I touch the "Log In"-button, the session state is OPENING - always. When I try to post a story to the user's wall I get an error stating

"Session: an attempt was made to request new permissions for a session that has a pending request."

Here is my code:

public class MainFragment extends Fragment {
    private static final String TAG = "MainFragment";
    private UiLifecycleHelper uiHelper;

    private Session.StatusCallback callback = new Session.StatusCallback() {
        @Override
        public void call(Session session, SessionState state,
                Exception exception) {
            onSessionStateChange(session, state, exception);
        }
    };

    private void onSessionStateChange(Session session, SessionState state,
            Exception exception) {
        if (state.isOpened()) {
            Log.d(TAG, "Logged in...");
        } else if (state.isClosed()) {
            Log.d(TAG, "Logged out...");
        } else {
            Log.d(TAG, "Unknown state: " + state);
        }
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        uiHelper = new UiLifecycleHelper(getActivity(), callback);
        uiHelper.onCreate(savedInstanceState);
    }

    @Override
    public void onResume() {
        super.onResume();

        // For scenarios where the main activity is launched and user
        // session is not null, the session state change notification
        // may not be triggered. Trigger it if it's open/closed.
        Session session = Session.getActiveSession();
        if (session != null && (session.isOpened() || session.isClosed())) {
            onSessionStateChange(session, session.getState(), null);
        }

        uiHelper.onResume();
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        uiHelper.onActivityResult(requestCode, resultCode, data);
    }

    @Override
    public void onPause() {
        super.onPause();
        uiHelper.onPause();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        uiHelper.onDestroy();
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        uiHelper.onSaveInstanceState(outState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.facebook_login, container, false);
        LoginButton authButton = (LoginButton) view
                .findViewById(R.id.authButton);
        authButton.setFragment(this);

        return view;
    }

    private boolean isSubsetOf(Collection<String> subset,
            Collection<String> superset) {
        for (String string : subset) {
            if (!superset.contains(string)) {
                return false;
            }
        }
        return true;
    }

    private static final List<String> PERMISSIONS = Arrays
            .asList("publish_actions");
    private static final String PENDING_PUBLISH_KEY = "pendingPublishReauthorization";
    private boolean pendingPublishReauthorization = false;

    public void publishStory() {
        Session session = Session.getActiveSession();

        if (session != null) {

            // Check for publish permissions
            List<String> permissions = session.getPermissions();
            if (!isSubsetOf(PERMISSIONS, permissions)) {
                pendingPublishReauthorization = true;
                Session.NewPermissionsRequest newPermissionsRequest = new Session.NewPermissionsRequest(
                        this, PERMISSIONS);
                session.requestNewPublishPermissions(newPermissionsRequest);
                return;
            }

            Bundle postParams = new Bundle();
            postParams.putString("name", "Facebook SDK for Android");
            postParams.putString("caption",
                    "Build great social apps and get more installs.");
            postParams
                    .putString(
                            "description",
                            "The Facebook SDK for Android makes it easier and faster to develop Facebook integrated Android apps.");
            postParams.putString("link",
                    "https://developers.facebook.com/android");
            postParams
                    .putString("picture",
                            "https://raw.github.com/fbsamples/ios-3.x-howtos/master/Images/iossdk_logo.png");

            Request.Callback callback = new Request.Callback() {
                public void onCompleted(Response response) {
                    JSONObject graphResponse = response.getGraphObject()
                            .getInnerJSONObject();
                    String postId = null;
                    try {
                        postId = graphResponse.getString("id");
                    } catch (JSONException e) {
                        Log.i(TAG, "JSON error " + e.getMessage());
                    }
                    FacebookRequestError error = response.getError();
                    if (error != null) {
                        Toast.makeText(getActivity().getApplicationContext(),
                                error.getErrorMessage(), Toast.LENGTH_SHORT)
                                .show();
                    } else {
                        Toast.makeText(getActivity().getApplicationContext(),
                                postId, Toast.LENGTH_LONG).show();
                    }
                }
            };

            Request request = new Request(session, "me/feed", postParams,
                    HttpMethod.POST, callback);

            RequestAsyncTask task = new RequestAsyncTask(request);
            task.execute();
        }

    }
}

What's wrong?

Edit: I'd also like to mention that the text on the "Log In"-Button doesn't change. I don't get an error while authenticating, but as far as I read it should change to "Log Out".

It wasn't easy to figure this out. :-)

If you actually check out exception in onSessionStateChange() , you'll see that it's not really simply stuck in OPENING . That exception contains: java.lang.UnsupportedOperationException: Session: an attempt was made to open a session that has a pending request.

The reason I finally found was that onActivityResult() wasn't called. It's its responsibility to call back to uiHelper and that call will call the finishing part of the login, so it if isn't called, the state stays OPENING forever. Apart from the obvious pieces of advice above, this is what I found and they are all needed for the login to work:

Make sure you use both a fragment and an activity, as described in the FB documentation. Even if the doc hints at an activity being enough, it isn't. You need both even if the activity has nothing else but calling the single fragment.

Make sure you have no history set for the activity (neither in the manifest nor in code).

Make sure you override the fragment's onActivityResult() as per the documentation. And make sure you override the same in the activity as well:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
  super.onActivityResult(requestCode, resultCode, data);
}

Yes, this seems strange but it doesn't work without it.

Finally this works for me. The key points was to add

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    uiHelper.onActivityResult(requestCode, resultCode, data);
}

and

        loginBtn.setFragment(this);
    loginBtn.setUserInfoChangedCallback(new LoginButton.UserInfoChangedCallback() {
        @Override
        public void onUserInfoFetched(GraphUser user) {
            graphUser = user;
        }
    });

Code inside Fragment:

private GraphUser graphUser;
private UiLifecycleHelper uiHelper;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    uiHelper = new UiLifecycleHelper(getActivity(), callback);
    uiHelper.onCreate(savedInstanceState);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    return inflater.inflate(R.layout.test_frame, container, false);
}

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    view.findViewById(R.id.getMeBtn).setOnClickListener(this);

    LoginButton loginBtn = (LoginButton) view.findViewById(R.id.loginBtn);
    loginBtn.setFragment(this);
    loginBtn.setUserInfoChangedCallback(new LoginButton.UserInfoChangedCallback() {
        @Override
        public void onUserInfoFetched(GraphUser user) {
            graphUser = user;
        }
    });
}

@Override
public void onResume() {
    super.onResume();
    uiHelper.onResume();

    updateUI();
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    uiHelper.onSaveInstanceState(outState);
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    uiHelper.onActivityResult(requestCode, resultCode, data);
}

@Override
public void onPause() {
    super.onPause();
    uiHelper.onPause();
}

@Override
public void onDestroy() {
    super.onDestroy();
    uiHelper.onDestroy();
}

private Session.StatusCallback callback = new Session.StatusCallback() {
    @Override
    public void call(Session session, SessionState state, Exception exception) {
        onSessionStateChange(session, state, exception);
    }
};

private void onSessionStateChange(Session session, SessionState state, Exception exception) {
    updateUI();
}

private void updateUI() {
    if (graphUser != null) {
        Log.d("TEST", graphUser.getFirstName());
    }
}

@Override
public void onClick(View view) {
    Session session = Session.getActiveSession();
    Log.d("TEST", "session.getState() = " + session.getState());

    if (session.getState().isOpened()) {
        Request.executeMeRequestAsync(session, new Request.GraphUserCallback() {

            @Override
            public void onCompleted(GraphUser user, Response response) {
                if (user != null) {
                    updateUI();
                }
            }
        });
    }
}

I had the same issue. The state was always OPENING . Turns out that @Gabor was wright.

When the session state is OPENING a method onActivityResult gets called, that's for sure.

If you are using a fragment, onActivityResult is called in the ACTIVITY that holds the fragment, and not in the fragment. That is due to the passed context etc.

So i did it like this:

  1. Check what onActivityResults gets called (by logcat) - just to make sure

  2. In that method add a line Session.getActiveSession().onActivityResult(this, requestCode, resultCode, data); Notice that the parameter this is an activity context.

    OR

    Use the UiLifecycleHelper 's onActivityResult method, like this: UiHelper.onActivityResult(requestCode, resultCode, data); More about it here

If anyone else is still having this issue it can be caused by overriding onActivityResult in the Activity and not calling the super method.

Activity

protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
    /* other code here */
    super.onActivityResult(requestCode, resultCode, data);
}

Fragment

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    uiHelper.onActivityResult(requestCode, resultCode, data);
    super.onActivityResult(requestCode, resultCode, data);
}

You should implement onActivityResult in the activity not in the fragment. Worked for me.

Yet another reason this might occur (as it has for me) is when you're using getChildFragmentManager() - onActivityResult is not passed to nested Fragments, due to a bug . This (having the hosting Fragment pass its received onActivityResult to all children) is what helped me.

My Activity's onActivityResult is being invoked... So I did a very very bad hacking there:

public void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == 64206) //Bad hacking
            if(df!=null)
                df.onActivityResult(requestCode, resultCode, data);

df is my fragment. That made the session get open... this is friggin crazy. Very interested to find a better solution for this.

In my case, I was trying to change the fragment class from android.support.v4.app.Fragment to android.app.Fragment (I was not supporting pre API-11 Android versions) and omitted the FBAuthButton.setFragment(this); statement. That was actually causing the problem.

我遇到了同样的问题,我卸载了我的应用程序并重新安装,它工作了!

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