Comment utiliser le Kit de développement logiciel (SDK) Azure Mobile Apps pour AndroidHow to use the Azure Mobile Apps SDK for Android

Notes

Visual Studio App Center investit dans des services nouveaux et intégrés, essentiels au développement d’applications mobiles.Visual Studio App Center is investing in new and integrated services central to mobile app development. Les développeurs peuvent utiliser les services Build, Test et Distribute pour configurer le pipeline de livraison et d’intégration continues.Developers can use Build, Test and Distribute services to set up Continuous Integration and Delivery pipeline. Une fois l’application déployée, les développeurs peuvent superviser l’état et l’utilisation de leur application à l’aide des services Analytics et Diagnostics, puis interagir avec les utilisateurs à l’aide du service Push.Once the app is deployed, developers can monitor the status and usage of their app using the Analytics and Diagnostics services, and engage with users using the Push service. Les développeurs peuvent aussi utiliser Auth pour authentifier leurs utilisateurs ainsi que le service Data pour conserver et synchroniser les données d’application dans le cloud.Developers can also leverage Auth to authenticate their users and Data service to persist and sync app data in the cloud. Découvrez App Center dès aujourd'hui.Check out App Center today.

Ce guide vous montre comment utiliser le Kit de développement logiciel (SDK) du client Android pour Mobile Apps afin d’implémenter des scénarios courants tels que :This guide shows you how to use the Android client SDK for Mobile Apps to implement common scenarios, such as:

  • Interrogation des données (insertion, mise à jour et suppression).Querying for data (inserting, updating, and deleting).
  • Authentification.Authentication.
  • Gestion des erreurs.Handling errors.
  • Personnalisation du client.Customizing the client.

Ce guide est axé sur le kit de développement logiciel Android côté client.This guide focuses on the client-side Android SDK. Pour plus d’informations sur les kits SDK côté serveur pour Mobile Apps, consultez Utiliser le kit SDK backend .NET or How to use the Node.js backend SDK.To learn more about the server-side SDKs for Mobile Apps, see Work with .NET backend SDK or How to use the Node.js backend SDK.

Documentation de référenceReference Documentation

Des informations de référence sur l’API Javadocs pour la bibliothèque cliente Android se trouvent sur GitHub.You can find the Javadocs API reference for the Android client library on GitHub.

Plateformes prises en chargeSupported Platforms

Le Kit de développement logiciel (SDK) Azure Mobile Apps pour Android prend en charge les niveaux d’API compris entre 19 et 24 (de KitKat à Nougat) pour les facteurs de forme tablette et téléphone.The Azure Mobile Apps SDK for Android supports API levels 19 through 24 (KitKat through Nougat) for phone and tablet form factors. L’authentification utilise en particulier une approche courante d’infrastructure web pour recueillir des informations d’identification.Authentication, in particular, utilizes a common web framework approach to gather credentials. L’authentification de flux serveur ne fonctionne pas avec les appareils compacts comme les montres.Server-flow authentication does not work with small form factor devices such as watches.

Configuration et conditions préalablesSetup and Prerequisites

Terminez le didacticiel de démarrage rapide Mobile Apps .Complete the Mobile Apps quickstart tutorial. Cette tâche garantit que toutes les conditions préalables au développement d’Azure Mobile Apps ont été remplies.This task ensures all pre-requisites for developing Azure Mobile Apps have been met. Le didacticiel de démarrage rapide vous aide également à configurer et à créer votre premier backend Mobile App.The Quickstart also helps you configure your account and create your first Mobile App backend.

Si vous décidez de ne pas suivre le didacticiel de démarrage rapide, effectuez les tâches suivantes :If you decide not to complete the Quickstart tutorial, complete the following tasks:

Mise à jour du fichier de construction GradleUpdate the Gradle build file

Modifiez les deux fichiers build.gradle :Change both build.gradle files:

  1. ajoutez ce code au fichier de niveau projet build.gradle :Add this code to the Project level build.gradle file:

    buildscript {
        repositories {
            jcenter()
            google()
        }
    }
    
    allprojects {
        repositories {
            jcenter()
            google()
        }
    }
    
  2. Ajoutez ce code au fichier de niveau Module app build.gradle à l’intérieur de la balise dependencies :Add this code to the Module app level build.gradle file inside the dependencies tag:

    implementation 'com.microsoft.azure:azure-mobile-android:3.4.0@aar'
    

    La version la plus récente est la 3.4.0.Currently the latest version is 3.4.0. Les versions prises en charge sont listées sur Bintray.The supported versions are listed on bintray.

activer les autorisations Internet.Enable internet permission

Pour accéder à Azure, l’autorisation INTERNET doit être activée sur votre application.To access Azure, your app must have the INTERNET permission enabled. Si ce n’est pas déjà fait, ajoutez la ligne de code suivante à votre fichier AndroidManifest.xml :If it's not already enabled, add the following line of code to your AndroidManifest.xml file:

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

Créer une connexion clienteCreate a Client Connection

Azure Mobile Apps propose quatre fonctions à votre application mobile :Azure Mobile Apps provides four functions to your mobile application:

  • Accès aux données et synchronisation hors connexion avec un service Azure Mobile Apps.Data Access and Offline Synchronization with an Azure Mobile Apps Service.
  • Appel des API personnalisées écrites avec le Kit de développement logiciel (SDK) de serveur Azure Mobile Apps.Call Custom APIs written with the Azure Mobile Apps Server SDK.
  • Authentification avec l’authentification et l’autorisation Azure App Service.Authentication with Azure App Service Authentication and Authorization.
  • Inscription de notifications Push avec Notification Hubs.Push Notification Registration with Notification Hubs.

Pour chacune de ces fonctions, vous devez créer un objet MobileServiceClient au préalable.Each of these functions first requires that you create a MobileServiceClient object. Seul un objet MobileServiceClient doit être créé au sein de votre client mobile (autrement dit, il doit être un modèle de Singleton).Only one MobileServiceClient object should be created within your mobile client (that is, it should be a Singleton pattern). Pour créer un objet MobileServiceClient :To create a MobileServiceClient object:

MobileServiceClient mClient = new MobileServiceClient(
    "<MobileAppUrl>",       // Replace with the Site URL
    this);                  // Your application Context

<MobileAppUrl> est soit une chaîne, soit un objet d’URL qui pointe vers votre serveur principal mobile.The <MobileAppUrl> is either a string or a URL object that points to your mobile backend. Si vous utilisez Azure App Service pour héberger votre serveur principal mobile, veillez à utiliser la version https:// sécurisée de l’URL.If you are using Azure App Service to host your mobile backend, then ensure you use the secure https:// version of the URL.

Le client nécessite aussi un accès à l’activité ou au contexte (le paramètre this dans l’exemple).The client also requires access to the Activity or Context - the this parameter in the example. La construction de MobileServiceClient doit se produire dans la méthode onCreate() de l’activité référencée dans le fichier AndroidManifest.xml.The MobileServiceClient construction should happen within the onCreate() method of the Activity referenced in the AndroidManifest.xml file.

Nous vous recommandons d’extraire la communication du serveur dans sa propre classe (modèle singleton).As a best practice, you should abstract server communication into its own (singleton-pattern) class. Dans ce cas, vous devez transmettre l’activité dans le constructeur pour configurer correctement le service.In this case, you should pass the Activity within the constructor to appropriately configure the service. Par exemple :For example:

package com.example.appname.services;

import android.content.Context;
import com.microsoft.windowsazure.mobileservices.*;

public class AzureServiceAdapter {
    private String mMobileBackendUrl = "https://myappname.azurewebsites.net";
    private Context mContext;
    private MobileServiceClient mClient;
    private static AzureServiceAdapter mInstance = null;

    private AzureServiceAdapter(Context context) {
        mContext = context;
        mClient = new MobileServiceClient(mMobileBackendUrl, mContext);
    }

    public static void Initialize(Context context) {
        if (mInstance == null) {
            mInstance = new AzureServiceAdapter(context);
        } else {
            throw new IllegalStateException("AzureServiceAdapter is already initialized");
        }
    }

    public static AzureServiceAdapter getInstance() {
        if (mInstance == null) {
            throw new IllegalStateException("AzureServiceAdapter is not initialized");
        }
        return mInstance;
    }

    public MobileServiceClient getClient() {
        return mClient;
    }

    // Place any public methods that operate on mClient here.
}

Vous pouvez maintenant appeler AzureServiceAdapter.Initialize(this); dans la méthode onCreate() de votre activité principale.You can now call AzureServiceAdapter.Initialize(this); in the onCreate() method of your main activity. Toutes les autres méthodes nécessitant un accès au client utilisent AzureServiceAdapter.getInstance(); pour obtenir une référence à l’adaptateur de services.Any other methods needing access to the client use AzureServiceAdapter.getInstance(); to obtain a reference to the service adapter.

Opérations de donnéesData Operations

L’objectif principal du Kit de développement logiciel (SDK) Azure Mobile Apps consiste à fournir l’accès aux données stockées dans SQL Azure sur le serveur principal de l’application mobile.The core of the Azure Mobile Apps SDK is to provide access to data stored within SQL Azure on the Mobile App backend. Vous pouvez accéder à ces données à l’aide de classes fortement typées (recommandé) ou de requêtes non typées (déconseillé).You can access this data using strongly typed classes (preferred) or untyped queries (not recommended). La majeure partie de cette section s’intéresse à l’utilisation des classes fortement typées.The bulk of this section deals with using strongly typed classes.

Définir des classes de données clientDefine client data classes

