Facebook Android SDK 3.0 всегда возвращает OPENING при входе в систему

Я следовал инструкциям на странице https://developers.facebook.com/docs/howtos/androidsdk/3.0/login-with-facebook/. Когда я касаюсь кнопки «Войти», состояние сеанса ОТКРЫВАЕТСЯ - всегда. Когда я пытаюсь опубликовать историю на стене пользователя, я получаю сообщение об ошибке

«Сеанс: была предпринята попытка запросить новые разрешения для сеанса, для которого есть ожидающий запрос».

Вот мой код:

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

    }
}

Что случилось?

Изменить: я также хотел бы отметить, что текст на кнопке «Войти» не меняется. Я не получаю ошибку при аутентификации, но, насколько я читал, она должна измениться на «Выход из системы».


person laroe    schedule 10.01.2013    source источник
comment
Вы используете систему единого входа? У вас установлено приложение facebook? Вы действительно проходите через весь рабочий процесс авторизации приложения? Ошибка, которую вы видите, предполагает, что рабочий процесс авторизации приложения никогда не завершался (и onActivityResult вашего фрагмента никогда не вызывался), поэтому сеанс все еще считает, что есть ожидающий запрос.   -  person Ming Li    schedule 10.01.2013
comment
Да, я использую SSO, и у меня установлено приложение. Поэтому, когда я вхожу в систему из своего приложения, мне обычно не нужно проходить рабочий процесс авторизации, хотя в первый раз появляется диалоговое окно с запросом разрешений.   -  person laroe    schedule 11.01.2013
comment
Хм, интересно. Когда вы впервые авторизовали приложение (когда появилось диалоговое окно), получали ли вы какие-либо обратные вызовы, говорящие, что сеанс ОТКРЫТ? Поскольку после авторизации приложения код, который у вас есть, должен автоматически открывать сеанс при последующих перезапусках приложения.   -  person Ming Li    schedule 11.01.2013
comment
Обычно отсутствие перехода состояния указывает на то, что результат не передается в сеанс (т.е. onActivityResult не вызывается). Но ваш код выглядит нормально, и очевидных проблем нет. Можете ли вы попробовать удалить свое приложение с устройства, переустановить его, пройти авторизацию (где вы получаете диалог) и убедиться, что onActivityResult вызывается в вашем фрагменте?   -  person Ming Li    schedule 11.01.2013
comment
onCreate, onCreateView и onResume вызываются в MainFragment. Ничего больше.   -  person laroe    schedule 17.02.2013
comment
Итак, по какой-то причине onActivityResult не вызывается для вашего фрагмента. Можете ли вы опубликовать свой код активности (в частности, где вы создаете фрагмент)? В качестве альтернативы вы можете попытаться переопределить onActivityResult в своей деятельности и вызвать Session.getActiveSession().onActivityResult.   -  person Ming Li    schedule 18.02.2013
comment
@Override public void onCreate (Bundle saveInstanceState) { super.onCreate (savedInstanceState); // Facebook if (savedInstanceState == null) { // Добавляем фрагмент при начальной настройке активности mainFragment = new MainFragment(); getSupportFragmentManager().beginTransaction() .add(android.R.id.content, mainFragment).commit(); } else { // Или установить фрагмент из информации о восстановленном состоянии mainFragment = (MainFragment) getSupportFragmentManager() .findFragmentById(android.R.id.content); }   -  person laroe    schedule 18.02.2013
comment
onActivityResult не вызывается ни в MainFragment, ни в моей MainActivity.   -  person laroe    schedule 18.02.2013
comment
Исправление: onActivityResult вызывается в моем MainActivity. Однако я добавил authButton.setFragment(this); в MainFragment, поэтому onActivityResult следует вызывать там в соответствии с документацией.   -  person laroe    schedule 19.02.2013
comment
Хорошо, я нашел ошибку... Моя MainActivity не является подклассом FragmentActivity, а является неким базовым классом, который я создал для своего приложения. Однако этот базовый класс является подклассом FragmentActivity. Я предполагаю, что что-то было потеряно из-за такой установки. Спасибо за ваше время, @ming-li, вы как-то указали мне правильное направление ;-)   -  person laroe    schedule 19.02.2013
comment
С этим тоже борюсь. Если вы на самом деле проверите exception в onSessionStateChange(), вы увидите, что на самом деле оно не просто застряло в OPENING. Это исключение содержит: java.lang.UnsupportedOperationException: Session: была предпринята попытка открыть сеанс с ожидающим запросом. Это предполагает, что обратный вызов возвращается быстрее, чем фактически мог произойти вход в систему.   -  person Gábor    schedule 24.01.2014


Ответы (9)


Разобраться в этом было непросто. :-)

