Cómo llamar a varias API de backend desde un cliente de Android

En esta página, se describe cómo un cliente de Android llama a una API de backend compilada con Cloud Endpoints Frameworks para App Engine.

Generar la biblioteca cliente

Tu cliente de Android requiere la biblioteca cliente generada desde la API de backend que usa. Si todavía no posees la biblioteca cliente, consulta Generar una biblioteca cliente para obtener más detalles. A continuación, se detallan los pasos para agregar una biblioteca cliente al proyecto de cliente de Android.

Prepara el proyecto

En estas instrucciones, usarás Android Studio. Si todavía no lo has hecho, deberás configurar Android Studio para admitir un cliente que use marcos de trabajo.

Cómo configurar el proyecto

En Android Studio, tu proyecto usa el archivo build.gradle para las dependencias y otras configuraciones. Según la configuración predeterminada, Android Studio crea un archivo superior build.gradle a nivel de proyecto y uno específico para las apps para Android en el módulo de Android. Estas instrucciones son para el build.gradle específico de la app en el módulo de Android.

Para configurar build.gradle, haz lo siguiente:

  1. Haz doble clic en build.gradle para abrirlo.

  2. Edita este archivo de modo que contenga las líneas siguientes:

    buildscript {
        repositories {
            mavenCentral()
        }
        dependencies {
            classpath 'com.android.tools.build:gradle:1.0.0'
        }
    }
    apply plugin: 'com.android.application'
    
    repositories {
        mavenCentral()
        mavenLocal()
    }
    
    android {
        compileSdkVersion 26
    
        defaultConfig {
            applicationId "com.mycompany.myapp"
            minSdkVersion 17
            targetSdkVersion 26
            versionCode 1
            versionName "1.0"
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
        packagingOptions {
            exclude 'META-INF/DEPENDENCIES'
        }
    }
    
    dependencies {
        implementation 'com.android.support:appcompat-v7:26.1.0'
        implementation 'com.android.support.constraint:constraint-layout:1.1.3'
        // BEGIN Google APIs
        // Play Services will validate the application prior to allowing OAuth2 access.
        implementation 'com.google.android.gms:play-services-auth:16.0.0'
        implementation 'com.google.android.gms:play-services-identity:16.0.0'
        // The following lines implement maven imports as defined at:
        // https://github.com/googleapis/google-api-java-client/wiki/Setup-Instructions
        // Add the Google API client library.
        implementation('com.google.api-client:google-api-client:1.28.0') {
            // Exclude artifacts that the Android SDK/Runtime provides.
            exclude(group: 'xpp3', module: 'xpp3')
            exclude(group: 'org.apache.httpcomponents', module: 'httpclient')
            exclude(group: 'junit', module: 'junit')
            exclude(group: 'com.google.android', module: 'android')
        }
        // Add the Android extensions for the Google API client library.
        // This will automatically include play services as long as you have download that library
        // from the Android SDK manager.
        // Add the Android extensions for the Google API client library.
        implementation(group: 'com.google.api-client', name: 'google-api-client-android', version: '1.25.0') {
                   // Exclude play services, since we're not using this yet.
                  exclude(group: 'com.google.android.gms:play-services', module: 'google-play-services')
        }
        // END Google APIs
        // The following client libraries make HTTP/JSON on Android easier.
        // Android extensions for Google HTTP Client.
        implementation('com.google.http-client:google-http-client-android:1.21.0') {
            exclude(group: 'com.google.android', module: 'android')
        }
        implementation 'com.google.http-client:google-http-client-gson:1.21.0'
        // This is used by the Google HTTP client library.
        implementation 'com.google.guava:guava:18.0+'
        implementation files('libs/echo-v1-1.25.0-SNAPSHOT.jar')
    }
    
    

    Reemplaza com.mycompany.myapp por tus propios valores.

  3. Haz clic en Archivo > Guardar todo. Luego, sal y reinicia Android Studio.

Cómo agregar la biblioteca cliente al proyecto

Para agregar la biblioteca cliente al proyecto de Android, sigue estos pasos:

  1. Agrega un directorio /libs a tu proyecto si no tienes uno. Debe ser un par del directorio /src.

  2. Copia la biblioteca cliente generada desde la API de backend a /libs.

  3. Haz clic con el botón derecho en la biblioteca que acabas de agregar y, luego, selecciona Agregar como biblioteca al proyecto.

Crea el objeto de servicio

En el código de tu proyecto, debes usar un objeto de servicio para realizar solicitudes a la API de backend. Para solicitudes unauthenticated, crea el objeto de servicio de la siguiente manera:

BACKEND_API_NAME.Builder builder = new BACKEND_API_NAME.Builder(
    NetHttpTransport(), new GsonFactory(), null);
service = builder.build();

Reemplaza BACKEND_API_NAME por el nombre de la API de backend.

Cómo llamar a la API de backend

En tu proyecto, llama a la API con el objeto de servicio. Por ejemplo:

ScoreCollection scores = service.scores().list().execute();

En el fragmento anterior, solicitamos una lista de todos los objetos Score del servidor. Tendrás que proporcionar los parámetros o el cuerpo de la solicitud en el comando si list lo requiere. Android Studio proporciona la función de autocompletar código para las llamadas de método con identidad disponible y los parámetros que requieran.

Es importante tener en cuenta que deberás realizar las solicitudes en el subproceso de las llamadas a la API, ya que estas generan solicitudes a través de la red (Este requisito se agregó a las versiones más recientes de Android, pero aparece como una práctica recomendada incluso en las versiones más antiguas). Para hacerlo, debes usar un Thread o AsyncTask. Por ejemplo:

private class QueryScoresTask extends AsyncTask<Void, Void, ScoreCollection>{
  Context context;

  public QueryScoresTask(Context context) {
    this.context = context;
  }

  protected Scores doInBackground(Void... unused) {
    ScoreCollection scores = null;
    try {
      scores = service.scores().list().execute();
    } catch (IOException e) {
      Log.d("TicTacToe", e.getMessage(), e);
    }
    return scores;
  }

  protected void onPostExecute(ScoreCollection scores) {
    // Do something with the result.
  }
}

Realizar llamadas autenticadas

Estas instrucciones abarcan solo el código de cliente que debes agregar. Suponemos que ya agregaste la compatibilidad con Endpoints Frameworks para la autenticación, como se describe en Cómo autenticar usuarios.

Si tu cliente de Android realiza llamadas a un extremo que requiere autenticación, deberás hacer lo siguiente:

  • Configurar el cliente de Android para proporcionar credenciales al objeto de servicio
  • Utilizar el selector de cuenta para admitir la elección del usuario de cuentas de acceso.

Se proporcionan detalles en las siguientes secciones.

Configurar tu cliente de Android para proporcionar credenciales

Para respaldar las solicitudes a una API de backend que requiera autenticación, tu cliente de Android debe obtener credenciales de usuario y transferirlas al objeto de servicio.

Para obtener las credenciales de usuario y usar el selector de cuenta, deberás tener los permisos siguientes de Android:

<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />

Para obtener las credenciales de usuario, debes llamar a GoogleAccountCredential.usingAudience de la manera siguiente:

// Inside your Activity class onCreate method
settings = getSharedPreferences(
    "TicTacToeSample", 0);
credential = GoogleAccountCredential.usingAudience(this,
    "server:client_id:1-web-app.apps.googleusercontent.com");

El segundo parámetro de la llamada es el prefijo server:client_id que se agrega al ID del cliente web de la API de backend.

Código de ejemplo: obtener credenciales para el objeto de servicio

Con el código siguiente, se muestra cómo obtener credenciales y transferirlas al objeto de servicio:

// Inside your Activity class onCreate method
settings = getSharedPreferences("TicTacToeSample", 0);
credential = GoogleAccountCredential.usingAudience(this,
    "server:client_id:1-web-app.apps.googleusercontent.com");
setSelectedAccountName(settings.getString(PREF_ACCOUNT_NAME, null));

Tictactoe.Builder builder = new Tictactoe.Builder(
    NetHttpTransport(), new GsonFactory(),
    credential);
service = builder.build();

if (credential.getSelectedAccountName() != null) {
  // Already signed in, begin app!
} else {
  // Not signed in, show login window or request an account.
}

// setSelectedAccountName definition
private void setSelectedAccountName(String accountName) {
  SharedPreferences.Editor editor = settings.edit();
  editor.putString(PREF_ACCOUNT_NAME, accountName);
  editor.commit();
  credential.setSelectedAccountName(accountName);
  this.accountName = accountName;
}

Con el código del ejemplo anterior, se busca cualquier preferencia compartida almacenada por la app para Android y, luego, se intenta buscar el nombre de la cuenta que el usuario quiere usar a fin de autenticar la aplicación. Si la operación se realiza sin problemas, el código crea un objeto de credencial y lo transfiere a tu objeto de servicio. Estas credenciales permiten que la app para Android transfiera el token correcto a tu backend.

Ten en cuenta que el código del ejemplo verifica si la aplicación para Android ya sabe qué cuenta utilizar o no. En caso afirmativo, el flujo lógico puede continuar y se puede ejecutar la app para Android. Si no se determinó qué cuenta usar, aparecerá una pantalla de acceso o se le pedirá al usuario que elija una cuenta.

Por último, la muestra crea un objeto de credencial y lo transfiere al objeto de servicio. Estas credenciales permiten que la app para Android transfiera el token correcto a la app web de App Engine.

Usa el selector de cuenta

Android proporciona un intent para seleccionar una cuenta de usuario. Puedes invocarlo de la siguiente manera:

static final int REQUEST_ACCOUNT_PICKER = 2;

void chooseAccount() {
  startActivityForResult(credential.newChooseAccountIntent(),
      REQUEST_ACCOUNT_PICKER);
}

A continuación, se muestra un controlador para interpretar el resultado de este intent:

@Override
protected void onActivityResult(int requestCode, int resultCode,
   Intent data) {
 super.onActivityResult(requestCode, resultCode, data);
 switch (requestCode) {
   case REQUEST_ACCOUNT_PICKER:
     if (data != null && data.getExtras() != null) {
       String accountName =
           data.getExtras().getString(
               AccountManager.KEY_ACCOUNT_NAME);
       if (accountName != null) {
         setSelectedAccountName(accountName);
         SharedPreferences.Editor editor = settings.edit();
         editor.putString(PREF_ACCOUNT_NAME, accountName);
         editor.commit();
         // User is authorized.
       }
     }
     break;
  }
}

Prueba un cliente de Android en un servidor de desarrollo local

Puedes probar tu cliente en una API de backend que se ejecuta en producción de App Engine en cualquier momento sin aplicar cambios. Sin embargo, si quieres probar tu cliente en una API de backend que se ejecuta en un servidor de desarrollo local, necesitarás cambiar una línea de código en el cliente para que apunte a la dirección IP de la máquina que ejecuta el servidor de desarrollo local.

Para aplicar los cambios requeridos y realizar las pruebas en el servidor de desarrollo local, deberás hacer lo siguiente:

  1. Toma nota de la dirección IP de la máquina que ejecuta el servidor de desarrollo local; la necesitarás para agregar código al cliente de Android.

  2. Inicia el servidor de desarrollo local, como se describe en Ejecutar y probar los backends de API de forma local.

  3. Localiza el código que obtiene el controlador para el servicio de API de backend en tu proyecto de cliente de Android Studio. Por lo general, este código usa un Builder para configurar la solicitud a la API.

  4. Agrega la línea siguiente para anular la URL raíz en el objeto Builder (esta es la URL a la que se conecta el cliente de Android en la llamada a la API de backend):

    yourBuilderObject.setRootUrl("http://YOUR_MACHINE_IP_ADDRESS:8080/_ah/api");
    

    Por ejemplo:

    public static Helloworld getApiServiceHandle(@Nullable GoogleAccountCredential credential) {
      // Use a builder to help formulate the API request.
      Helloworld.Builder helloWorld = new Helloworld.Builder(AppConstants.HTTP_TRANSPORT,
          AppConstants.JSON_FACTORY,credential);
    
      helloWorld.setRootUrl("http://YOUR_MACHINE_IP_ADDRESS:8080/_ah/api");
      return helloWorld.build();
    }
    

    Reemplaza YOUR_MACHINE_IP_ADDRESS por la dirección IP de tu sistema.

  5. Vuelve a compilar el proyecto de cliente de Android.

  6. Si quieres ejecutar tu app de cliente en un emulador AVD, sigue estos pasos:

    1. En Android Studio, selecciona Herramientas > Android > Administrador de AVD y, luego, inicia un AVD existente si tienes uno. De lo contrario, crea uno y, después, inícialo.
    2. Ve a Ejecutar > Depurar YOUR_PROJECT_NAME, donde YOUR_PROJECT_NAME representa el nombre de tu proyecto de Google Cloud.
    3. Cuando se te solicite elegir un dispositivo, selecciona el AVD.
    4. Prueba tu cliente.
  7. Si quieres ejecutar tu app de cliente en un dispositivo físico Android, sigue los pasos siguientes:

    1. Asegúrate de que tu dispositivo Android esté habilitado para la depuración.
    2. En Android Studio, selecciona Ejecutar > Depurar YOUR_PROJECT_NAME.
    3. Cuando se te solicite elegir un dispositivo, selecciona tu dispositivo físico Android.
    4. Prueba tu cliente.

Código fuente del cliente de muestra

Consulta los ejemplos de Android Cloud Endpoints v2.0 para ver un código de muestra.