Подтвердить что ты не робот

Как правильно использовать Google Plus для входа в систему с несколькими действиями?

Каков был бы хороший/рекомендуемый способ связать жизненный цикл клиента API Google+ с потоком приложения с несколькими действиями? Заставить действия зависеть от метода клиента onConnected api для запуска его функциональности, использовать его как единовременную вещь "активации" или, может быть, что-то еще полностью?

В настоящее время я пытаюсь понять, как правильно использовать вход Google+ в моем приложении Android, которое выполняет более одного действия.

Идея состоит в том, чтобы на первом этапе использовать вход G+ только для аутентификации пользователя и получения его электронной почты, для отправки уведомлений и тому подобного. В конце концов я планирую развернуть другие функции Google, такие как, например, Карты или другие сервисы Google Play, поэтому я думаю, что полезно уже реализовать их.

Тем не менее, мое приложение работает не так, как ожидалось, и я сузил проблему до того факта, что я еще не понял знак G+ в цикле приложения, когда присутствует более одного действия.

Какой правильный или рекомендуемый способ реализации этого метода аутентификации? Есть ли какая-то модель, которая могла бы направить меня в правильном направлении?

Например, я нашел очень простую диаграмму жизненного цикла клиента API, но как это связано с потоком приложений?

Изначально у меня есть вход в систему, где я помещаю кнопку входа. Следуя Руководству Google, я могу войти в систему, и когда вызывается метод onConnected, я запускаю Домашнюю активность (что-то вроде панели инструментов или основного экрана приложения).

Это работает несколько. Например, что было бы хорошим способом обработки onStart и onStop для каждого действия? я должен повторно соединиться и повторно аутентифицировать клиент API каждый раз для каждого действия? Так что, может быть, неплохо иметь BaseActivity для реализации всего этого.

Другая проблема заключается в том, должен ли я использовать тот же объект клиента API и каким-либо образом передавать его, или, возможно, сохранить его в классе Base Activity? или я должен каждый раз создавать и инициализировать новый клиентский объект API?

Как насчет того, чтобы просто использовать действие входа в систему для аутентификации с помощью G+, а затем просто получить электронное письмо и сохранить его в локальной базе данных, а также пометить пользователя как "прошедшего проверку подлинности" или "активный" или что-то в этом роде. Это избавит меня от необходимости повторной аутентификации каждый раз, когда приложение закрывается или соединение приостанавливается, даже учитывая некоторую экономию батареи.

The app is not really using G+ posting or any other functionality like that. Ideally it should work well offline, и only need connection for stuff like initial authentication or other one-time only things.

Приложение на самом деле не использует публикацию G+ или другие подобные функции. В идеале он должен хорошо работать в автономном режиме, и ему нужно только соединение для таких вещей, как первоначальная аутентификация или другие единовременные вещи. Любые предложения или указатели в правильном направлении очень ценятся.

Изменение: я прочитал все руководства и учебники, которые я могу найти, которые используют Google+, и каждый из них обращается к этому с одной точки зрения деятельности. Я думаю, что это достаточно распространенная проблема, которая выиграет от шаблона или хотя бы общего руководства.

4b9b3361

Ответ 1

Повторное подключение для каждого мероприятия абсолютно нормально. В целом есть три способа, которыми я видел людей, реализующих это:

  • Реализуйте в основном в baseactivity, и другие расширяют это. Это соединение/разъединение в каждом действии, но с кодом только в одном месте.
  • Внедрить соединение/отсоединение в фрагменте и включить его в действия, требующие авторизации. Это полезно, если у вас уже есть базовая активность, которую вы не можете продлить (например, в некоторых играх).
  • Внедрить службу для подключения/отключения. Это может привести к возбуждению трансляции или аналогичному, если требуется вход.

Все эти работы, и я видел, как все они использовались в реальных приложениях. Главное, чтобы помнить, состоит в том, чтобы отделить 99% -ную логику (пользователь либо подписан, либо выписан, и об этом вам сообщается) из-за относительно редкого "использования в настоящий момент". Так, например, вы могли бы отключить onConnected/onConnection, но в основном вы игнорируете или просто перебрасываете информацию о состоянии приложения. Только на экране с кнопкой входа вам нужно разрешение результата соединения и материал onActivityResult. Подумайте о соединении служб Google Play как о том, чтобы в основном запрашивать состояние пользователя, а не подписывать его, и все должно быть в порядке.

Ответ 2

Я согласен с ответом Яна Барбера, но, чтобы объяснить немного дальше, ваш Activity следует рассматривать в двух типах - Activity, которые разрешают вход в систему, и Activity, которые требуют входа.

Большинство Activity не относятся к аутентификации пользователя и будут иметь одинаковую логику в вашем приложении. Они создадут GoogleApiClient, который подключается к процессу сервисов Google Play, запущенному на устройстве, и считывает состояние входа в кешированный пользователь - возврат onConnected(), если пользователь подписан, и onConnectionFailed(), если нет. Большинство ваших Activity захотят reset ваше состояние приложения и запустите ваш LoginActivity, если пользователь не был подписан. Каждый Activity должен поддерживать свой собственный экземпляр GoogleApiClient, поскольку это легкий объект, используемый для доступ к общему состоянию, хранящемуся в процессе обслуживания Google Play. Такое поведение может быть, например, инкапсулировано в общий класс BaseActivity или общий SignInFragment класс, но каждый экземпляр должен иметь свой собственный экземпляр GoogleApiClient.

Однако ваш LoginActivity должен быть реализован по-разному. Он также должен создать GoogleApiClient, но когда он получает onConnected(), указывающий, что пользователь подписан, он должен запустить соответствующий Activity для пользователя и finish(). Когда ваш LoginActivity получает onConnectionFailed(), указывающий, что пользователь не подписан, вы должны попытаться разрешить проблемы с записью с помощью startResolutionForResult().

