Quelle serait une bonne façon/recommandée de lier le cycle de vie du client de l'API Google+ avec le flux d'une application multi-activités? Faire dépendre les activités de la méthode du client api onConnected pour déclencher sa fonctionnalité, l'utiliser comme une seule "activation" ou peut-être autre chose?
J'ai du mal à comprendre comment utiliser correctement la connexion Google+ dans mon Android, qui a plus d'une activité.
L'idée est, dans un premier temps, d'utiliser le signe G + juste pour authentifier l'utilisateur et pouvoir recevoir son email, envoyer des notifications et des trucs comme ça. Finalement, je prévois de déployer d'autres fonctionnalités de Google comme peut-être Maps ou d'autres services Google Play, donc je pense qu'il est déjà utile de les mettre en œuvre.
Cependant, mon application ne se comporte pas comme prévu, et j'ai limité le problème au fait que je n'ai pas encore compris le cycle de connexion G + dans l'application lorsque plusieurs activités sont présentes.
Quelle est la façon correcte ou recommandée d'implémenter cette méthode d'authentification? y a-t-il peut-être une sorte de schéma qui pourrait me guider dans la bonne direction?
Par exemple, j'ai trouvé n diagramme très simple du cycle de vie de le client api, mais comment cela se rapporte-t-il au flux de l'application?
Au départ, j'ai une activité de connexion, où je mets le bouton de connexion. Suivant guide de Google Je peux me connecter et lorsque la méthode onConnected est appelée, je démarre l'activité d'accueil (un peu comme le tableau de bord ou l'écran principal de l'application).
Cela fonctionne quelque peu. Par exemple, quelle serait une bonne façon de gérer les fonctions onStart et onStop pour chaque activité? dois-je me reconnecter et ré-authentifier le client api à chaque fois pour chaque activité? Alors peut-être que c'est une bonne idée d'avoir une BaseActivity pour implémenter tout cela.
Un autre problème est, dois-je utiliser le même objet client api et le transmettre d'une manière ou d'une autre, ou peut-être le stocker dans la classe d'activité de base? ou devrais-je créer et initialiser un nouvel objet client api à chaque fois?
Que diriez-vous d'utiliser simplement l'activité de connexion pour vous authentifier avec G +, puis récupérez simplement l'e-mail et stockez-le dans une base de données locale, et marquez l'utilisateur comme "authentifié" ou "actif" ou quelque chose. Cela m'éviterait de devoir me réauthentifier chaque fois que l'application est fermée ou que la connexion est suspendue, ce qui permet même d'économiser de la batterie.
L'application n'utilise pas vraiment la publication G + ou toute autre fonctionnalité comme celle-ci. Idéalement, cela devrait bien fonctionner hors ligne et ne nécessite une connexion que pour des choses comme l'authentification initiale ou d'autres choses uniques.
Toute suggestion ou indication dans la bonne direction est très appréciée.
Edit: j'ai lu tous les guides et tutoriels que je pouvais trouver, qui utilisent Google+, et chacun d'eux aborde cela dans une perspective d'activité unique. Je pense que c'est un problème suffisamment courant pour qu'il bénéficie d'un schéma ou au moins d'une ligne directrice générale.
Se reconnecter pour chaque activité est tout à fait correct. En gros, il y a 3 façons dont j'ai vu des gens mettre en œuvre ceci:
Tous ces travaux, et je les ai tous vus utilisés dans des applications du monde réel. La principale chose à retenir est de séparer la logique à 99% (l'utilisateur est connecté ou déconnecté, et vous en êtes informé) du cas d'utilisation relativement rare "se connecter à ce moment". Ainsi, par exemple, onConnect/onConnection peut avoir échoué à tirer beaucoup, mais la plupart du temps, vous ignorez ou inversez un peu l'état de l'application. Ce n'est que sur un écran avec un bouton de connexion que vous avez besoin de la résolution du résultat de la connexion et des éléments onActivityResult. Considérez la connexion aux services google play comme étant principalement de demander l'état de l'utilisateur, plutôt que de vous connecter, et ça devrait aller.
Je suis d'accord avec la réponse d'Ian Barber mais pour expliquer un peu plus loin, vos Activity
doivent être considérés en deux types - Activity
qui résolvent la connexion et Activity
qui nécessitent une connexion.
La plupart des Activity
ne se soucient pas d'authentifier l'utilisateur et auront la même logique dans votre application. Ils créeront un GoogleApiClient , qui se connecte au processus des services Google Play en cours d'exécution sur l'appareil et lit l'état de connexion mis en cache de l'utilisateur - renvoyant onConnected()
si l'utilisateur est signé dans, et onConnectionFailed()
sinon. La plupart de vos Activity
voudront réinitialiser l'état de votre application et démarrer votre LoginActivity
si l'utilisateur n'était pas connecté. Chaque Activity
devrait conserver sa propre instance de GoogleApiClient
car il s'agit d'un objet léger utilisé pour accéder à l'état partagé détenu par le processus des services Google Play. Ce comportement peut, par exemple, être encapsulé dans une classe partagée BaseActivity
ou une classe partagée SignInFragment
, mais chaque instance doit avoir sa propre instance GoogleApiClient
.
Cependant, votre LoginActivity
doit être implémenté différemment. Il doit également créer un GoogleApiClient
, mais lorsqu'il reçoit onConnected()
indiquant que l'utilisateur est connecté, il doit démarrer un Activity
approprié pour l'utilisateur et finish()
. Lorsque votre LoginActivity
reçoit onConnectionFailed()
indiquant que l'utilisateur n'est pas connecté, vous devez essayer de résoudre les problèmes de connexion avec startResolutionForResult()
.
Pour le codeur impatient, une version de travail de l'implémentation suivante peut être trouvée sur GitHub .
Après avoir réécrit le code d'activité de connexion plusieurs fois dans de nombreuses applications différentes, la solution facile (et pas si élégante) a été de créer le client API Google en tant qu'objet de classe d'application. Mais, puisque l'état de connexion affecte le flux UX, je n'ai jamais été satisfait de cette approche.
En réduisant notre problème uniquement au concept de connexion , nous pouvons considérer que:
Puisque le Connection
encapsule le GoogleApiClient
, il implémentera les ConnectionCallbacks
et 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();
}
}
Les activités peuvent communiquer avec la classe Connection via les méthodes connect
, disconnect
et revoke
, mais leurs comportements sont déterminés par l'état actuel. Les méthodes suivantes sont requises par la machine d'état:
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);
}
Il s'agit d'un modèle de comportement qui permet à un objet de modifier son comportement lorsque son état interne change. Le livre GoF Design Patterns décrit comment une connexion TCP connexion peut être représentée par ce modèle (ce qui est également notre cas).
Un état d'une machine d'état doit être un singleton
, et le plus simple est de le faire dans Java était de créer Enum
nommé State
comme suit:
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) {}
La classe Connection
contient le contexte, c'est-à-dire l'état actuel, qui définit comment les méthodes Connection
connect
, disconnect
et revoke
seront se comporter:
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);
}
Puisqu'il n'est pas nécessaire de recréer cette classe à plusieurs reprises, nous la fournissons en tant que singleton:
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;
}
La classe Connection
étend Java Observable
, donc 1 ou plusieurs activités peuvent observer les changements d'état:
@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...
}