Если вы на самом деле проверите exception в onSessionStateChange(), вы увидите, что на самом деле он не просто застрял в OPENING. Это исключение содержит: java.lang.UnsupportedOperationException: Session: была предпринята попытка открыть сеанс с ожидающим запросом.

Причина, которую я наконец нашел, заключалась в том, что onActivityResult() не вызывался. Он отвечает за обратный вызов uiHelper, и этот вызов вызовет завершающую часть входа в систему, поэтому, если он не вызывается, состояние остается OPENING навсегда. Помимо очевидных советов выше, вот что я нашел, и они все необходимы для работы входа в систему:

Убедитесь, что вы используете и фрагмент, и действие, как описано в документации FB. Даже если документ намекает на то, что активности достаточно, это не так. Вам нужны оба, даже если в действии нет ничего, кроме вызова одного фрагмента.

Убедитесь, что у вас не установлена ​​история активности (ни в манифесте, ни в коде).

Убедитесь, что вы переопределяете onActivityResult() фрагмента в соответствии с документацией. И убедитесь, что вы переопределяете то же самое в действии:

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

Да, это кажется странным, но без этого не работает.

person Gábor    schedule 24.01.2014

Наконец это работает для меня. Ключевыми моментами было добавить

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

а также

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

Код внутри фрагмента:

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();
                }
            }
        });
    }
}
person Roger Alien    schedule 01.05.2013

Я была такая же проблема. Состояние всегда было OPENING. Оказывается, @Gabor был Райтом.

Когда состояние сеанса равно OPENING, вызывается метод onActivityResult, это точно.

Если вы используете фрагмент, onActivityResult вызывается в АКТИВНОСТИ, которая содержит фрагмент, а не во фрагменте. Это связано с переданным контекстом и т.д.

Итак, я сделал это так:

  1. Проверьте, что вызывается onActivityResults (logcat) - просто чтобы убедиться

  2. В этом методе добавьте строку Session.getActiveSession().onActivityResult(this, requestCode, resultCode, data); Обратите внимание, что параметр this является контекстом действия.

    ИЛИ

    Используйте метод onActivityResult UiLifecycleHelper, например: UiHelper.onActivityResult(requestCode, resultCode, data);Подробнее об этом здесь

person mdzeko    schedule 25.07.2014

Если у кого-то еще есть эта проблема, это может быть вызвано переопределением onActivityResult в действии и отсутствием вызова суперметода.

Деятельность

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

Фрагмент

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    uiHelper.onActivityResult(requestCode, resultCode, data);
    super.onActivityResult(requestCode, resultCode, data);
}
person Steven H    schedule 29.11.2013

Вы должны реализовать onActivityResult в действии, а не во фрагменте. Работал на меня.

person kanaqsasak    schedule 26.02.2013
comment
Этот ответ дан в другом месте и не имеет отношения к вопросу ОП. Тем не менее, это распространенная ошибка, когда это результат. - person Thomas Dignan; 16.04.2013
comment
Ну, действительно вызывается onActivityResult АКтивности, а не Фрагмента... вот и у меня так же происходит... Что тогда делать?? - person Felipe Caldas; 31.05.2013
comment
Эта четкая краткая заметка полностью спасла мой нерабочий код. Я больше нигде не видел этого ответа. Спасибо, что добавили его сюда! - person Gene Bo; 27.02.2015

Еще одна причина, по которой это может произойти (как и у меня), заключается в том, что вы используете getChildFragmentManager() — onActivityResult не передается во вложенные фрагменты из-за ошибка. Мне помогло Это (передача полученного фрагмента хостинга onActivityResult всем дочерним элементам).

person kaay    schedule 17.07.2014

Моя активность onActivityResult вызывается... Итак, я сделал очень плохой взлом:

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

df - мой фрагмент. Это заставило сессию открыться ... это чертовски безумно. Очень интересно найти лучшее решение для этого.

person Felipe Caldas    schedule 31.05.2013
comment
В итоге пришлось делать то же самое, используя диалог фрагмента в качестве презентации для кнопки (и других кнопок входа) и не желая, чтобы весь код для всех входов в систему был в одном фрагменте диалога. - person ort11; 04.11.2014
comment
Просто отметим, что это не плохой хак, если вы правильно проверите requestCode... if requestCode == Session.DEFAULT_AUTHORIZE_ACTIVITY_CODE - person Keith Entzeroth; 27.02.2015

В моем случае я пытался изменить класс фрагмента с android.support.v4.app.Fragment на android.app.Fragment (я не поддерживал версии Android до API-11) и пропустил оператор FBAuthButton.setFragment(this);. Это и было причиной проблемы.

person axplusb    schedule 04.05.2015

У меня возникла та же проблема, я удалил свое приложение и переустановил, и оно сработало!

person serenskye    schedule 25.04.2013