Pour accéder aux données à partir des tables SQL Azure, définissez des classes de données client qui correspondent aux tables du backend Mobile Apps.To access data from SQL Azure tables, define client data classes that correspond to the tables in the Mobile App backend. Les exemples de cette rubrique reposent sur une table nommée MyDataTable, qui contient les colonnes suivantes :Examples in this topic assume a table named MyDataTable, which has the following columns:

  • idid
  • texttext
  • terminécomplete

L’objet côté client typé correspondant se trouve dans un fichier appelé MyDataTable.java :The corresponding typed client-side object resides in a file called MyDataTable.java:

public class ToDoItem {
    private String id;
    private String text;
    private Boolean complete;
}

Incluez des méthodes getter et setter pour chaque champ que vous ajoutez.Add getter and setter methods for each field that you add. Si votre table SQL Azure contient davantage de colonnes, vous devez ajouter les champs correspondants à cette classe.If your SQL Azure table contains more columns, you would add the corresponding fields to this class. Pour exemple, si l’objet de transfert de données (DTO, data transfer object) comporte une colonne d’entiers Priority, vous pouvez ajouter ce champ, ainsi que ses méthodes getter et setter :For example, if the DTO (data transfer object) had an integer Priority column, then you might add this field, along with its getter and setter methods:

private Integer priority;

/**
* Returns the item priority
*/
public Integer getPriority() {
    return mPriority;
}

/**
* Sets the item priority
*
* @param priority
*            priority to set
*/
public final void setPriority(Integer priority) {
    mPriority = priority;
}

Pour apprendre à créer des tables supplémentaires sur votre serveur principal Mobile Apps, consultez Procédure : Définir un contrôleur de table (.NET backend) or Define Tables using a Dynamic Schema (backend Node.js).To learn how to create additional tables in your Mobile Apps backend, see How to: Define a table controller (.NET backend) or Define Tables using a Dynamic Schema (Node.js backend).

Une table de serveur principal Azure Mobile Apps définit cinq champs spéciaux, dont quatre sont disponibles pour les clients :An Azure Mobile Apps backend table defines five special fields, four of which are available to clients:

  • String id: ID global unique de l'enregistrement.String id: The globally unique ID for the record. Nous vous recommandons de faire de l’ID la représentation de chaîne d’un objet UUID.As a best practice, make the id the String representation of a UUID object.
  • DateTimeOffset updatedAt: date/heure de la dernière mise à jour.DateTimeOffset updatedAt: The date/time of the last update. Le champ updatedAt est défini par le serveur et ne doit jamais être défini par votre code client.The updatedAt field is set by the server and should never be set by your client code.
  • DateTimeOffset createdAt: date/heure de création de l'objet.DateTimeOffset createdAt: The date/time that the object was created. Le champ createdAt est défini par le serveur et ne doit jamais être défini par votre code client.The createdAt field is set by the server and should never be set by your client code.
  • byte[] version: généralement représentée sous forme de chaîne, la version est également définie par le serveur.byte[] version: Normally represented as a string, the version is also set by the server.
  • boolean deleted: indique que l'enregistrement a été supprimé mais pas encore vidé.boolean deleted: Indicates that the record has been deleted but not purged yet. N’utilisez pas deleted en tant que propriété dans votre classe.Do not use deleted as a property in your class.

Le champ id est obligatoire.The id field is required. Les champs updatedAt et version sont utilisés pour la synchronisation hors connexion (pour la synchronisation incrémentielle et la résolution des conflits, respectivement).The updatedAt field and version field are used for offline synchronization (for incremental sync and conflict resolution respectively). Le champ createdAt est un champ de référence et n’est pas utilisé par le client.The createdAt field is a reference field and is not used by the client. Les noms sont des noms « à travers le câble » des propriétés et ne sont pas réglables.The names are "across-the-wire" names of the properties and are not adjustable. Cependant, vous pouvez créer un mappage entre votre objet et les noms « à travers le câble » à l’aide de la bibliothèque gson.However, you can create a mapping between your object and the "across-the-wire" names using the gson library. Par exemple :For example:

package com.example.zumoappname;

import com.microsoft.windowsazure.mobileservices.table.DateTimeOffset;

public class ToDoItem
{
    @com.google.gson.annotations.SerializedName("id")
    private String mId;
    public String getId() { return mId; }
    public final void setId(String id) { mId = id; }

    @com.google.gson.annotations.SerializedName("complete")
    private boolean mComplete;
    public boolean isComplete() { return mComplete; }
    public void setComplete(boolean complete) { mComplete = complete; }

    @com.google.gson.annotations.SerializedName("text")
    private String mText;
    public String getText() { return mText; }
    public final void setText(String text) { mText = text; }

    @com.google.gson.annotations.SerializedName("createdAt")
    private DateTimeOffset mCreatedAt;
    public DateTimeOffset getCreatedAt() { return mCreatedAt; }
    protected void setCreatedAt(DateTimeOffset createdAt) { mCreatedAt = createdAt; }

    @com.google.gson.annotations.SerializedName("updatedAt")
    private DateTimeOffset mUpdatedAt;
    public DateTimeOffset getUpdatedAt() { return mUpdatedAt; }
    protected void setUpdatedAt(DateTimeOffset updatedAt) { mUpdatedAt = updatedAt; }

    @com.google.gson.annotations.SerializedName("version")
    private String mVersion;
    public String getVersion() { return mVersion; }
    public final void setVersion(String version) { mVersion = version; }

    public ToDoItem() { }

    public ToDoItem(String id, String text) {
        this.setId(id);
        this.setText(text);
    }

    @Override
    public boolean equals(Object o) {
        return o instanceof ToDoItem && ((ToDoItem) o).mId == mId;
    }

    @Override
    public String toString() {
        return getText();
    }
}

Créer une référence de tableCreate a Table Reference

Pour accéder à une table, créez d’abord un objet MobileServiceTable en appelant la méthode getTable sur MobileServiceClient.To access a table, first create a MobileServiceTable object by calling the getTable method on the MobileServiceClient. Cette méthode comporte deux surcharges :This method has two overloads:

public class MobileServiceClient {
    public <E> MobileServiceTable<E> getTable(Class<E> clazz);
    public <E> MobileServiceTable<E> getTable(String name, Class<E> clazz);
}

Dans le code suivant, mClient est une référence à votre objet MobileServiceClient.In the following code, mClient is a reference to your MobileServiceClient object. La première surcharge est utilisée quand le nom de la classe et le nom de la table sont identiques. Elle est reprise dans le didacticiel de démarrage rapide :The first overload is used where the class name and the table name are the same, and is the one used in the Quickstart:

MobileServiceTable<ToDoItem> mToDoTable = mClient.getTable(ToDoItem.class);

La deuxième surcharge est utilisée quand le nom de la table est différent du nom de la classe : le premier paramètre correspond au nom de la table.The second overload is used when the table name is different from the class name: the first parameter is the table name.

MobileServiceTable<ToDoItem> mToDoTable = mClient.getTable("ToDoItemBackup", ToDoItem.class);

Interroger une table de serveur principalQuery a Backend Table

Commencez par obtenir une référence de table.First, obtain a table reference. Ensuite, exécutez une requête sur la référence de table.Then execute a query on the table reference. Une requête est une combinaison des éléments suivants :A query is any combination of:

Les clauses doivent être présentées dans l’ordre indiqué ci-dessus.The clauses must be presented in the preceding order.

Filtrage des résultatsFiltering Results

La forme générale d’une requête est :The general form of a query is:

List<MyDataTable> results = mDataTable
    // More filters here
    .execute()          // Returns a ListenableFuture<E>
    .get()              // Converts the async into a sync result

L’exemple précédent renvoie tous les résultats (jusqu’à la taille de page maximale définie par le serveur).The preceding example returns all results (up to the maximum page size set by the server). La méthode .execute() exécute la requête sur le serveur principal.The .execute() method executes the query on the backend. La requête est convertie en requête OData v3 avant sa transmission au backend Mobile Apps.The query is converted to an OData v3 query before transmission to the Mobile Apps backend. À la réception, le serveur principal Mobile Apps convertit la requête en une instruction SQL avant de l’exécuter sur l’instance SQL Azure.On receipt, the Mobile Apps backend converts the query into an SQL statement before executing it on the SQL Azure instance. Étant donné que l’activité réseau prend un certain temps, la méthode .execute() retourne un élément ListenableFuture<E>.Since network activity takes some time, The .execute() method returns a ListenableFuture<E>.

Filtrer les données renvoyéesFilter returned data

L’exécution de la requête suivante retourne tous les éléments de la table ToDoItem dont le paramètre complete est égal à false.The following query execution returns all items from the ToDoItem table where complete equals false.

List<ToDoItem> result = mToDoTable
    .where()
    .field("complete").eq(false)
    .execute()
    .get();

mToDoTable est la référence à la table des services mobiles que vous venez de créer.mToDoTable is the reference to the mobile service table that we created previously.

Définissez un filtre en appelant la méthode where sur la référence de table.Define a filter using the where method call on the table reference. La méthode where est suivie d’une méthode field, suivie d’une méthode qui spécifie le prédicat logique.The where method is followed by a field method followed by a method that specifies the logical predicate. Méthodes de prédicat possibles : eq (égal à), ne (différent de), gt (supérieur à), ge (supérieur ou égal à), lt (inférieur à), le (inférieur ou égal à).Possible predicate methods include eq (equals), ne (not equal), gt (greater than), ge (greater than or equal to), lt (less than), le (less than or equal to). Ces méthodes vous permettent de comparer les champs de nombre et de chaîne à des valeurs spécifiques.These methods let you compare number and string fields to specific values.

