studio services play googleapiclient google gms for example compile auth apis android design-patterns google-plus google-play-services google-api-client

android - services - Separando las preocupaciones de la actividad y GoogleApiClient



google play services android api (1)

Como de costumbre, hay un montón de código en mi LoginActivity y realmente preferiría separar las responsabilidades de la Activity de las preocupaciones del signo de Google Play.

Después de volver a escribir este código de LoginActivity varias veces, en muchas aplicaciones diferentes, la solución fácil (y no tan elegante) fue crear el cliente de API de Google como un objeto de clase de Application . Pero, dado que el estado de la conexión afecta el flujo UX, nunca estuve contento con este enfoque.

¿Existe una forma elegante de ubicar GoogleApiClient fuera de la Activity ?


0. TL; DR

Para el codificador impaciente, se puede encontrar una versión funcional de la siguiente implementación en GitHub .

Reduciendo nuestro problema solo al concepto de conexión , podemos considerar que:

  1. Tiene estados finitos.
  2. Encapsula el cliente de conexión.
  3. Es (más bien) ser único.
  4. El estado actual afecta el comportamiento de la aplicación.

1. Patrón de estado

Este es un patrón de comportamiento que permite a un objeto alterar su comportamiento cuando cambia su estado interno. El libro GoF Design Patterns describe cómo se puede representar una conexión TCP mediante este patrón (que también es nuestro caso).

Un estado de una máquina de estados debería ser un singleton , y lo más fácil de hacerlo en Java era crear Enum named State siguiente manera:

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

La Activity se comunicará con la clase abstracta de Connection (que contiene el contexto) a través de los métodos connect() , disconnect() y revoke() . El estado actual define cómo se comportarán estos métodos:

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

2. Patrón de Proxy

La clase GoogleConnection hereda de Connection y encapsula GoogleApiClient , por lo que debe proporcionar ConnectionCallbacks y OnConnectionFailedListener siguiente manera:

@Override public void onConnected(Bundle connectionHint) { changeState(State.OPENED); } @Override public void onConnectionSuspended(int cause) { mGoogleApiClient.connect(); } @Override public void onConnectionFailed(ConnectionResult result) { if (state.equals(State.CLOSED) && result.hasResolution()) { changeState(State.CREATED); connectionResult = result; } else { connect(); } } public void onActivityResult(int resultCode) { if (resultCode == Activity.RESULT_OK) { connect(); } else { changeState(State.CREATED); } }

Los métodos onSignIn() , onSignUp() , onSignOut() y onRevokeAndSignOut son necesarios en el segundo paso de esta explicación.

public void onSignUp() { try { Activity activity = activityWeakReference.get(); changeState(State.OPENING); connectionResult.startResolutionForResult(activity, REQUEST_CODE); } catch (IntentSender.SendIntentException e) { changeState(State.CREATED); mGoogleApiClient.connect(); } } public void onSignIn() { if (!mGoogleApiClient.isConnected() && !mGoogleApiClient.isConnecting()) { mGoogleApiClient.connect(); } } public void onSignOut() { Plus.AccountApi.clearDefaultAccount(mGoogleApiClient); mGoogleApiClient.disconnect(); changeState(State.CLOSED); mGoogleApiClient.connect(); } public void onRevokeAndSignOut() { Plus.AccountApi.clearDefaultAccount(mGoogleApiClient); Plus.AccountApi.revokeAccessAndDisconnect(mGoogleApiClient); changeState(State.CLOSED); mGoogleApiClient = mGoogleApiClientBuilder.build(); mGoogleApiClient.connect(); }

3. Patrón Singleton

Como no es necesario recrear esta clase repetidamente, lo proporcionamos como singleton:

public static Connection getInstance(Activity activity) { if (null == sConnection) { sConnection = new GoogleConnection(activity); } return sConnection; } public void onActivityResult(int result) { if (result == Activity.RESULT_OK) { changeState(State.CREATED); } else { changeState(State.CLOSED); } onSignIn(); } private GoogleConnection(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; googleApiClient.connect(); }

4. Patrón observable

La clase Connection amplía Java Observable , por lo que una o más actividades pueden observar los cambios de estado:

@Override protected void onCreate(Bundle bundle) { mConnection = GoogleConnection.getInstance(this); mConnection.addObserver(this); } @Override protected void onDestroy() { mConnection.deleteObserver(this); } @Override protected void onActivityResult(int request, int result, Intent data) { if (Connection.REQUEST_CODE == request) { mConnection.onActivityResult(result); } } @Override public void update(Observable observable, Object data) { if (observable == mGoogleConnection) { // UI/UX magic happens here ;-) } }