Ответ 3

0. TL; DR

Для нетерпеливого кодера рабочую версию следующей реализации можно найти на GitHub.

После переписывания кода активности входа в систему несколько раз во многих различных приложениях, простым (и не очень элегантным) решением было создание клиента API Google в качестве объекта класса Application. Но, поскольку состояние соединения влияет на поток UX, я никогда не был доволен этим подходом.

Сводя нашу проблему только к концепции подключения, мы можем считать, что:

  1. Он скрывает клиент Google API.
  2. У него конечные состояния.
  3. Это (довольно) уникальный.
  4. Текущее состояние влияет на поведение приложения.

1. Прокси-шаблон

Поскольку Connection инкапсулирует GoogleApiClient, он будет реализовывать ConnectionCallbacks и OnConnectionFailedListener:

@Override
public void onConnected(Bundle hint) {
    changeState(State.OPENED);
}

@Override
public void onConnectionSuspended(int cause) {
    changeState(State.CLOSED);
    connect();
}

@Override
public void onConnectionFailed(ConnectionResult result) {
    if (currentState.equals(State.CLOSED) && result.hasResolution()) {
        changeState(State.CREATED);
        connectionResult = result;
    } else {
        connect();
    }
}

Действия могут связываться с классом Connection через методы connect, disconnect и revoke, но их поведение определяется текущим состоянием. Для конечного автомата требуются следующие методы:

protected void onSignIn() {
    if (!googleApiClient.isConnected() && !googleApiClient.isConnecting()) {
        googleApiClient.connect();
    }
}

protected void onSignOut() {
    if (googleApiClient.isConnected()) {
        Plus.AccountApi.clearDefaultAccount(googleApiClient);
        googleApiClient.disconnect();
        googleApiClient.connect();
        changeState(State.CLOSED);
    }
}

protected void onSignUp() {
    Activity activity = activityWeakReference.get();
    try {
        changeState(State.OPENING);
        connectionResult.startResolutionForResult(activity, REQUEST_CODE);
    } catch (IntentSender.SendIntentException e) {
        changeState(State.CREATED);
        googleApiClient.connect();
    }
}

protected void onRevoke() {
    Plus.AccountApi.clearDefaultAccount(googleApiClient);
    Plus.AccountApi.revokeAccessAndDisconnect(googleApiClient);
    googleApiClient = googleApiClientBuilder.build();
    googleApiClient.connect();
    changeState(State.CLOSED);
}

2. Государственный паттерн

Это поведенческий паттерн, позволяющий объекту изменять свое поведение при изменении его внутреннего состояния. Книга шаблонов дизайна GoF описывает, как TCP-соединение может быть представлено этим шаблоном (что также является нашим случаем).

Состояние из конечного автомата должно быть singleton, и проще всего сделать это в Java, создав Enum с именем State следующим образом:

public enum State {
    CREATED {
        @Override
        void connect(Connection connection) {
            connection.onSignUp();
        }
        @Override
        void disconnect(Connection connection) {
            connection.onSignOut();
        }
    },
    OPENING {},
    OPENED {
        @Override
        void disconnect(Connection connection) {
            connection.onSignOut();
        }
        @Override
        void revoke(Connection connection) {
            connection.onRevoke();
        }
    },
    CLOSED {
        @Override
        void connect(Connection connection) {
            connection.onSignIn();
        }
    };

void connect(Connection connection) {}
void disconnect(Connection connection) {}
void revoke(Connection connection) {}

Класс Connection содержит контекст, то есть текущее состояние, которое определяет, как будут вести себя методы Connection connect, disconnect и revoke:

public void connect() {
    currentState.connect(this);
}

public void disconnect() {
    currentState.disconnect(this);
}

public void revoke() {
    currentState.revoke(this);
}

private void changeState(State state) {
    currentState = state;
    setChanged();
    notifyObservers(state);
}

3. Шаблон синглтона

Поскольку нет необходимости повторно создавать этот класс, мы предоставляем его как одиночный:

public static Connection getInstance(Activity activity) {
    if (null == sConnection) {
        sConnection = new Connection(activity);
    }

    return sConnection;
}

public void onActivityResult(int result) {
    if (result == Activity.RESULT_OK) {
        changeState(State.CREATED);
    } else {
        changeState(State.CLOSED);
    }
    onSignIn();
}

private Connection(Activity activity) {
    activityWeakReference = new WeakReference<>(activity);

    googleApiClientBuilder = new GoogleApiClient
           .Builder(activity)
           .addConnectionCallbacks(this)
           .addOnConnectionFailedListener(this)
           .addApi(Plus.API, Plus.PlusOptions.builder().build())
           .addScope(new Scope("email"));

    googleApiClient = googleApiClientBuilder.build();
    currentState = State.CLOSED;
}

4. Наблюдаемая картина

Класс Connection расширяет Java Observable, поэтому одно или несколько действий могут наблюдать изменения состояния:

@Override
protected void onCreate(Bundle bundle) {
    connection = Connection.getInstance(this);
    connection.addObserver(this);
}

@Override
protected void onStart() {
    connection.connect();
}

@Override
protected void onDestroy() {
    connection.deleteObserver(this);
    connection.disconnect();
}

@Override
protected void onActivityResult(int request, int result, Intent data) {
    if (Connection.REQUEST_CODE == request) {
        connection.onActivityResult(result);
    }
}

@Override
public void update(Observable observable, Object data) {
    if (observable != connection) {
        return;
    }
    // Your presentation logic goes here...
}