Vous pouvez activer des filtres sur les dates.You can filter on dates. Les méthodes suivantes vous permettent de comparer le champ de date complet ou des parties de la date : year, month, day, hour, minute et second.The following methods let you compare the entire date field or parts of the date: year, month, day, hour, minute, and second. L’exemple suivant ajoute un filtre pour les éléments dont la date d’échéance due date est égale à 2013.The following example adds a filter for items whose due date equals 2013.

List<ToDoItem> results = MToDoTable
    .where()
    .year("due").eq(2013)
    .execute()
    .get();

Les méthodes suivantes prennent en charge des filtres complexes sur les champs de chaîne : startsWith, endsWith, concat, subString, indexOf, replace, toLower, toUpper, trim, and length.The following methods support complex filters on string fields: startsWith, endsWith, concat, subString, indexOf, replace, toLower, toUpper, trim, and length. L’exemple suivant filtre les lignes de la table dans lesquelles la colonne text commence par « PRI0 ».The following example filters for table rows where the text column starts with "PRI0."

List<ToDoItem> results = mToDoTable
    .where()
    .startsWith("text", "PRI0")
    .execute()
    .get();

Les méthodes d’opérateur suivantes sont prises en charge sur les champs numériques : add, sub, mul, div, mod, floor, ceiling, and round.The following operator methods are supported on number fields: add, sub, mul, div, mod, floor, ceiling, and round. L’exemple suivant filtre les lignes de la table dans lesquelles la colonne duration est un nombre pair.The following example filters for table rows where the duration is an even number.

List<ToDoItem> results = mToDoTable
    .where()
    .field("duration").mod(2).eq(0)
    .execute()
    .get();

Vous pouvez combiner les prédicats avec les méthodes logiques and, or et not.You can combine predicates with these logical methods: and, or and not. L’exemple suivant combine deux des exemples précédents.The following example combines two of the preceding examples.

List<ToDoItem> results = mToDoTable
    .where()
    .year("due").eq(2013).and().startsWith("text", "PRI0")
    .execute()
    .get();

Regrouper et imbriquer des opérateurs logiques :Group and nest logical operators:

List<ToDoItem> results = mToDoTable
    .where()
    .year("due").eq(2013)
    .and(
        startsWith("text", "PRI0")
        .or()
        .field("duration").gt(10)
    )
    .execute().get();

Pour obtenir plus de détails et des exemples de filtres, consultez la page Exploring the richness of the Android client query model(Exploration de la richesse du modèle de requête client Android).For more detailed discussion and examples of filtering, see Exploring the richness of the Android client query model.

Trier les données renvoyéesSort returned data

Le code qui suit renvoie tous les éléments de la table ToDoItem triés par ordre croissant sur le champ text .The following code returns all items from a table of ToDoItems sorted ascending by the text field. mToDoTable est la référence à la table de back-end créée précédemment :mToDoTable is the reference to the backend table that you created previously:

List<ToDoItem> results = mToDoTable
    .orderBy("text", QueryOrder.Ascending)
    .execute()
    .get();

Le premier paramètre de la méthode orderBy est une chaîne équivalente au nom du champ sur lequel effectuer le tri.The first parameter of the orderBy method is a string equal to the name of the field on which to sort. Le second paramètre utilise l’énumération QueryOrder pour spécifier si le tri doit être croissant ou décroissant.The second parameter uses the QueryOrder enumeration to specify whether to sort ascending or descending. Si vous filtrez avec la méthode where, la méthode where doit être appelée avant la méthode orderBy.If you are filtering using the where method, the where method must be invoked before the orderBy method.

Sélectionner des colonnes spécifiquesSelect specific columns

Le code qui suit illustre comment renvoyer tous les éléments d’une table ToDoItems, mais uniquement en affichant les champs complete et text.The following code illustrates how to return all items from a table of ToDoItems, but only displays the complete and text fields. mToDoTable est la référence à la table de back-end créée précédemment.mToDoTable is the reference to the backend table that we created previously.

List<ToDoItemNarrow> result = mToDoTable
    .select("complete", "text")
    .execute()
    .get();

Les paramètres associés à la fonction select sont les noms de chaîne des colonnes de table à renvoyer.The parameters to the select function are the string names of the table's columns that you want to return. La méthode select doit suivre des méthodes telles que where et orderBy.The select method needs to follow methods like where and orderBy. Elle peut être suivie de méthodes de pagination telles que skip et top.It can be followed by paging methods like skip and top.

Renvoyer les données de pagesReturn data in pages

Les données sont TOUJOURS renvoyées dans les pages.Data is ALWAYS returned in pages. Le nombre maximal d’enregistrements renvoyés est défini par le serveur.The maximum number of records returned is set by the server. Si le client demande plus d’enregistrements, le serveur renvoie le nombre maximal d’enregistrements.If the client requests more records, then the server returns the maximum number of records. Par défaut, la taille de page maximale sur le serveur est de 50 enregistrements.By default, the maximum page size on the server is 50 records.

Le premier exemple présente comment sélectionner les cinq premiers éléments d'une table.The first example shows how to select the top five items from a table. Cette requête renvoie les éléments d’une table ToDoItem.The query returns the items from a table of ToDoItems. mToDoTable est la référence à la table de back-end créée précédemment :mToDoTable is the reference to the backend table that you created previously:

List<ToDoItem> result = mToDoTable
    .top(5)
    .execute()
    .get();

Voici maintenant une requête qui ignore les cinq premiers éléments, puis renvoie les cinq suivants :Here's a query that skips the first five items, and then returns the next five:

List<ToDoItem> result = mToDoTable
    .skip(5).top(5)
    .execute()
    .get();

Si vous souhaitez obtenir tous les enregistrements dans une table, implémentez le code pour effectuer une itération sur toutes les pages :If you wish to get all records in a table, implement code to iterate over all pages:

List<MyDataModel> results = new ArrayList<>();
int nResults;
do {
    int currentCount = results.size();
    List<MyDataModel> pagedResults = mDataTable
        .skip(currentCount).top(500)
        .execute().get();
    nResults = pagedResults.size();
    if (nResults > 0) {
        results.addAll(pagedResults);
    }
} while (nResults > 0);

Une demande pour tous les enregistrements à l’aide de cette méthode crée un minimum de deux demandes à destination du serveur principal Mobile Apps.A request for all records using this method creates a minimum of two requests to the Mobile Apps backend.

Conseil

Le choix de la taille de page appropriée est un équilibre entre l’utilisation de la mémoire pendant l’exécution de la demande, l’utilisation de la bande passante et le délai de réception de toutes les données.Choosing the right page size is a balance between memory usage while the request is happening, bandwidth usage and delay in receiving the data completely. La valeur par défaut (50 enregistrements) convient à tous les appareils.The default (50 records) is suitable for all devices. Si vous travaillez exclusivement sur des appareils avec une grande capacité de mémoire, augmentez cette valeur jusqu’à 500.If you exclusively operate on larger memory devices, increase up to 500. Nous avons constaté que l’augmentation de la taille de page au-delà de 500 enregistrements engendre des retards inacceptables et d’importants problèmes de mémoire.We have found that increasing the page size beyond 500 records results in unacceptable delays and large memory issues.

Procédure : Concaténer les méthodes de requêteHow to: Concatenate query methods

Les méthodes utilisées dans les requêtes de tables de backend peuvent être concaténées.The methods used in querying backend tables can be concatenated. La concaténation des méthodes de requêtes vous permet de sélectionner des colonnes spécifiques de lignes filtrées, qui sont triées et paginées.Chaining query methods allows you to select specific columns of filtered rows that are sorted and paged. Vous pouvez créer des filtres logiques complexes.You can create complex logical filters. Chaque méthode de requête retourne un objet de requête.Each query method returns a Query object. Pour mettre fin à la série de méthodes et exécuter la requête, appelez la méthode execute .To end the series of methods and actually run the query, call the execute method. Par exemple :For example:

List<ToDoItem> results = mToDoTable
        .where()
        .year("due").eq(2013)
        .and(
            startsWith("text", "PRI0").or().field("duration").gt(10)
        )
        .orderBy(duration, QueryOrder.Ascending)
        .select("id", "complete", "text", "duration")
        .skip(200).top(100)
        .execute()
        .get();

Les méthodes de requête concaténées doivent être classées comme suit :The chained query methods must be ordered as follows:

  1. Filtrage des méthodes (where).Filtering (where) methods.
  2. Tri des méthodes (orderBy).Sorting (orderBy) methods.
  3. Sélection des méthodes (select).Selection (select) methods.
  4. pagination des méthodes (skip et top).paging (skip and top) methods.

Lier des données à l’interface utilisateurBind data to the user interface

La liaison des données nécessite trois composants :Data binding involves three components:

  • la source de données ;The data source
  • la mise en page à l’écran ;The screen layout
  • l’adaptateur qui lie ces deux éléments.The adapter that ties the two together.

Dans notre exemple de code, nous renvoyons les données de la table SQL Azure Mobile Apps ToDoItem dans un tableau.In our sample code, we return the data from the Mobile Apps SQL Azure table ToDoItem into an array. Cette activité est un cas de figure très courant pour les applications de données.This activity is a common pattern for data applications. Les requêtes de base de données renvoient souvent une suite de lignes que le client récupère dans une liste ou un tableau.Database queries often return a collection of rows that the client gets in a list or array. Dans cet exemple, le tableau est la source des données.In this sample, the array is the data source. Ce code spécifie une mise en page à l'écran qui définit la façon dont les données sont affichées sur l'appareil.The code specifies a screen layout that defines the view of the data that appears on the device. Les deux sont liés par un adaptateur, qui, dans ce code, est une extension de la classe ArrayAdapter<ToDoItem> .The two are bound together with an adapter, which in this code is an extension of the ArrayAdapter<ToDoItem> class.

Définir la mise en pageDefine the Layout

La mise en page est définie par plusieurs éléments de code XML.The layout is defined by several snippets of XML code. En nous basant sur une mise en page existante, le code qui suit représente la vue ListView que nous souhaitons remplir avec les données du serveur.Given an existing layout, the following code represents the ListView we want to populate with our server data.

    <ListView
        android:id="@+id/listViewToDo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        tools:listitem="@layout/row_list_to_do" >
    </ListView>

Dans le code précédent, l'attribut listitem spécifie l'ID de la mise en page de chaque ligne dans la liste.In the preceding code, the listitem attribute specifies the id of the layout for an individual row in the list. Ce code spécifie une case à cocher ainsi que le texte associé, et il est instancié une fois pour chaque élément dans la liste.This code specifies a check box and its associated text and gets instantiated once for each item in the list. Cette mise en page n’affiche pas le champ id ; une mise en page plus complexe spécifierait d’autres champs dans l’affichage.This layout does not display the id field, and a more complex layout would specify additional fields in the display. Le code se trouve dans le fichier row_list_to_do.xml.This code is in the row_list_to_do.xml file.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">
    <CheckBox
        android:id="@+id/checkToDoItem"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/checkbox_text" />
</LinearLayout>

Définir l’adaptateurDefine the adapter

Comme la source de données de notre vue est un tableau ToDoItem, nous créons une sous-classe de notre adaptateur à partir de la classe ArrayAdapter<ToDoItem> .Since the data source of our view is an array of ToDoItem, we subclass our adapter from an ArrayAdapter<ToDoItem> class. Cette sous-classe produit une vue pour chaque élément ToDoItem utilisant la mise en page row_list_to_do.This subclass produces a View for every ToDoItem using the row_list_to_do layout. Dans notre code, nous définissons la classe suivante, qui est une extension de la classe ArrayAdapter<E>  :In our code, we define the following class that is an extension of the ArrayAdapter<E> class:

public class ToDoItemAdapter extends ArrayAdapter<ToDoItem> {
}

Ignorez la méthode getView de l'adaptateur.Override the adapters getView method. Par exemple :For example:

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View row = convertView;

        final ToDoItem currentItem = getItem(position);

        if (row == null) {
            LayoutInflater inflater = ((Activity) mContext).getLayoutInflater();
            row = inflater.inflate(R.layout.row_list_to_do, parent, false);
        }
        row.setTag(currentItem);

        final CheckBox checkBox = (CheckBox) row.findViewById(R.id.checkToDoItem);
        checkBox.setText(currentItem.getText());
        checkBox.setChecked(false);
        checkBox.setEnabled(true);

        checkBox.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View arg0) {
                if (checkBox.isChecked()) {
                    checkBox.setEnabled(false);
                    if (mContext instanceof ToDoActivity) {
                        ToDoActivity activity = (ToDoActivity) mContext;
                        activity.checkItem(currentItem);
                    }
                }
            }
        });
        return row;
    }

Nous créons une instance de cette classe dans notre activité, comme suit :We create an instance of this class in our Activity as follows:

    ToDoItemAdapter mAdapter;
    mAdapter = new ToDoItemAdapter(this, R.layout.row_list_to_do);

Le deuxième paramètre du constructeur ToDoItemAdapter est une référence à la mise en page.The second parameter to the ToDoItemAdapter constructor is a reference to the layout. Nous pouvons maintenant instancier l’élément ListView et attribuer l’adaptateur à ListView.We can now instantiate the ListView and assign the adapter to the ListView.

    ListView listViewToDo = (ListView) findViewById(R.id.listViewToDo);
    listViewToDo.setAdapter(mAdapter);

Utiliser l’adaptateur pour effectuer la liaison à l’interface utilisateurUse the Adapter to Bind to the UI

Vous êtes désormais prêt à utiliser la liaison des données.You are now ready to use data binding. Le code suivant montre comment obtenir les éléments du tableau et remplit l’adaptateur local en utilisant les éléments renvoyés.The following code shows how to get items in the table and fills the local adapter with the returned items.

    public void showAll(View view) {
        AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>(){
            @Override
            protected Void doInBackground(Void... params) {
                try {
                    final List<ToDoItem> results = mToDoTable.execute().get();
                    runOnUiThread(new Runnable() {

                        @Override
                        public void run() {
                            mAdapter.clear();
                            for (ToDoItem item : results) {
                                mAdapter.add(item);
                            }
                        }
                    });
                } catch (Exception exception) {
                    createAndShowDialog(exception, "Error");
                }
                return null;
            }
        };
        runAsyncTask(task);
    }

Appelez l’adaptateur chaque fois que vous modifiez le tableau ToDoItem .Call the adapter any time you modify the ToDoItem table. Comme les modifications se font enregistrement par enregistrement, vous ne gérez qu’une seule ligne, et non une collection.Since modifications are done on a record by record basis, you handle a single row instead of a collection. Lorsque vous insérez un élément, appelez la méthode add de l'adaptateur. Et lorsque vous supprimez un élément, appelez la méthode remove.When you insert an item, call the add method on the adapter; when deleting, call the remove method.

Vous trouverez un exemple complet dans le projet de démarrage rapide Android.You can find a complete example in the Android Quickstart Project.

Insérer des données dans le serveur principalInsert data into the backend

Créez une instance de la classe ToDoItem et définissez ses propriétés.Instantiate an instance of the ToDoItem class and set its properties.

ToDoItem item = new ToDoItem();
item.text = "Test Program";
item.complete = false;

Utilisez ensuite insert() pour insérer un objet :Then use insert() to insert an object:

ToDoItem entity = mToDoTable
    .insert(item)       // Returns a ListenableFuture<ToDoItem>
    .get();

L’entité renvoyée correspond aux données insérées dans la table du serveur principal, incluant l’ID et toutes les autres valeurs (telles que les champs createdAt, updatedAt et version) définies sur le serveur principal.The returned entity matches the data inserted into the backend table, included the ID and any other values (such as the createdAt, updatedAt, and version fields) set on the backend.

Les tables Mobile Apps nécessitent une colonne de clé primaire nommée id. Cette colonne doit être une chaîne.Mobile Apps tables require a primary key column named id. This column must be a string. La valeur par défaut de la colonne ID est un GUID.The default value of the ID column is a GUID. Vous pouvez fournir d’autres valeurs uniques, telles que des adresses de messagerie ou des noms d’utilisateurs.You can provide other unique values, such as email addresses or usernames. Lorsqu’aucune valeur d’ID de chaîne n’est fournie pour un enregistrement inséré, le backend génère une nouvelle valeur GUID.When a string ID value is not provided for an inserted record, the backend generates a new GUID.

Les valeurs d’ID de chaîne offrent les avantages suivants :String ID values provide the following advantages:

  • Les ID peuvent être générés sans effectuer d’aller-retour vers la base de données.IDs can be generated without making a round trip to the database.
  • Il est plus facile de fusionner des enregistrements de plusieurs tables ou bases de données.Records are easier to merge from different tables or databases.
  • Les valeurs d’ID s’intègrent mieux à la logique d’une application.ID values integrate better with an application's logic.

Les valeurs d’ID de chaîne sont obligatoires pour la prise en charge de la synchronisation hors connexion.String ID values are REQUIRED for offline sync support. Vous ne pouvez pas modifier un ID une fois stocké dans la base de données principale.You cannot change an Id once it is stored in the backend database.

Mettre à jour des données dans une application mobileUpdate data in a mobile app

Pour mettre à jour les données d’une table, transmettez le nouvel objet à la méthode update() .To update data in a table, pass the new object to the update() method.

mToDoTable
    .update(item)   // Returns a ListenableFuture<ToDoItem>
    .get();

Dans cet exemple, l’élément est une référence à une ligne dans la table ToDoItem, qui a été modifiée.In this example, item is a reference to a row in the ToDoItem table, which has had some changes made to it. La ligne avec le même id est mise à jour.The row with the same id is updated.

Supprimer des données dans une application mobileDelete data in a mobile app

Le code suivant montre comment supprimer les données d’une table en spécifiant l’objet de données.The following code shows how to delete data from a table by specifying the data object.

mToDoTable
    .delete(item);

Vous pouvez également supprimer un élément en spécifiant le champ id de la ligne à supprimer.You can also delete an item by specifying the id field of the row to delete.

String myRowId = "2FA404AB-E458-44CD-BC1B-3BC847EF0902";
mToDoTable
    .delete(myRowId);

Rechercher un élément spécifique par IDLook up a specific item by Id

Recherchez un élément avec un champ id spécifique avec la méthode lookUp() :Look up an item with a specific id field with the lookUp() method:

ToDoItem result = mToDoTable
    .lookUp("0380BAFB-BCFF-443C-B7D5-30199F730335")
    .get();

Procédure : Utiliser des données non typéesHow to: Work with untyped data

Le modèle de programmation non typé vous offre un contrôle total de la sérialisation JSON.The untyped programming model gives you exact control over JSON serialization. Il existe certains scénarios courants où vous pouvez utiliser un modèle de programmation non typé.There are some common scenarios where you may wish to use an untyped programming model. Par exemple, si la table de votre backend contient un grand nombre de colonnes et que vous n’avez besoin de faire référence qu’à quelques-unes d’entre elles.For example, if your backend table contains many columns and you only need to reference a subset of the columns. Avec le modèle typé, vous devez définir toutes les colonnes du serveur principal Mobile Apps dans votre classe de données.The typed model requires you to define all the columns defined in the Mobile Apps backend in your data class. La plupart des appels d'API permettant d'accéder aux données sont similaires à ceux des appels de programmation typés.Most of the API calls for accessing data are similar to the typed programming calls. La principale différence est que, dans le modèle non typé, vous appelez des méthodes dans l'objet MobileServiceJsonTable plutôt que dans l'objet MobileServiceTable.The main difference is that in the untyped model you invoke methods on the MobileServiceJsonTable object, instead of the MobileServiceTable object.

Créer une instance de table non typéeCreate an instance of an untyped table

Comme pour le modèle typé, vous commencez par obtenir une référence de table, mais il s'agit dans ce cas d'un objet MobileServicesJsonTable .Similar to the typed model, you start by getting a table reference, but in this case it's a MobileServicesJsonTable object. Obtenez la référence en appelant la méthode getTable sur une instance du client :Obtain the reference by calling the getTable method on an instance of the client:

private MobileServiceJsonTable mJsonToDoTable;
//...
mJsonToDoTable = mClient.getTable("ToDoItem");

Une fois que vous avez créé une instance de l’objet MobileServiceJsonTable, celui-ci a pratiquement la même API disponible qu’avec le modèle de programmation typé.Once you have created an instance of the MobileServiceJsonTable, it has virtually the same API available as with the typed programming model. Dans certains cas, les méthodes utilisent un paramètre non typé au lieu d’un paramètre typé.In some cases, the methods take an untyped parameter instead of a typed parameter.

Insérer une table non typéeInsert into an untyped table

Le code suivant vous explique comment effectuer une insertion.The following code shows how to do an insert. La première étape consiste à créer une bibliothèque JsonObject, which is part of the gson.The first step is to create a JsonObject, which is part of the gson library.

JsonObject jsonItem = new JsonObject();
jsonItem.addProperty("text", "Wake up");
jsonItem.addProperty("complete", false);

Ensuite, utilisez insert() pour insérer l’objet non typé dans la table.Then, Use insert() to insert the untyped object into the table.

JsonObject insertedItem = mJsonToDoTable
    .insert(jsonItem)
    .get();

Si vous avez besoin d’obtenir l’ID de l’objet inséré, utilisez la méthode getAsJsonPrimitive() .If you need to get the ID of the inserted object, use the getAsJsonPrimitive() method.

String id = insertedItem.getAsJsonPrimitive("id").getAsString();

Supprimer d’une table non typéeDelete from an untyped table

Le code qui suit montre comment supprimer une instance, dans ce cas la même instance de l'objet JsonObject créé dans l'exemple insert précédent.The following code shows how to delete an instance, in this case, the same instance of a JsonObject that was created in the prior insert example. Le code est identique à la casse typée, mais la méthode a une signature différente puisqu’elle fait référence à un objet JsonObject.The code is the same as with the typed case, but the method has a different signature since it references an JsonObject.

mToDoTable
    .delete(insertedItem);

Vous pouvez aussi directement supprimer une instance avec son ID :You can also delete an instance directly by using its ID:

mToDoTable.delete(ID);

Renvoyer toutes les lignes d’une table non typéeReturn all rows from an untyped table

Le code suivant montre comment récupérer toute une table.The following code shows how to retrieve an entire table. Étant donné que vous utilisez un tableau JSON, vous pouvez extraire uniquement certaines colonnes de la table, de manière sélective.Since you are using a JSON Table, you can selectively retrieve only some of the table's columns.

public void showAllUntyped(View view) {
    new AsyncTask<Void, Void, Void>() {
        @Override
        protected Void doInBackground(Void... params) {
            try {
                final JsonElement result = mJsonToDoTable.execute().get();
                final JsonArray results = result.getAsJsonArray();
                runOnUiThread(new Runnable() {

                    @Override
                    public void run() {
                        mAdapter.clear();
                        for (JsonElement item : results) {
                            String ID = item.getAsJsonObject().getAsJsonPrimitive("id").getAsString();
                            String mText = item.getAsJsonObject().getAsJsonPrimitive("text").getAsString();
                            Boolean mComplete = item.getAsJsonObject().getAsJsonPrimitive("complete").getAsBoolean();
                            ToDoItem mToDoItem = new ToDoItem();
                            mToDoItem.setId(ID);
                            mToDoItem.setText(mText);
                            mToDoItem.setComplete(mComplete);
                            mAdapter.add(mToDoItem);
                        }
                    }
                });
            } catch (Exception exception) {
                createAndShowDialog(exception, "Error");
            }
            return null;
        }
    }.execute();
}

Les mêmes méthodes de filtrage et de pagination disponibles pour le modèle typé le sont également pour le modèle non typé.The same set of filtering, filtering and paging methods that are available for the typed model are available for the untyped model.

Implémenter la synchronisation hors connexionImplement Offline Sync

Le Kit de développement logiciel (SDK) Azure Mobile Apps Client implémente également la synchronisation hors connexion des données à l’aide d’une base de données SQLite pour stocker une copie des données du serveur en local.The Azure Mobile Apps Client SDK also implements offline synchronization of data by using a SQLite database to store a copy of the server data locally. Les opérations effectuées sur une table en mode hors connexion ne nécessitent pas de connectivité mobile pour fonctionner.Operations performed on an offline table do not require mobile connectivity to work. La synchronisation hors connexion contribue à la résilience et aux performances au détriment d’une logique plus complexe pour la résolution des conflits.Offline sync aids in resilience and performance at the expense of more complex logic for conflict resolution. Le Kit de développement logiciel (SDK) Azure Mobile Apps Client implémente les fonctionnalités suivantes :The Azure Mobile Apps Client SDK implements the following features:

  • Synchronisation incrémentielle : seuls les enregistrements nouveaux et mis à jour sont téléchargés, ce qui permet d'économiser de la bande passante et de la mémoire.Incremental Sync: Only updated and new records are downloaded, saving bandwidth and memory consumption.
  • Accès concurrentiel optimiste : les opérations sont supposées aboutir.Optimistic Concurrency: Operations are assumed to succeed. La résolution des conflits est différée jusqu’à ce que des mises à jour soient effectuées sur le serveur.Conflict Resolution is deferred until updates are performed on the server.
  • Résolution des conflits : le Kit de développement logiciel (SDK) détecte les modifications conflictuelles ayant été apportées au niveau du serveur et fournit des hooks pour avertir l'utilisateur.Conflict Resolution: The SDK detects when a conflicting change has been made at the server and provides hooks to alert the user.
  • Suppression réversible : les enregistrements supprimés sont marqués comme supprimés, ce qui permet à d'autres appareils de mettre à jour leur cache hors connexion.Soft Delete: Deleted records are marked deleted, allowing other devices to update their offline cache.

Initialiser la synchronisation hors connexionInitialize Offline Sync

Chaque table hors connexion doit être définie dans le cache hors connexion avant toute utilisation.Each offline table must be defined in the offline cache before use. En règle générale, la définition de table s’effectue immédiatement après la création du client :Normally, table definition is done immediately after the creation of the client:

AsyncTask<Void, Void, Void> initializeStore(MobileServiceClient mClient)
    throws MobileServiceLocalStoreException, ExecutionException, InterruptedException
{
    AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
        @Override
        protected void doInBackground(Void... params) {
            try {
                MobileServiceSyncContext syncContext = mClient.getSyncContext();
                if (syncContext.isInitialized()) {
                    return null;
                }
                SQLiteLocalStore localStore = new SQLiteLocalStore(mClient.getContext(), "offlineStore", null, 1);

                // Create a table definition.  As a best practice, store this with the model definition and return it via
                // a static method
                Map<String, ColumnDataType> toDoItemDefinition = new HashMap<String, ColumnDataType>();
                toDoItemDefinition.put("id", ColumnDataType.String);
                toDoItemDefinition.put("complete", ColumnDataType.Boolean);
                toDoItemDefinition.put("text", ColumnDataType.String);
                toDoItemDefinition.put("version", ColumnDataType.String);
                toDoItemDefinition.put("updatedAt", ColumnDataType.DateTimeOffset);

                // Now define the table in the local store
                localStore.defineTable("ToDoItem", toDoItemDefinition);

                // Specify a sync handler for conflict resolution
                SimpleSyncHandler handler = new SimpleSyncHandler();

                // Initialize the local store
                syncContext.initialize(localStore, handler).get();
            } catch (final Exception e) {
                createAndShowDialogFromTask(e, "Error");
            }
            return null;
        }
    };
    return runAsyncTask(task);
}

Obtenir une référence à la table de cache hors connexionObtain a reference to the Offline Cache Table

Pour une table en ligne, vous utilisez .getTable().For an online table, you use .getTable(). Pour une table hors connexion, utilisez .getSyncTable() :For an offline table, use .getSyncTable():

MobileServiceSyncTable<ToDoItem> mToDoTable = mClient.getSyncTable("ToDoItem", ToDoItem.class);

Toutes les méthodes disponibles pour les tables en ligne (y compris le filtrage, le tri, la pagination, l’insertion de données, la mise à jour de données et la suppression de données) fonctionnent de la même façon sur les tables hors connexion.All the methods that are available for online tables (including filtering, sorting, paging, inserting data, updating data, and deleting data) work equally well on online and offline tables.

Synchroniser le cache hors connexion localSynchronize the Local Offline Cache

La synchronisation est sous le contrôle de votre application.Synchronization is within the control of your app. Voici un exemple de méthode de synchronisation :Here is an example synchronization method:

private AsyncTask<Void, Void, Void> sync(MobileServiceClient mClient) {
    AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>(){
        @Override
        protected Void doInBackground(Void... params) {
            try {
                MobileServiceSyncContext syncContext = mClient.getSyncContext();
                syncContext.push().get();
                mToDoTable.pull(null, "todoitem").get();
            } catch (final Exception e) {
                createAndShowDialogFromTask(e, "Error");
            }
            return null;
        }
    };
    return runAsyncTask(task);
}

Si un nom de requête est fourni à la méthode .pull(query, queryname), la synchronisation incrémentielle est utilisée pour renvoyer uniquement les enregistrements qui ont été créés ou modifiés depuis la dernière extraction complète.If a query name is provided to the .pull(query, queryname) method, then incremental sync is used to return only records that have been created or changed since the last successfully completed pull.

Gérer les conflits lors de la synchronisation hors connexionHandle Conflicts during Offline Synchronization

Si un conflit se produit pendant une opération .push(), une exception MobileServiceConflictException est levée.If a conflict happens during a .push() operation, a MobileServiceConflictException is thrown. L’élément émis par le serveur est intégré à l’exception et peut être récupéré par .getItem() sur l’exception.The server-issued item is embedded in the exception and can be retrieved by .getItem() on the exception. Ajustez l’opération push en appelant les éléments suivants sur l’objet MobileServiceSyncContext :Adjust the push by calling the following items on the MobileServiceSyncContext object:

  • .cancelAndDiscardItem()
  • .cancelAndUpdateItem()
  • .updateOperationAndItem()

Une fois tous les conflits marqués comme vous le souhaitez, appelez .push() pour résoudre tous les conflits.Once all conflicts are marked as you wish, call .push() again to resolve all the conflicts.

Appeler une API personnaliséeCall a custom API

Une API personnalisée vous permet de définir des points de terminaison exposant une fonctionnalité de serveur qui ne mappe pas vers une opération d'insertion, de mise à jour, de suppression ou de lecture.A custom API enables you to define custom endpoints that expose server functionality that does not map to an insert, update, delete, or read operation. En utilisant une API personnalisée, vous pouvez exercer davantage de contrôle sur la messagerie, notamment lire et définir des en-têtes de message HTTP et définir un format de corps de message autre que JSON.By using a custom API, you can have more control over messaging, including reading and setting HTTP message headers and defining a message body format other than JSON.

À partir d’un client Android, vous appelez la méthode invokeApi pour appeler le point de terminaison de l’API personnalisée.From an Android client, you call the invokeApi method to call the custom API endpoint. L’exemple suivant montre comment appeler un point de terminaison d’API nommé completeAll, qui retourne une classe de collection nommée MarkAllResult.The following example shows how to call an API endpoint named completeAll, which returns a collection class named MarkAllResult.

public void completeItem(View view) {
    ListenableFuture<MarkAllResult> result = mClient.invokeApi("completeAll", MarkAllResult.class);
    Futures.addCallback(result, new FutureCallback<MarkAllResult>() {
        @Override
        public void onFailure(Throwable exc) {
            createAndShowDialog((Exception) exc, "Error");
        }

        @Override
        public void onSuccess(MarkAllResult result) {
            createAndShowDialog(result.getCount() + " item(s) marked as complete.", "Completed Items");
            refreshItemsFromTable();
        }
    });
}

La méthode invokeApi est appelée sur le client pour envoyer une requête POST à la nouvelle API personnalisée.The invokeApi method is called on the client, which sends a POST request to the new custom API. Le résultat renvoyé par l'API personnalisée apparaît dans la boîte de message, avec les erreurs éventuelles.The result returned by the custom API is displayed in a message dialog, as are any errors. Les autres versions de la méthode invokeApi vous permettent éventuellement d’envoyer un objet dans le corps de la demande, de spécifier la méthode HTTP et d’envoyer des paramètres de requête avec la demande.Other versions of invokeApi let you optionally send an object in the request body, specify the HTTP method, and send query parameters with the request. Des versions non typées de la méthode invokeApi sont également fournies.Untyped versions of invokeApi are provided as well.

Ajouter l’authentification à votre applicationAdd authentication to your app

Les didacticiels décrivent déjà en détail l’ajout de ces fonctionnalités.Tutorials already describe in detail how to add these features.

App Service prend en charge l'authentification des utilisateurs de l'application par l'intermédiaire de différents fournisseurs d'identité externes : Facebook, Google, compte Microsoft, Twitter et Azure Active Directory.App Service supports authenticating app users using various external identity providers: Facebook, Google, Microsoft Account, Twitter, and Azure Active Directory. Vous pouvez définir des autorisations sur les tables pour limiter l'accès à certaines opérations aux seuls utilisateurs authentifiés.You can set permissions on tables to restrict access for specific operations to only authenticated users. Vous pouvez également utiliser l’identité des utilisateurs authentifiés pour implémenter des règles d’autorisation dans votre serveur principal.You can also use the identity of authenticated users to implement authorization rules in your backend.

Deux flux d’authentification sont pris en charge : un flux serveur et un flux client.Two authentication flows are supported: a server flow and a client flow. Le flux serveur fournit l'authentification la plus simple, car il repose sur l'interface Web des fournisseurs d’identité.The server flow provides the simplest authentication experience, as it relies on the identity providers web interface. Aucun SDK supplémentaires n’est requis pour implémenter l’authentification de flux serveur.No additional SDKs are required to implement server flow authentication. L’authentification de flux serveur ne fournit pas une intégration approfondie dans l’appareil mobile et elle est uniquement recommandée dans des scénarios de preuve de concept.Server flow authentication does not provide a deep integration into the mobile device and is only recommended for proof of concept scenarios.

Le flux client permet une intégration approfondie avec les fonctionnalités propres aux appareils, telles que l'authentification unique, car il repose sur des Kits de développement logiciel (SDK) proposés par le fournisseur d’identité.The client flow allows for deeper integration with device-specific capabilities such as single sign-on as it relies on SDKs provided by the identity provider. Par exemple, vous pouvez intégrer le SDK Facebook dans votre application mobile.For example, you can integrate the Facebook SDK into your mobile application. Le client mobile permute dans l’application Facebook et confirme votre ouverture de session avant de rebasculer vers votre application mobile.The mobile client swaps into the Facebook app and confirms your sign-on before swapping back to your mobile app.

Quatre étapes sont nécessaires pour activer l'authentification dans votre application :Four steps are required to enable authentication in your app:

  • inscription de votre application pour l’authentification auprès d’un fournisseur d’identité ;Register your app for authentication with an identity provider.
  • configuration de votre backend App Service ;Configure your App Service backend.
  • restriction des autorisations de table aux utilisateurs authentifiés uniquement sur le backend App Service ;Restrict table permissions to authenticated users only on the App Service backend.
  • ajout de code d’authentification à votre application.Add authentication code to your app.

Vous pouvez définir des autorisations sur les tables pour limiter l'accès à certaines opérations aux seuls utilisateurs authentifiés.You can set permissions on tables to restrict access for specific operations to only authenticated users. Vous pouvez également utiliser le SID d’un utilisateur authentifié pour modifier des demandes.You can also use the SID of an authenticated user to modify requests. Pour plus d’informations, consultez la rubrique Prise en main de l’authentification et les instructions pour le Kit de développement logiciel (SDK) Server.For more information, review Get started with authentication and the Server SDK HOWTO documentation.

Authentification : Flux serveurAuthentication: Server Flow

Le code suivant démarre un processus de connexion du flux serveur à l’aide du fournisseur Google.The following code starts a server flow login process using the Google provider. Une configuration supplémentaire est nécessaire en raison des exigences de sécurité pour le fournisseur Google :Additional configuration is required because of the security requirements for the Google provider:

MobileServiceUser user = mClient.login(MobileServiceAuthenticationProvider.Google, "{url_scheme_of_your_app}", GOOGLE_LOGIN_REQUEST_CODE);

En outre, ajoutez la méthode suivante à la classe d’activité principale :In addition, add the following method to the main Activity class:

// You can choose any unique number here to differentiate auth providers from each other. Note this is the same code at login() and onActivityResult().
public static final int GOOGLE_LOGIN_REQUEST_CODE = 1;

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // When request completes
    if (resultCode == RESULT_OK) {
        // Check the request code matches the one we send in the login request
        if (requestCode == GOOGLE_LOGIN_REQUEST_CODE) {
            MobileServiceActivityResult result = mClient.onActivityResult(data);
            if (result.isLoggedIn()) {
                // login succeeded
                createAndShowDialog(String.format("You are now logged in - %1$2s", mClient.getCurrentUser().getUserId()), "Success");
                createTable();
            } else {
                // login failed, check the error message
                String errorMessage = result.getErrorMessage();
                createAndShowDialog(errorMessage, "Error");
            }
        }
    }
}

L’élément GOOGLE_LOGIN_REQUEST_CODE défini dans votre activité principale est utilisé pour la méthode login() et dans la méthode onActivityResult().The GOOGLE_LOGIN_REQUEST_CODE defined in your main Activity is used for the login() method and within the onActivityResult() method. Vous pouvez choisir n’importe quel nombre unique, tant que le même nombre est utilisé dans les méthodes login() et onActivityResult().You can choose any unique number, as long as the same number is used within the login() method and the onActivityResult() method. Si vous extrayez le code client dans un adaptateur de services (comme indiqué précédemment), vous devez appeler les méthodes appropriées sur l’adaptateur de services.If you abstract the client code into a service adapter (as shown earlier), you should call the appropriate methods on the service adapter.

Vous devez également configurer le projet pour customtabs.You also need to configure the project for customtabs. Commencez par spécifier une URL de redirection.First specify a redirect-URL. Ajoutez l’extrait de code suivant à AndroidManifest.xml :Add the following snippet to AndroidManifest.xml:

<activity android:name="com.microsoft.windowsazure.mobileservices.authentication.RedirectUrlActivity">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="{url_scheme_of_your_app}" android:host="easyauth.callback"/>
    </intent-filter>
</activity>

Ajoutez redirectUriScheme au fichier build.gradle de votre application :Add the redirectUriScheme to the build.gradle file for your application:

android {
    buildTypes {
        release {
            // … …
            manifestPlaceholders = ['redirectUriScheme': '{url_scheme_of_your_app}://easyauth.callback']
        }
        debug {
            // … …
            manifestPlaceholders = ['redirectUriScheme': '{url_scheme_of_your_app}://easyauth.callback']
        }
    }
}

Enfin, ajoutez com.android.support:customtabs:28.0.0 à la liste des dépendances dans le fichier build.gradle :Finally, add com.android.support:customtabs:28.0.0 to the dependencies list in the build.gradle file:

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.google.code.gson:gson:2.3'
    implementation 'com.google.guava:guava:18.0'
    implementation 'com.android.support:customtabs:28.0.0'
    implementation 'com.squareup.okhttp:okhttp:2.5.0'
    implementation 'com.microsoft.azure:azure-mobile-android:3.4.0@aar'
    implementation 'com.microsoft.azure:azure-notifications-handler:1.0.1@jar'
}

Obtenez l’ID de l’utilisateur connecté à partir d’un MobileServiceUser à l’aide de la méthode getUserId.Obtain the ID of the logged-in user from a MobileServiceUser using the getUserId method. Pour obtenir un exemple de la manière d’utiliser Futures pour appeler les API de connexion asynchrones, consultez la rubrique Prise en main de l’authentification.For an example of how to use Futures to call the asynchronous login APIs, see Get started with authentication.

Avertissement

Le schéma d’URL mentionné respecte la casse.The URL Scheme mentioned is case-sensitive. Assurez-vous que toutes les occurrences de {url_scheme_of_you_app} respectent la casse.Ensure that all occurrences of {url_scheme_of_you_app} match case.

Mettre en cache des jetons d’authentificationCache authentication tokens

Pour cela, vous devez stocker localement l'ID utilisateur et le jeton d'authentification sur l'appareil.Caching authentication tokens requires you to store the User ID and authentication token locally on the device. Au prochain démarrage de l'application, vous vérifiez le cache, et si ces valeurs sont présentes, vous pouvez ignorer la procédure d'ouverture de session et rafraîchir le client avec ces données.The next time the app starts, you check the cache, and if these values are present, you can skip the log in procedure and rehydrate the client with this data. Mais ces données sont sensibles et elles doivent être stockées sous forme chiffrée au cas où le téléphone serait volé.However this data is sensitive, and it should be stored encrypted for safety in case the phone gets stolen. Vous pouvez obtenir un exemple complet qui montre comment mettre en cache des jetons d’authentification dans la section Mettre en cache des jetons d’authentification.You can see a complete example of how to cache authentication tokens in Cache authentication tokens section.

Lorsque vous tentez d’utiliser un jeton qui a expiré, vous recevez un message 401 - Connexion non autorisée .When you try to use an expired token, you receive a 401 unauthorized response. Vous pouvez gérer les erreurs d’authentification à l’aide de filtres.You can handle authentication errors using filters. Les filtres interceptent les requêtes adressées au backend App Service.Filters intercept requests to the App Service backend. Le code de filtre teste la réponse pour une erreur 401, déclenche le processus de connexion, puis reprend la demande qui a généré l’erreur 401.The filter code tests the response for a 401, triggers the sign-in process, and then resumes the request that generated the 401.

Utiliser des jetons d’actualisationUse Refresh Tokens

Le jeton renvoyé par l’authentification et l’autorisation Azure App Service a une durée de vie définie d’une heure.The token returned by Azure App Service Authentication and Authorization has a defined life time of one hour. Passé ce délai, vous devez de nouveau authentifier l’utilisateur.After this period, you must reauthenticate the user. Si vous utilisez un jeton de longue durée reçu via l’authentification de flux client, vous pouvez procéder à la nouvelle authentification avec l’authentification et l’autorisation Azure App Service à l’aide du même jeton.If you are using a long-lived token that you have received via client-flow authentication, then you can reauthenticate with Azure App Service Authentication and Authorization using the same token. Un autre jeton Azure App Service est généré avec une nouvelle durée de vie.Another Azure App Service token is generated with a new lifetime.

Vous pouvez également inscrire le fournisseur pour qu’il utilise des jetons d’actualisation.You can also register the provider to use Refresh Tokens. Un jeton d’actualisation n’est pas toujours disponible.A Refresh Token is not always available. Une configuration supplémentaire est nécessaire :Additional configuration is required:

  • Pour Azure Active Directory, configurez une clé secrète client pour l’application Azure Active Directory.For Azure Active Directory, configure a client secret for the Azure Active Directory App. Spécifiez la clé secrète client dans Azure App Service lors de la configuration de l’authentification Azure Active Directory.Specify the client secret in the Azure App Service when configuring Azure Active Directory Authentication. Lorsque vous appelez .login(), transmettez response_type=code id_token en tant que paramètre :When calling .login(), pass response_type=code id_token as a parameter:

    HashMap<String, String> parameters = new HashMap<String, String>();
    parameters.put("response_type", "code id_token");
    MobileServiceUser user = mClient.login
        MobileServiceAuthenticationProvider.AzureActiveDirectory,
        "{url_scheme_of_your_app}",
        AAD_LOGIN_REQUEST_CODE,
        parameters);
    
  • Pour Google, transmettez access_type=offline en tant que paramètre :For Google, pass the access_type=offline as a parameter:

    HashMap<String, String> parameters = new HashMap<String, String>();
    parameters.put("access_type", "offline");
    MobileServiceUser user = mClient.login
        MobileServiceAuthenticationProvider.Google,
        "{url_scheme_of_your_app}",
        GOOGLE_LOGIN_REQUEST_CODE,
        parameters);
    
  • Pour un compte Microsoft, sélectionnez la portée wl.offline_access.For Microsoft Account, select the wl.offline_access scope.

Pour actualiser un jeton, appelez .refreshUser() :To refresh a token, call .refreshUser():

MobileServiceUser user = mClient
    .refreshUser()  // async - returns a ListenableFuture<MobileServiceUser>
    .get();

Nous vous recommandons de créer un filtre qui détecte une réponse 401 à partir du serveur et tente d’actualiser le jeton utilisateur.As a best practice, create a filter that detects a 401 response from the server and tries to refresh the user token.

Se connecter avec l’authentification de flux clientLog in with Client-flow Authentication

Le processus général de connexion avec l’authentification de flux client se déroule comme suit :The general process for logging in with client-flow authentication is as follows:

  • Configurez l’authentification et l’autorisation Azure App Service comme vous le feriez pour l’authentification de flux client.Configure Azure App Service Authentication and Authorization as you would server-flow authentication.

  • Intégrez le Kit de développement logiciel (SDK) du fournisseur d’authentification pour que l’authentification puisse générer un jeton d’accès.Integrate the authentication provider SDK for authentication to produce an access token.

  • Appelez la méthode .login() comme suit (result doit être un AuthenticationResult) :Call the .login() method as follows (result should be an AuthenticationResult):

    JSONObject payload = new JSONObject();
    payload.put("access_token", result.getAccessToken());
    ListenableFuture<MobileServiceUser> mLogin = mClient.login("{provider}", payload.toString());
    Futures.addCallback(mLogin, new FutureCallback<MobileServiceUser>() {
        @Override
        public void onFailure(Throwable exc) {
            exc.printStackTrace();
        }
        @Override
        public void onSuccess(MobileServiceUser user) {
            Log.d(TAG, "Login Complete");
        }
    });
    

Consultez l’exemple de code complet dans la section suivante.See the complete code example in the next section.

Remplacez la méthode onSuccess() par le code que vous souhaitez utiliser sur une connexion réussie.Replace the onSuccess() method with whatever code you wish to use on a successful login. La chaîne {provider} est un fournisseur valide : aad (Azure Active Directory), facebook, google, microsoftaccount ou twitter.The {provider} string is a valid provider: aad (Azure Active Directory), facebook, google, microsoftaccount, or twitter. Si vous avez implémenté l’authentification personnalisée, vous pouvez également utiliser la balise de fournisseur d’authentification personnalisée.If you have implemented custom authentication, then you can also use the custom authentication provider tag.

Authentifier des utilisateurs avec la bibliothèque ADAL (Active Directory Authentication Library)Authenticate users with the Active Directory Authentication Library (ADAL)

Vous pouvez utiliser la bibliothèque d’authentification Active Directory (ADAL) pour authentifier des utilisateurs dans votre application à l’aide d’Azure Active Directory.You can use the Active Directory Authentication Library (ADAL) to sign users into your application using Azure Active Directory. L’utilisation d’une connexion par flux de client est souvent préférable à l’utilisation des méthodes loginAsync() , car elle offre une interface UX native plus simple et permet une personnalisation supplémentaire.Using a client flow login is often preferable to using the loginAsync() methods as it provides a more native UX feel and allows for additional customization.

  1. Si vous souhaitez configurer le backend de votre application mobile pour utiliser la connexion AAD, suivez le didacticiel Configurer votre application App Service pour utiliser la connexion Azure Active Directory .Configure your mobile app backend for AAD sign-in by following the How to configure App Service for Active Directory login tutorial. Bien que cette étape soit facultative, veillez à inscrire une application cliente native.Make sure to complete the optional step of registering a native client application.

  2. Installez la bibliothèque ADAL en modifiant votre fichier build.gradle pour inclure les définitions suivantes :Install ADAL by modifying your build.gradle file to include the following definitions:

    repositories {
        mavenCentral()
        flatDir {
            dirs 'libs'
        }
        maven {
            url "YourLocalMavenRepoPath\\.m2\\repository"
        }
    }
    packagingOptions {
        exclude 'META-INF/MSFTSIG.RSA'
        exclude 'META-INF/MSFTSIG.SF'
    }
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar'])
        implementation('com.microsoft.aad:adal:1.16.1') {
            exclude group: 'com.android.support'
        } // Recent version is 1.16.1
        implementation 'com.android.support:support-v4:28.0.0'
    }
    
  3. Ajoutez le code suivant à votre application, en procédant aux remplacements suivants :Add the following code to your application, making the following replacements:

    • Remplacez INSERT-AUTHORITY-HERE par le nom du client dans lequel vous avez approvisionné votre application.Replace INSERT-AUTHORITY-HERE with the name of the tenant in which you provisioned your application. Le format doit être https://login.microsoftonline.com/contoso.onmicrosoft.com.The format should be https://login.microsoftonline.com/contoso.onmicrosoft.com.
    • Remplacez INSERT-RESOURCE-ID-HERE par l’ID client du serveur principal de votre application mobile.Replace INSERT-RESOURCE-ID-HERE with the client ID for your mobile app backend. Vous pouvez obtenir l’ID client sur le portail, sous l’onglet Avancé du menu Paramètres Azure Active Directory.You can obtain the client ID from the Advanced tab under Azure Active Directory Settings in the portal.
    • Remplacez INSERT-CLIENT-ID-HERE par l’ID client que vous avez copié depuis l’application cliente native.Replace INSERT-CLIENT-ID-HERE with the client ID you copied from the native client application.
    • Remplacez INSERT-REDIRECT-URI-HERE par le point de terminaison /.auth/login/done de votre site, en utilisant le modèle HTTPS.Replace INSERT-REDIRECT-URI-HERE with your site's /.auth/login/done endpoint, using the HTTPS scheme. Cette valeur doit être similaire à https://contoso.azurewebsites.net/.auth/login/done .This value should be similar to https://contoso.azurewebsites.net/.auth/login/done.
private AuthenticationContext mContext;

private void authenticate() {
    String authority = "INSERT-AUTHORITY-HERE";
    String resourceId = "INSERT-RESOURCE-ID-HERE";
    String clientId = "INSERT-CLIENT-ID-HERE";
    String redirectUri = "INSERT-REDIRECT-URI-HERE";
    try {
        mContext = new AuthenticationContext(this, authority, true);
        mContext.acquireToken(this, resourceId, clientId, redirectUri, PromptBehavior.Auto, "", callback);
    } catch (Exception exc) {
        exc.printStackTrace();
    }
}

private AuthenticationCallback<AuthenticationResult> callback = new AuthenticationCallback<AuthenticationResult>() {
    @Override
    public void onError(Exception exc) {
        if (exc instanceof AuthenticationException) {
            Log.d(TAG, "Cancelled");
        } else {
            Log.d(TAG, "Authentication error:" + exc.getMessage());
        }
    }

    @Override
    public void onSuccess(AuthenticationResult result) {
        if (result == null || result.getAccessToken() == null
                || result.getAccessToken().isEmpty()) {
            Log.d(TAG, "Token is empty");
        } else {
            try {
                JSONObject payload = new JSONObject();
                payload.put("access_token", result.getAccessToken());
                ListenableFuture<MobileServiceUser> mLogin = mClient.login("aad", payload.toString());
                Futures.addCallback(mLogin, new FutureCallback<MobileServiceUser>() {
                    @Override
                    public void onFailure(Throwable exc) {
                        exc.printStackTrace();
                    }
                    @Override
                    public void onSuccess(MobileServiceUser user) {
                        Log.d(TAG, "Login Complete");
                    }
                });
            }
            catch (Exception exc){
                Log.d(TAG, "Authentication error:" + exc.getMessage());
            }
        }
    }
};

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (mContext != null) {
        mContext.onActivityResult(requestCode, resultCode, data);
    }
}

Ajuster la communication client-serveurAdjust the Client-Server Communication

La connexion client est normalement une connexion HTTP de base utilisant la bibliothèque HTTP sous-jacente fournie avec le Kit de développement logiciel (SDK) Android.The Client connection is normally a basic HTTP connection using the underlying HTTP library supplied with the Android SDK. Vous voulez peut-être la modifier pour plusieurs raisons :There are several reasons why you would want to change that:

  • Vous souhaitez utiliser une autre bibliothèque HTTP pour régler les délais d’expiration.You wish to use an alternate HTTP library to adjust timeouts.
  • Vous souhaitez fournir une barre de progression.You wish to provide a progress bar.
  • Vous souhaitez ajouter un en-tête personnalisé pour prendre en charge la fonctionnalité de gestion des API.You wish to add a custom header to support API management functionality.
  • Vous souhaitez intercepter une réponse indiquant un échec pour pouvoir implémenter la procédure de nouvelle authentification.You wish to intercept a failed response so that you can implement reauthentication.
  • Vous souhaitez enregistrer les demandes du serveur principal sur un service analytique.You wish to log backend requests to an analytics service.

Utilisation d’une autre bibliothèque HTTPUsing an alternate HTTP Library

Appelez la méthode .setAndroidHttpClientFactory() juste après avoir créé votre référence client.Call the .setAndroidHttpClientFactory() method immediately after creating your client reference. Par exemple, pour définir le délai d’expiration de connexion sur 60 secondes (au lieu de la valeur par défaut de 10 secondes) :For example, to set the connection timeout to 60 seconds (instead of the default 10 seconds):

mClient = new MobileServiceClient("https://myappname.azurewebsites.net");
mClient.setAndroidHttpClientFactory(new OkHttpClientFactory() {
    @Override
    public OkHttpClient createOkHttpClient() {
        OkHttpClient client = new OkHttpClient();
        client.setReadTimeout(60, TimeUnit.SECONDS);
        client.setWriteTimeout(60, TimeUnit.SECONDS);
        return client;
    }
});

Implémenter un filtre de progressionImplement a Progress Filter

Vous pouvez implémenter une interception de chaque demande en implémentant un élément ServiceFilter.You can implement an intercept of every request by implementing a ServiceFilter. Par exemple, le code suivant met à jour une barre de progression créée au préalable :For example, the following updates a pre-created progress bar:

private class ProgressFilter implements ServiceFilter {
    @Override
    public ListenableFuture<ServiceFilterResponse> handleRequest(ServiceFilterRequest request, NextServiceFilterCallback next) {
        final SettableFuture<ServiceFilterResponse> resultFuture = SettableFuture.create();
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (mProgressBar != null) mProgressBar.setVisibility(ProgressBar.VISIBLE);
            }
        });

        ListenableFuture<ServiceFilterResponse> future = next.onNext(request);
        Futures.addCallback(future, new FutureCallback<ServiceFilterResponse>() {
            @Override
            public void onFailure(Throwable e) {
                resultFuture.setException(e);
            }
            @Override
            public void onSuccess(ServiceFilterResponse response) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        if (mProgressBar != null)
                            mProgressBar.setVisibility(ProgressBar.GONE);
                    }
                });
                resultFuture.set(response);
            }
        });
        return resultFuture;
    }
}

Vous pouvez attacher ce filtre au client comme suit :You can attach this filter to the client as follows:

mClient = new MobileServiceClient(applicationUrl).withFilter(new ProgressFilter());

Personnaliser des en-têtes de demandeCustomize Request Headers

Utilisez l’élément ServiceFilter suivant et attachez le filtre comme vous le feriez pour l’élément ProgressFilter :Use the following ServiceFilter and attach the filter in the same way as the ProgressFilter:

private class CustomHeaderFilter implements ServiceFilter {
    @Override
    public ListenableFuture<ServiceFilterResponse> handleRequest(ServiceFilterRequest request, NextServiceFilterCallback next) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                request.addHeader("X-APIM-Router", "mobileBackend");
            }
        });
        SettableFuture<ServiceFilterResponse> result = SettableFuture.create();
        try {
            ServiceFilterResponse response = next.onNext(request).get();
            result.set(response);
        } catch (Exception exc) {
            result.setException(exc);
        }
    }
}

Configurer la sérialisation automatiqueConfigure Automatic Serialization

Vous pouvez spécifier une stratégie de conversion qui s’applique à chaque colonne à l’aide de l’API gson.You can specify a conversion strategy that applies to every column by using the gson API. La bibliothèque cliente Android utilise gson en arrière-plan pour sérialiser les objets Java en données JSON, qui sont ensuite envoyées à Azure App Service.The Android client library uses gson behind the scenes to serialize Java objects to JSON data before the data is sent to Azure App Service. Le code suivant utilise la méthode setFieldNamingStrategy() pour définir la stratégie.The following code uses the setFieldNamingStrategy() method to set the strategy. Cet exemple supprimera le premier caractère (un « m »), puis mettra le caractère suivant en minuscule, ce pour tous les noms de champ.This example will delete the initial character (an "m"), and then lower-case the next character, for every field name. Par exemple, il changera « mId » en « id ».For example, it would turn "mId" into "id." Implémentez une stratégie de conversion pour réduire le besoin d’annotations SerializedName() sur la plupart des champs.Implement a conversion strategy to reduce the need for SerializedName() annotations on most fields.

FieldNamingStrategy namingStrategy = new FieldNamingStrategy() {
    public String translateName(File field) {
        String name = field.getName();
        return Character.toLowerCase(name.charAt(1)) + name.substring(2);
    }
}

client.setGsonBuilder(
    MobileServiceClient
        .createMobileServiceGsonBuilder()
        .setFieldNamingStrategy(namingStrategy)
);

Ce code doit être exécuté avant de créer une référence de client mobile à l’aide de MobileServiceClient.This code must be executed before creating a mobile client reference using the MobileServiceClient.