Come usare Azure Mobile Apps SDK per AndroidHow to use the Azure Mobile Apps SDK for Android

Questa guida illustra come usare Android SDK del client per le App per dispositivi mobili di Azure per implementare scenari comuni, ad esempio:This guide shows you how to use the Android client SDK for Mobile Apps to implement common scenarios, such as:

  • L'esecuzione di query sui dati, come inserimento, aggiornamento ed eliminazione.Querying for data (inserting, updating, and deleting).
  • L'autenticazione.Authentication.
  • La gestione degli errori.Handling errors.
  • La personalizzazione del client.Customizing the client.

Questa guida descrive Android SDK lato client.This guide focuses on the client-side Android SDK. Per altre informazioni sugli SDK lato server per le app per dispositivi mobili, vedere Usare l'SDK del server back-end .NET o Come usare Node.js SDK back-end.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.

Documentazione di riferimentoReference Documentation

È possibile trovare il riferimento API Javadocs per la libreria client Android in GitHub.You can find the Javadocs API reference for the Android client library on GitHub.

Piattaforme supportateSupported Platforms

Azure Mobile Apps SDK per Android supporta API di livello da 19 a 24 (da KitKat a Nougat) per i fattori di forma di telefoni e tablet.The Azure Mobile Apps SDK for Android supports API levels 19 through 24 (KitKat through Nougat) for phone and tablet form factors. L'autenticazione in particolare utilizza un approccio comune ai framework Web per raccogliere le credenziali.Authentication, in particular, utilizes a common web framework approach to gather credentials. L'autenticazione basata sul flusso server non funziona con dispositivi con fattore di forma piccolo, ad esempio gli orologi.Server-flow authentication does not work with small form factor devices such as watches.

Installazione e prerequisitiSetup and Prerequisites

Completare l' esercitazione introduttiva sulle App per dispositivi mobili di Azure .Complete the Mobile Apps quickstart tutorial. Questa attività garantisce che vengano soddisfatti tutti i prerequisiti per lo sviluppo di App per dispositivi mobili di Azure.This task ensures all pre-requisites for developing Azure Mobile Apps have been met. L'esercitazione introduttiva è utile per configurare il proprio account e creare il primo back-end per dispositivi mobili.The Quickstart also helps you configure your account and create your first Mobile App backend.

Se si decide di non completare l'esercitazione introduttiva, completare le attività seguenti:If you decide not to complete the Quickstart tutorial, complete the following tasks:

Aggiornare il file di compilazione GradleUpdate the Gradle build file

Modificare entrambi i file build.gradle :Change both build.gradle files:

  1. Aggiungere il codice seguente al livello Project del file build.gradle all'interno del tag buildscript:Add this code to the Project level build.gradle file inside the buildscript tag:

    buildscript {
        repositories {
            jcenter()
        }
    }
    
  2. Aggiungere il codice seguente al livello Module app del file build.gradle all'interno del tag dependencies:Add this code to the Module app level build.gradle file inside the dependencies tag:

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

    La versione più recente è la 3.4.0.Currently the latest version is 3.4.0. Le versioni supportate sono elencate su bintray.The supported versions are listed on bintray.

Abilitare l'autorizzazione per InternetEnable internet permission

Per accedere ad Azure, è necessario abilitare l'autorizzazione INTERNET per l'app.To access Azure, your app must have the INTERNET permission enabled. Se non è già abilitata, aggiungere la riga di codice seguente al file 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" />

Creare una connessione clientCreate a Client Connection

App per dispositivi mobili di Azure fornisce quattro funzioni all'applicazione per dispositivi mobili:Azure Mobile Apps provides four functions to your mobile application:

  • Accesso ai dati e sincronizzazione offline con un servizio di App per dispositivi mobili di Azure.Data Access and Offline Synchronization with an Azure Mobile Apps Service.
  • Chiamata ad API personalizzate scritte con Azure Mobile Apps Server SDK.Call Custom APIs written with the Azure Mobile Apps Server SDK.
  • Autenticazione con autenticazione e autorizzazione nel servizio app di Azure.Authentication with Azure App Service Authentication and Authorization.
  • Registrazione di notifiche push con Hub di notifica.Push Notification Registration with Notification Hubs.

Per ogni funzione prima di tutto è necessario creare un oggetto MobileServiceClient.Each of these functions first requires that you create a MobileServiceClient object. È consigliabile creare un solo oggetto MobileServiceClient nel client per dispositivi mobili, ovvero il modello deve essere singleton.Only one MobileServiceClient object should be created within your mobile client (that is, it should be a Singleton pattern). Per creare un oggetto MobileServiceClient:To create a MobileServiceClient object:

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

<MobileAppUrl> è una stringa o un oggetto URL che punta al back-end per dispositivi mobili.The <MobileAppUrl> is either a string or a URL object that points to your mobile backend. Se si usa il servizio app di Azure per ospitare il back-end per dispositivi mobili, verificare di usare la versione https:// sicura dell'URL.If you are using Azure App Service to host your mobile backend, then ensure you use the secure https:// version of the URL.

Per il client è anche necessario l'accesso all'attività o al contesto (il parametro this nell'esempio).The client also requires access to the Activity or Context - the this parameter in the example. La costruzione MobileServiceClient deve avvenire nel metodo onCreate() dell'attività a cui si fa riferimento nel file AndroidManifest.xml.The MobileServiceClient construction should happen within the onCreate() method of the Activity referenced in the AndroidManifest.xml file.

Come procedura consigliata, è opportuno astrarre le comunicazioni del server nella relativa classe (modello singleton).As a best practice, you should abstract server communication into its own (singleton-pattern) class. In questo caso, è consigliabile passare l'attività nel costruttore per configurare il servizio in modo appropriato.In this case, you should pass the Activity within the constructor to appropriately configure the service. Ad esempio: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.
}

È ora possibile chiamare AzureServiceAdapter.Initialize(this); nel metodo onCreate() dell'attività principale.You can now call AzureServiceAdapter.Initialize(this); in the onCreate() method of your main activity. Tutti gli altri metodi che hanno necessità di accedere al client usano AzureServiceAdapter.getInstance(); per ottenere un riferimento all'adattatore del servizio.Any other methods needing access to the client use AzureServiceAdapter.getInstance(); to obtain a reference to the service adapter.

Operazioni datiData Operations

Azure Mobile Apps SDK fondamentalmente fornisce accesso ai dati archiviati in SQL Azure nel back-end dell'app per dispositivi mobili.The core of the Azure Mobile Apps SDK is to provide access to data stored within SQL Azure on the Mobile App backend. È possibile accedere a questi dati usando classi fortemente tipizzate (preferibile) o query non tipizzate (non consigliato).You can access this data using strongly typed classes (preferred) or untyped queries (not recommended). La maggior parte di questa sezione illustra l'uso di classi fortemente tipizzate.The bulk of this section deals with using strongly typed classes.

Definire le classi di dati clientDefine client data classes

Per accedere ai dati dalle tabelle di SQL Azure, definire le classi di dati client che corrispondono alle tabelle nel back-end dell'app per dispositivi mobili.To access data from SQL Azure tables, define client data classes that correspond to the tables in the Mobile App backend. Negli esempi di questo argomento si presuppone l'uso di una tabella denominata MyDataTable con le colonne seguenti:Examples in this topic assume a table named MyDataTable, which has the following columns:

  • idid
  • texttext
  • completecomplete

L'oggetto lato client tipizzato corrispondente si trova in un file denominato 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;
}

Aggiungere i metodi getter e setter per ogni campo aggiunto.Add getter and setter methods for each field that you add. Se la propria tabella di SQL Azure include più colonne, aggiungere i campi corrispondenti a questa classe.If your SQL Azure table contains more columns, you would add the corresponding fields to this class. Se ad esempio l'oggetto di trasferimento dati (DTO) include una colonna Priority di tipo Integer, è consigliabile aggiungere questo campo insieme ai relativi metodi getter e 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;
}

Per informazioni su come creare altre tabelle nel back-end delle app per dispositivi mobili, vedere Procedura: Definire un controller tabelle (back-end .NET) o Procedura: Definire le tabelle con uno schema dinamico (back-end 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).

Una tabella del back-end di App per dispositivi mobili di Azure definisce cinque campi speciali, quattro dei quali disponibili per i client:An Azure Mobile Apps backend table defines five special fields, four of which are available to clients:

  • String id: ID univoco globale per il record.String id: The globally unique ID for the record. Come procedura consigliata, impostare l'ID come rappresentazione di stringa di un oggetto UUID.As a best practice, make the id the String representation of a UUID object.
  • DateTimeOffset updatedAt: data/ora dell'ultimo aggiornamento.DateTimeOffset updatedAt: The date/time of the last update. Il campo updatedAt viene impostato dal server e non deve mai essere impostato dal codice client.The updatedAt field is set by the server and should never be set by your client code.
  • DateTimeOffset createdAt: data/ora di creazione dell'oggetto.DateTimeOffset createdAt: The date/time that the object was created. Il campo createdAt viene impostato dal server e non deve mai essere impostato dal codice client.The createdAt field is set by the server and should never be set by your client code.
  • byte[] version: rappresentata in genere come stringa, anche la versione viene impostata dal server.byte[] version: Normally represented as a string, the version is also set by the server.
  • boolean deleted: indica che il record è stato eliminato, ma non ancora definitivamente.boolean deleted: Indicates that the record has been deleted but not purged yet. Non usare deleted come proprietà nella classe.Do not use deleted as a property in your class.

Il campo id è obbligatorio.The id field is required. I campi updatedAt e version vengono usati per la sincronizzazione offline, rispettivamente per la sincronizzazione incrementale e per la risoluzione dei conflitti.The updatedAt field and version field are used for offline synchronization (for incremental sync and conflict resolution respectively). Il campo createdAt è un campo di riferimento e non viene usato dal client.The createdAt field is a reference field and is not used by the client. I nomi sono nomi delle proprietà usati per la trasmissione in rete e non sono modificabili.The names are "across-the-wire" names of the properties and are not adjustable. È tuttavia possibile creare un mapping tra l'oggetto e tali nomi usando la libreria gson.However, you can create a mapping between your object and the "across-the-wire" names using the gson library. ad esempio: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 getUpdatedAt() { return mCreatedAt; }
    protected DateTimeOffset setUpdatedAt(DateTimeOffset createdAt) { mCreatedAt = createdAt; }

    @com.google.gson.annotations.SerializedName("updatedAt")
    private DateTimeOffset mUpdatedAt;
    public DateTimeOffset getUpdatedAt() { return mUpdatedAt; }
    protected DateTimeOffset 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();
    }
}

Creare un riferimento alla tabellaCreate a Table Reference

Per accedere a una tabella, creare prima di tutto un oggetto MobileServiceTable chiamando il metodo getTable su MobileServiceClient.To access a table, first create a MobileServiceTable object by calling the getTable method on the MobileServiceClient. Questo metodo presenta due overload: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);
}

Nel codice seguente mClient è un riferimento all'oggetto MobileServiceClient.In the following code, mClient is a reference to your MobileServiceClient object. Il primo overload viene usato quando il nome della classe e il nome della tabella sono identici ed è quello usato nella guida introduttiva: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);

Il secondo overload viene usato quando il nome della tabella è diverso da quello della classe: il primo parametro è il nome della tabella.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);

Eseguire una query su una tabella del back-endQuery a Backend Table

Ottenere prima di tutto un riferimento alla tabella.First, obtain a table reference. Eseguire quindi una query sul riferimento alla tabella.Then execute a query on the table reference. Una query è qualsiasi combinazione di:A query is any combination of:

Le clausole devono essere presentate nell'ordine precedente.The clauses must be presented in the preceding order.

Applicazione di filtri ai risultatiFiltering Results

Il formato generale di una query è: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'esempio precedente restituisce tutti i risultati, fino alle dimensioni di pagina massime impostate dal server.The preceding example returns all results (up to the maximum page size set by the server). Il metodo .execute() esegue la query sul back-end.The .execute() method executes the query on the backend. La query viene convertita in una query OData v3 prima della trasmissione al back-end di App per dispositivi mobili.The query is converted to an OData v3 query before transmission to the Mobile Apps backend. Alla ricezione, il back-end di App per dispositivi mobili converte la query in un'istruzione SQL prima di eseguirla nell'istanza di SQL Azure.On receipt, the Mobile Apps backend converts the query into an SQL statement before executing it on the SQL Azure instance. Poiché l'attività di rete richiede tempo, il metodo .execute() restituisce ListenableFuture<E>.Since network activity takes some time, The .execute() method returns a ListenableFuture<E>.

Filtrare i dati restituitiFilter returned data

L'esecuzione della query seguente restituisce tutti gli elementi della tabella ToDoItem in cui complete è uguale a 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 è il riferimento alla tabella di Servizi mobili creata in precedenza.mToDoTable is the reference to the mobile service table that we created previously.

Definire un filtro con la chiamata al metodo where sul riferimento alla tabella.Define a filter using the where method call on the table reference. Il metodo where è seguito da un metodo field, seguito a sua volta da un metodo che specifica il predicato logico.The where method is followed by a field method followed by a method that specifies the logical predicate. I possibili metodi di predicato includono eq (uguale a), ne (non uguale a), gt (maggiore di), ge (maggiore o uguale a), lt (minore di), le (minore o uguale a).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). Questi metodi consentono di confrontare campi numerici e campi stringa con valori specifici,These methods let you compare number and string fields to specific values.

ad esempio filtrare in base alle date.You can filter on dates. I metodi seguenti consentono di confrontare l'intero campo data o parti della data: year, month, day, hour, minute e second.The following methods let you compare the entire date field or parts of the date: year, month, day, hour, minute, and second. L'esempio seguente aggiunge un filtro per gli elementi la cui data di scadenza (due) è uguale a 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();

I metodi seguenti supportano filtri complessi sui campi di tipo stringa: startsWith, endsWith, concat, subString, indexOf, replace, toLower, toUpper, trim e length.The following methods support complex filters on string fields: startsWith, endsWith, concat, subString, indexOf, replace, toLower, toUpper, trim, and length. L'esempio seguente filtra le righe della tabella in cui la colonna text inizia con "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();

I metodi operatore seguenti sono supportati sui campi numerici: add, sub, mul, div, mod, floor, ceiling e round.The following operator methods are supported on number fields: add, sub, mul, div, mod, floor, ceiling, and round. L'esempio seguente filtra le righe della tabella in cui il valore di duration è un numero pari.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();

È possibile combinare i predicati con questi metodi logici: and, or e not.You can combine predicates with these logical methods: and, or and not. L'esempio seguente combina due degli esempi precedenti.The following example combines two of the preceding examples.

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

Raggruppare e annidare operatori logici: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();

Per una descrizione più dettagliata ed esempi di filtro, vedere Exploring the richness of the Android client query model (Analisi delle funzionalità complesse disponibili nel modello di query client per Android).For more detailed discussion and examples of filtering, see Exploring the richness of the Android client query model.

Ordinare i dati restituitiSort returned data

Il codice di esempio seguente restituisce tutti gli elementi di una tabella di oggetti ToDoItems elencati in ordine crescente in base al campo text .The following code returns all items from a table of ToDoItems sorted ascending by the text field. mToDoTable è il riferimento alla tabella del back-end creata in precedenza.mToDoTable is the reference to the backend table that you created previously:

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

Il primo parametro del metodo orderBy è una stringa uguale al nome del campo in base al quale eseguire l'ordinamento.The first parameter of the orderBy method is a string equal to the name of the field on which to sort. Il secondo parametro usa l'enumerazione QueryOrder per specificare l'ordinamento crescente o decrescente.The second parameter uses the QueryOrder enumeration to specify whether to sort ascending or descending. Se per il filtro si usa il metodo where, è necessario chiamare il metodo where prima del metodo orderBy.If you are filtering using the where method, the where method must be invoked before the orderBy method.

Selezionare colonne specificheSelect specific columns

Il codice seguente illustra come restituire tutti gli elementi di una tabella di oggetti ToDoItems, visualizzando però solo i campi complete e text.The following code illustrates how to return all items from a table of ToDoItems, but only displays the complete and text fields. mToDoTable è il riferimento alla tabella del back-end creata in precedenza.mToDoTable is the reference to the backend table that we created previously.

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

I parametri della funzione select sono i nomi in formato stringa delle colonne della tabella che si vuole restituire.The parameters to the select function are the string names of the table's columns that you want to return. Il metodo select deve seguire metodi come where e orderBy.The select method needs to follow methods like where and orderBy. Può essere seguito da metodi di paging come skip e top.It can be followed by paging methods like skip and top.

Restituire i dati in pagineReturn data in pages

I dati vengono SEMPRE restituiti in pagine.Data is ALWAYS returned in pages. Il numero massimo di record restituiti viene impostato dal server.The maximum number of records returned is set by the server. Se il client richiede più record, il server restituisce il numero massimo di record.If the client requests more records, then the server returns the maximum number of records. Per impostazione predefinita, le dimensioni di pagina massime nel server sono pari a 50 record.By default, the maximum page size on the server is 50 records.

Il primo esempio illustra come selezionare i primi cinque elementi di una tabella.The first example shows how to select the top five items from a table. La query restituisce gli elementi di una tabella di oggetti ToDoItems.The query returns the items from a table of ToDoItems. mToDoTable è il riferimento alla tabella del back-end creata in precedenza.mToDoTable is the reference to the backend table that you created previously:

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

Ecco una query che ignora i primi cinque elementi e quindi restituisce i cinque successivi: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();

Per ottenere tutti i record in una tabella, implementare il codice per scorrere tutte le pagine:If you wish to get all records in a table, implement code to iterate over all pages:

List<MyDataModel> results = new List<MyDataModel>();
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);

Una richiesta per tutti i record con questo metodo crea un minimo di due richieste al back-end di App per dispositivi mobili.A request for all records using this method creates a minimum of two requests to the Mobile Apps backend.

Suggerimento

La scelta delle dimensioni di pagina appropriate è un equilibrio tra utilizzo della memoria mentre la richiesta è in corso, utilizzo della larghezza di banda e ritardo nella ricezione completa dei dati.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. Il valore predefinito (50 record) è adatto a tutti i dispositivi.The default (50 records) is suitable for all devices. Se si opera esclusivamente su dispositivi con maggiore memoria, aumentare fino a 500.If you exclusively operate on larger memory devices, increase up to 500. È stato osservato che l'aumento delle dimensioni di pagina oltre i 500 record comporta ritardi inaccettabili e problemi di memoria di grandi dimensioni.We have found that increasing the page size beyond 500 records results in unacceptable delays and large memory issues.

Procedura: Concatenare metodi di queryHow to: Concatenate query methods

I metodi usati per eseguire query su tabelle di back-end possono essere concatenati.The methods used in querying backend tables can be concatenated. Il concatenamento dei metodi di query consente di selezionare colonne specifiche di righe filtrate ordinate e sottoposte a paging.Chaining query methods allows you to select specific columns of filtered rows that are sorted and paged. È possibile creare filtri logici complessi.You can create complex logical filters. Ogni metodo di query restituisce un oggetto Query.Each query method returns a Query object. Per terminare la serie di metodi ed eseguire effettivamente la query, chiamare il metodo execute .To end the series of methods and actually run the query, call the execute method. ad esempio: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();

I metodi con query concatenate devono essere ordinati nel modo seguente:The chained query methods must be ordered as follows:

  1. Metodi di filtraggio (where).Filtering (where) methods.
  2. Metodi di ordinamento (orderBy).Sorting (orderBy) methods.
  3. Metodi di selezione (select).Selection (select) methods.
  4. Metodi di paging (skip e top).paging (skip and top) methods.

Associare dati all'interfaccia utenteBind data to the user interface

L'associazione dati riguarda tre componenti:Data binding involves three components:

  • Origine datiThe data source
  • Layout della schermataThe screen layout
  • Adattatore che collega i due componenti.The adapter that ties the two together.

Nel codice di esempio i dati vengono restituiti dalla tabella di app per dispositivi mobili di SQL Azure ToDoItem in una matrice.In our sample code, we return the data from the Mobile Apps SQL Azure table ToDoItem into an array. Si tratta di una delle attività più comuni per le applicazioni dati.This activity is a common pattern for data applications. Le query su database restituiscono spesso una raccolta di righe che il client ottiene in un elenco o una matrice.Database queries often return a collection of rows that the client gets in a list or array. In questo esempio la matrice è l'origine dati.In this sample, the array is the data source. Nel codice viene specificato un layout di schermata che definisce la visualizzazione dei dati che appaiono sul dispositivo.The code specifies a screen layout that defines the view of the data that appears on the device. I due elementi vengono associati tra loro tramite un adattatore, che in questo codice è un'estensione della classe ArrayAdapter<ToDoItem>.The two are bound together with an adapter, which in this code is an extension of the ArrayAdapter<ToDoItem> class.

Definire il layoutDefine the Layout

Il layout è definito da diversi frammenti di codice XML.The layout is defined by several snippets of XML code. Dato un layout esistente, il codice seguente rappresenta l'oggetto ListView che si vuole popolare con i dati del server.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>

Nel codice precedente l'attributo listitem consente di specificare l'ID del layout per una singola riga dell'elenco.In the preceding code, the listitem attribute specifies the id of the layout for an individual row in the list. Questo codice specifica una casella di controllo e il testo ad essa associato e viene creata una sola istanza per ogni elemento nell'elenco.This code specifies a check box and its associated text and gets instantiated once for each item in the list. Questo layout non visualizza il campo id , ma se il layout è più complesso nella visualizzazione vengono specificati campi aggiuntivi.This layout does not display the id field, and a more complex layout would specify additional fields in the display. Il codice è incluso nel file 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>

Definire l'adattatoreDefine the adapter

Poiché l'origine dati della visualizzazione è una matrice di oggetti ToDoItem, viene creata una sottoclasse per l'adattatore da una classe ArrayAdapter<ToDoItem>.Since the data source of our view is an array of ToDoItem, we subclass our adapter from an ArrayAdapter<ToDoItem> class. Questa sottoclasse consente di ottenere una visualizzazione per ogni oggetto ToDoItem che usa il layout row_list_to_do.This subclass produces a View for every ToDoItem using the row_list_to_do layout. Nel codice viene definita la classe seguente che costituisce un'estensione della 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> {
}

Eseguire l'override del metodo getView dell'adattatore.Override the adapters getView method. ad esempio: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;
    }

Creare un'istanza della classe nell'attività nel modo seguente:We create an instance of this class in our Activity as follows:

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

Il secondo parametro del costruttore ToDoItemAdapter è un riferimento al layout.The second parameter to the ToDoItemAdapter constructor is a reference to the layout. È ora possibile creare un'istanza di ListView e assegnare l'adattatore a ListView.We can now instantiate the ListView and assign the adapter to the ListView.

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

Usare l'adattatore per l'associazione all'interfaccia utenteUse the Adapter to Bind to the UI

È ora possibile utilizzare l'associazione dati.You are now ready to use data binding. Il codice seguente illustra come recuperare gli elementi nella tabella e inserisce gli elementi restituiti per l'adattatore locale.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);
    }

Chiamare l'adattatore ogni volta che si modifica la tabella ToDoItem .Call the adapter any time you modify the ToDoItem table. Dal momento che le modifiche vengono fatte record per record, si gestisce una singola riga anziché una raccolta.Since modifications are done on a record by record basis, you handle a single row instead of a collection. Quando si inserisce un elemento, chiamare il metodo add sull'adattatore, mentre quando lo si elimina, chiamare il metodo remove.When you insert an item, call the add method on the adapter; when deleting, call the remove method.

Un esempio completo è disponibile nel progetto di avvio rapido per Android.You can find a complete example in the Android Quickstart Project.

Inserire dati nel back-endInsert data into the backend

Viene creata un'istanza della classe ToDoItem e vengono impostate le relative proprietà.Instantiate an instance of the ToDoItem class and set its properties.

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

Usare quindi insert() per inserire un oggetto:Then use insert() to insert an object:

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

L'entità restituita corrisponde ai dati inseriti nella tabella del back-end, inclusi l'ID e gli eventuali altri valori, ad esempio i campi createdAt, updatedAt e version, impostati sul back-end.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.

Le tabelle di App per dispositivi mobili richiedono una colonna chiave primaria denominata id. Questa colonna deve essere una stringa.Mobile Apps tables require a primary key column named id. This column must be a string. Il valore predefinito della colonna ID è un GUID.The default value of the ID column is a GUID. È possibile specificare altri valori univoci, come gli indirizzi di posta elettronica o i nomi utente.You can provide other unique values, such as email addresses or usernames. Se non si fornisce un valore ID di stringa per un record inserito, il back-end genera un nuovo GUID.When a string ID value is not provided for an inserted record, the backend generates a new GUID.

I valori ID di stringa offrono i vantaggi seguenti:String ID values provide the following advantages:

  • Gli ID possono essere generati senza creare un round trip al database.IDs can be generated without making a round trip to the database.
  • L'unione di record da tabelle o database diversi risulta semplificata.Records are easier to merge from different tables or databases.
  • I valori ID si integrano in modo più efficace con la logica di un'applicazione.ID values integrate better with an application's logic.

I valori ID di stringa sono OBBLIGATORI per il supporto di sincronizzazione offline.String ID values are REQUIRED for offline sync support. Non è possibile modificare un ID dopo che è stato archiviato nel database back-end.You cannot change an Id once it is stored in the backend database.

Aggiornare dati in un'app per dispositivi mobiliUpdate data in a mobile app

Per aggiornare i dati in una tabella, passare il nuovo oggetto al metodo update() .To update data in a table, pass the new object to the update() method.

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

In questo esempio item è un riferimento a una riga nella tabella ToDoItem alla quale sono state apportate alcune modifiche.In this example, item is a reference to a row in the ToDoItem table, which has had some changes made to it. Viene aggiornata la riga con lo stesso id .The row with the same id is updated.

Eliminare dati in un'app per dispositivi mobiliDelete data in a mobile app

Il codice seguente illustra come eliminare dati da una tabella specificando l'oggetto dati.The following code shows how to delete data from a table by specifying the data object.

mToDoTable
    .delete(item);

È anche possibile eliminare un elemento specificando il campo id della riga da eliminare.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);

Cercare un elemento specifico per IDLook up a specific item by Id

Cercare un elemento con un campo id specifico con il metodo lookUp():Look up an item with a specific id field with the lookUp() method:

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

Procedura: Usare dati non tipizzatiHow to: Work with untyped data

Il modello di programmazione non tipizzato offre un controllo accurato della serializzazione JSON.The untyped programming model gives you exact control over JSON serialization. È consigliabile usarlo in alcuni scenari comuni.There are some common scenarios where you may wish to use an untyped programming model. Un esempio di utilizzo è una tabella di back-end che contiene un numero elevato di colonne ed è necessario fare riferimento solo ad alcune di esse.For example, if your backend table contains many columns and you only need to reference a subset of the columns. Il modello tipizzato richiede la definizione di tutte le colonne definite nel back-end di App per dispositivi mobili nella classe dati.The typed model requires you to define all the columns defined in the Mobile Apps backend in your data class. La maggior parte delle chiamate API per l'accesso ai dati è simile alle chiamate della programmazione tipizzata.Most of the API calls for accessing data are similar to the typed programming calls. La principale differenza consiste nel fatto che nel modello non tipizzato i metodi vengono chiamati sull'oggetto MobileServiceJsonTable, anziché su MobileServiceTable.The main difference is that in the untyped model you invoke methods on the MobileServiceJsonTable object, instead of the MobileServiceTable object.

Creare un'istanza di una tabella non tipizzataCreate an instance of an untyped table

Analogamente al modello tipizzato, si inizia recuperando un riferimento alla tabella, ma in questo caso si tratta di un oggetto MobileServicesJsonTable .Similar to the typed model, you start by getting a table reference, but in this case it's a MobileServicesJsonTable object. Ottenere il riferimento chiamando il metodo getTable su un'istanza del client:Obtain the reference by calling the getTable method on an instance of the client:

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

Dopo avere creato un'istanza di MobileServiceJsonTable, l'istanza contiene praticamente la stessa API disponibile con il modello di programmazione tipizzato.Once you have created an instance of the MobileServiceJsonTable, it has virtually the same API available as with the typed programming model. In alcuni casi, i metodi accettano un parametro non tipizzato anziché un parametro tipizzato.In some cases, the methods take an untyped parameter instead of a typed parameter.

Eseguire un inserimento in una tabella non tipizzataInsert into an untyped table

Nel codice seguente viene illustrato come eseguire un'operazione di insert.The following code shows how to do an insert. Il primo passaggio consiste nel creare un oggetto JsonObject, incluso nella libreria 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);

Usare quindi il metodo insert() per inserire l'oggetto non tipizzato nella tabella.Then, Use insert() to insert the untyped object into the table.

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

Se è necessario ottenere l'ID dell'oggetto inserito, usare il metodo getAsJsonPrimitive() .If you need to get the ID of the inserted object, use the getAsJsonPrimitive() method.

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

Eliminare un'istanza da una tabella non tipizzataDelete from an untyped table

Nel codice seguente viene illustrato come eliminare un'istanza, in questo caso la stessa istanza di un oggetto JsonObject creato nell'esempio insert precedente.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. Il codice è analogo a quello del caso tipizzato, ma il metodo ha una firma diversa poiché fa riferimento a un oggetto 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);

È inoltre possibile eliminare un'istanza direttamente utilizzando il relativo ID:You can also delete an instance directly by using its ID:

mToDoTable.delete(ID);

Restituire tutte le righe di una tabella non tipizzataReturn all rows from an untyped table

Il seguente codice illustra come recuperare un'intera tabella.The following code shows how to retrieve an entire table. Poiché si sta usando una tabella JSONO, è possibile recuperare in modo selettivo solo alcune colonne della tabella.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();
}

Lo stesso insieme di metodi di filtro e paging disponibili per il modello tipizzato è disponibile per il modello non tipizzato.The same set of filtering, filtering and paging methods that are available for the typed model are available for the untyped model.

Implementare la sincronizzazione offlineImplement Offline Sync

Azure Mobile Apps Client SDK implementa anche la sincronizzazione offline dei dati usando un database SQLite per archiviare una copia locale dei dati del server.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. Le operazioni eseguite su una tabella offline non richiedono la connettività per dispositivi mobili.Operations performed on an offline table do not require mobile connectivity to work. La sincronizzazione offline agevola la resilienza e le prestazioni a scapito di una logica più complessa per la risoluzione dei conflitti.Offline sync aids in resilience and performance at the expense of more complex logic for conflict resolution. Azure Mobile Apps Client SDK implementa le funzionalità seguenti:The Azure Mobile Apps Client SDK implements the following features:

  • Sincronizzazione incrementale: vengono scaricati solo i record aggiornati e nuovi, risparmiando larghezza di banda e utilizzo della memoria.Incremental Sync: Only updated and new records are downloaded, saving bandwidth and memory consumption.
  • Concorrenza ottimistica: si presuppone che le operazioni abbiano esito positivo.Optimistic Concurrency: Operations are assumed to succeed. La risoluzione dei conflitti viene posticipata finché gli aggiornamenti non vengono eseguiti nel server.Conflict Resolution is deferred until updates are performed on the server.
  • Risoluzione dei conflitti: l'SDK rileva quando è stata apportata una modifica in conflitto nel server e fornisce hook per avvisare l'utente.Conflict Resolution: The SDK detects when a conflicting change has been made at the server and provides hooks to alert the user.
  • Eliminazione temporanea: i record eliminati vengono contrassegnati come tali, consentendo agli altri dispositivi di aggiornare la cache offline.Soft Delete: Deleted records are marked deleted, allowing other devices to update their offline cache.

Inizializzare la sincronizzazione offlineInitialize Offline Sync

Ogni tabella offline deve essere definito nella cache offline prima dell'uso.Each offline table must be defined in the offline cache before use. La definizione della tabella in genere viene eseguita immediatamente dopo la creazione del 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);
}

Ottenere un riferimento alla tabella della cache offlineObtain a reference to the Offline Cache Table

Per una tabella online, si usa .getTable().For an online table, you use .getTable(). Per una tabella offline, usare .getSyncTable():For an offline table, use .getSyncTable():

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

Tutti i metodi disponibili per le tabelle online (inclusi filtri, ordinamento, paging, inserimento di dati, aggiornamento di dati ed eliminazione di dati) funzionano correttamente sia nelle tabelle online che in quelle offline.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.

Sincronizzare la cache locale offlineSynchronize the Local Offline Cache

La sincronizzazione è sotto il controllo dell'app.Synchronization is within the control of your app. Ecco un esempio di metodo di sincronizzazione: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);
}

Se viene fornito un nome di query al metodo .pull(query, queryname), viene usata la sincronizzazione incrementale per restituire solo i record creati o modificati dopo il corretto completamento dell'ultimo pull.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.

Gestire i conflitti durante la sincronizzazione offlineHandle Conflicts during Offline Synchronization

Se si verifica un conflitto durante un'operazione .push(), viene generata un'eccezione MobileServiceConflictException.If a conflict happens during a .push() operation, a MobileServiceConflictException is thrown. L'elemento rilasciato dal server viene incorporato nell'eccezione e può essere recuperato da .getItem() nell'eccezione.The server-issued item is embedded in the exception and can be retrieved by .getItem() on the exception. Modificare il push chiamando gli elementi seguenti sull'oggetto MobileServiceSyncContext:Adjust the push by calling the following items on the MobileServiceSyncContext object:

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

Dopo che tutti i conflitti sono stati contrassegnati nel modo desiderato, chiamare di nuovo .push() per risolvere tutti i conflitti.Once all conflicts are marked as you wish, call .push() again to resolve all the conflicts.

Chiamare un'API personalizzataCall a custom API

Un'API personalizzata consente di definire endpoint personalizzati che espongono la funzionalità del server di cui non è possibile eseguire il mapping a un'operazione di inserimento, aggiornamento, eliminazione o lettura.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. L'utilizzo di un'API personalizzata offre maggiore controllo sulla messaggistica, incluse la lettura e l'impostazione delle intestazioni del messaggio HTTP e la definizione di un formato del corpo del messaggio diverso da 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.

Per chiamare l'endpoint dell'API personalizzata da un client Android, chiamare il metodo invokeApi .From an Android client, you call the invokeApi method to call the custom API endpoint. L'esempio seguente illustra come chiamare un endpoint API denominato completeAll, che restituisce una classe della raccolta denominata 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();
        }
    });
}

Nel client viene chiamato il metodo invokeApi , che invia una richiesta POST alla nuova API personalizzata.The invokeApi method is called on the client, which sends a POST request to the new custom API. Il risultato restituito dall'API personalizzata viene visualizzato in una finestra di dialogo con messaggio, insieme a eventuali errori.The result returned by the custom API is displayed in a message dialog, as are any errors. Altre versioni di invokeApi consentono di inviare facoltativamente un oggetto nel corpo della richiesta, specificare il metodo HTTP e inviare parametri di query con la richiesta.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. Vengono fornite anche versioni non tipizzate di invokeApi .Untyped versions of invokeApi are provided as well.

Aggiungere l'autenticazione all'appAdd authentication to your app

Le esercitazioni descrivono già in dettaglio come aggiungere queste funzionalità.Tutorials already describe in detail how to add these features.

Il servizio app supporta l'autenticazione degli utenti di app con diversi provider di identità esterni: Facebook, Google, account Microsoft, Twitter e Azure Active Directory.App Service supports authenticating app users using various external identity providers: Facebook, Google, Microsoft Account, Twitter, and Azure Active Directory. È possibile impostare le autorizzazioni per le tabelle per limitare l'accesso per operazioni specifiche solo agli utenti autenticati.You can set permissions on tables to restrict access for specific operations to only authenticated users. È inoltre possibile utilizzare l'identità degli utenti autenticati per implementare regole di autorizzazione nel backend.You can also use the identity of authenticated users to implement authorization rules in your backend.

Sono supportati due flussi di autenticazione, ovvero un flusso server e un flusso client.Two authentication flows are supported: a server flow and a client flow. Il flusso server è il processo di autenticazione più semplice, poiché si basa sull'interfaccia Web del provider di identità.The server flow provides the simplest authentication experience, as it relies on the identity providers web interface. Non sono necessari SDK aggiuntivi per implementare l'autenticazione del flusso server.No additional SDKs are required to implement server flow authentication. L'autenticazione del flusso server non fornisce un'integrazione completa con il dispositivo mobile ed è consigliata solo per dimostrare scenari concettuali.Server flow authentication does not provide a deep integration into the mobile device and is only recommended for proof of concept scenarios.

Il flusso client assicura maggiore integrazione con funzionalità specifiche del dispositivo, ad esempio Single-Sign-On, poiché si basa su SDK forniti dal provider di 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. Ad esempio, è possibile integrare l'SDK di Facebook nell'applicazione per dispositivi mobili.For example, you can integrate the Facebook SDK into your mobile application. Il client per dispositivi mobili passa all'app Facebook e conferma l'accesso prima di passare di nuovo all'app per dispositivi mobili.The mobile client swaps into the Facebook app and confirms your sign-on before swapping back to your mobile app.

Per abilitare l'autenticazione nell'app, è necessario eseguire quattro passaggi:Four steps are required to enable authentication in your app:

  • Registrare l'app per l'autenticazione con un provider di identità.Register your app for authentication with an identity provider.
  • Configurare il back-end del servizio app.Configure your App Service backend.
  • Limitare le autorizzazioni per la tabella solo agli utenti autenticati nel back-end del servizio app.Restrict table permissions to authenticated users only on the App Service backend.
  • Aggiungere codice di autenticazione all'app.Add authentication code to your app.

È possibile impostare le autorizzazioni per le tabelle per limitare l'accesso per operazioni specifiche solo agli utenti autenticati.You can set permissions on tables to restrict access for specific operations to only authenticated users. Per modificare le richieste, è anche possibile usare il SID di un utente autenticato.You can also use the SID of an authenticated user to modify requests. Per altre informazioni, vedere Aggiungere l'autenticazione all'app Android e la documentazione sulle procedure dell'SDK del server.For more information, review [Get started with authentication] and the Server SDK HOWTO documentation.

Autenticazione: flusso serverAuthentication: Server Flow

Il codice seguente avvia la procedura di accesso del flusso server con il provider Google.The following code starts a server flow login process using the Google provider. È necessaria una configurazione aggiuntiva a causa dei requisiti di sicurezza per il provider 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);

Aggiungere anche il metodo seguente alla classe Activity 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'elemento GOOGLE_LOGIN_REQUEST_CODE definito nell'attività principale viene usato per il metodo login() e nel metodo onActivityResult().The GOOGLE_LOGIN_REQUEST_CODE defined in your main Activity is used for the login() method and within the onActivityResult() method. È possibile scegliere qualsiasi numero univoco, purché lo stesso numero venga usato nei metodi login() e onActivityResult().You can choose any unique number, as long as the same number is used within the login() method and the onActivityResult() method. Se si astrae il codice client in un adattatore di un servizio (come illustrato prima), è consigliabile chiamare i metodi appropriati sull'adattatore del servizio.If you abstract the client code into a service adapter (as shown earlier), you should call the appropriate methods on the service adapter.

È anche necessario configurare il progetto per customtabs.You also need to configure the project for customtabs. Specificare prima di tutto un URL di reindirizzamento.First specify a redirect-URL. Aggiungere il frammento di codice seguente a 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>

Aggiungere redirectUriScheme al file build.gradle per l'applicazione: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']
        }
    }
}

Aggiungere infine com.android.support:customtabs:23.0.1 all'elenco di dipendenze nel file build.gradle:Finally, add com.android.support:customtabs:23.0.1 to the dependencies list in the build.gradle file:

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

È possibile ottenere l'ID dell'utente connesso da un oggetto MobileServiceUser usando il metodo getUserId.Obtain the ID of the logged-in user from a MobileServiceUser using the getUserId method. Per un esempio di come usare Futures per chiamare le API di accesso asincrone, vedere Aggiungere l'autenticazione all'app Android.For an example of how to use Futures to call the asynchronous login APIs, see [Get started with authentication].

Avviso

Lo schema URL indicato rispetta la distinzione tra maiuscole e minuscole.The URL Scheme mentioned is case-sensitive. Assicurarsi che tutte le occorrenze di {url_scheme_of_you_app} rispettino questa distinzione.Ensure that all occurrences of {url_scheme_of_you_app} match case.

Memorizzare nella cache i token di autenticazioneCache authentication tokens

Per memorizzare nella cache i token di autenticazione, è necessario archiviare l'ID utente e il token di autenticazione in locale nel dispositivo.Caching authentication tokens requires you to store the User ID and authentication token locally on the device. Al successivo avvio dell'app, la cache viene verificata e, se sono presenti questi valori, è possibile ignorare la procedura di accesso e riattivare il client con questi dati.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. Questi dati sono tuttavia sensibili e devono essere crittografati per garantire la sicurezza in caso di furto del telefono.However this data is sensitive, and it should be stored encrypted for safety in case the phone gets stolen. È possibile vedere un esempio completo della memorizzazione nella cache dei token di autenticazione nella sezione Memorizzare nella cache i token di autenticazione.You can see a complete example of how to cache authentication tokens in Cache authentication tokens section.

Quando si prova a usare un token scaduto, viene visualizzata una risposta di tipo 401 - Non autorizzato .When you try to use an expired token, you receive a 401 unauthorized response. È possibile gestire gli errori di autenticazione tramite i filtri.You can handle authentication errors using filters. I filtri intercettano le richieste al back-end del servizio app.Filters intercept requests to the App Service backend. Il codice di filtro verifica quindi la risposta per un errore di tipo 401, attiva il processo di accesso e quindi riprende la richiesta che ha generato l'errore.The filter code tests the response for a 401, triggers the sign-in process, and then resumes the request that generated the 401.

Usare i token di aggiornamentoUse Refresh Tokens

La durata definita del token restituito dall'autenticazione e dall'autorizzazione nel servizio app di Azure è di un'ora.The token returned by Azure App Service Authentication and Authorization has a defined life time of one hour. Dopo questo periodo, è necessario ripetere l'autenticazione dell'utente.After this period, you must reauthenticate the user. Se si usa un token di lunga durata ricevuto tramite l'autenticazione basata sul flusso client, è possibile ripetere l'autenticazione con l'autenticazione e l'autorizzazione del servizio app di Azure usando lo stesso token.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. Viene generato un altro token del servizio app di Azure con una nuova durata.Another Azure App Service token is generated with a new lifetime.

È anche possibile registrare il provider per l'uso di token di aggiornamento.You can also register the provider to use Refresh Tokens. Un token di aggiornamento non è sempre disponibile.A Refresh Token is not always available. È necessaria una configurazione aggiuntiva:Additional configuration is required:

  • Per Azure Active Directory, configurare un segreto client per l'app Azure Active Directory.For Azure Active Directory, configure a client secret for the Azure Active Directory App. Specificare il segreto client nel servizio app di Azure durante la configurazione dell'autenticazione di Azure Active Directory.Specify the client secret in the Azure App Service when configuring Azure Active Directory Authentication. Quando si chiama .login(), passare response_type=code id_token come parametro: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);
    
  • Per Google, passare access_type=offline come parametro: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);
    
  • Per un account Microsoft, selezionare l'ambito wl.offline_access.For Microsoft Account, select the wl.offline_access scope.

Per aggiornare un token, chiamare .refreshUser():To refresh a token, call .refreshUser():

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

Come procedura consigliata, creare un filtro che rileva una risposta 401 dal server e prova ad aggiornare il token dell'utente.As a best practice, create a filter that detects a 401 response from the server and tries to refresh the user token.

Accedere con l'autenticazione basata sul flusso clientLog in with Client-flow Authentication

Il processo generale per l'accesso con l'autenticazione basata sul flusso client è il seguente:The general process for logging in with client-flow authentication is as follows:

  • Configurare l'autenticazione e l'autorizzazione del servizio app di Azure come si configura l'autenticazione basata sul flusso server.Configure Azure App Service Authentication and Authorization as you would server-flow authentication.
  • Integrare l'SDK del provider di autenticazione per generare un token di accesso.Integrate the authentication provider SDK for authentication to produce an access token.
  • Chiamare il metodo .login() come illustrato di seguito:Call the .login() method as follows:

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

Sostituire il metodo onSuccess() con il codice che si vuole usare all'esecuzione di un accesso.Replace the onSuccess() method with whatever code you wish to use on a successful login. La stringa {provider} è un provider valido: aad (Azure Active Directory), facebook, google, microsoftaccount o twitter.The {provider} string is a valid provider: aad (Azure Active Directory), facebook, google, microsoftaccount, or twitter. Se è stata implementata l'autenticazione personalizzata, è anche possibile usare il tag del provider di autenticazione personalizzata.If you have implemented custom authentication, then you can also use the custom authentication provider tag.

Autenticare gli utenti con Active Directory Authentication Library (ADAL)Authenticate users with the Active Directory Authentication Library (ADAL)

È possibile usare Active Directory Authentication Library (ADAL) per far accedere gli utenti all'applicazione tramite Azure Active Directory.You can use the Active Directory Authentication Library (ADAL) to sign users into your application using Azure Active Directory. L'uso dell'accesso del flusso client è spesso preferibile all'uso dei metodi loginAsync() , perché garantisce un'esperienza utente più naturale e consente una maggiore personalizzazione.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. Configurare il back-end dell'app per dispositivi mobili per l'accesso ad Azure Active Directory seguendo l'esercitazione Come configurare un'applicazione del servizio app per usare l'account di accesso di 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. Assicurarsi di completare il passaggio facoltativo di registrazione di un'applicazione client nativa.Make sure to complete the optional step of registering a native client application.
  2. Installare ADAL modificando il file build.gradle per includere le definizioni seguenti: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 {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile('com.microsoft.aad:adal:1.1.1') {
        exclude group: 'com.android.support'
    } // Recent version is 1.1.1
    compile 'com.android.support:support-v4:23.0.0'
}
  1. Aggiungere all'applicazione il codice seguente apportando le sostituzioni seguenti:Add the following code to your application, making the following replacements:
  • Sostituire INSERT-AUTHORITY-HERE con il nome del tenant in cui è stato eseguito il provisioning dell'applicazione.Replace INSERT-AUTHORITY-HERE with the name of the tenant in which you provisioned your application. Il formato deve essere https://login.microsoftonline.com/contoso.onmicrosoft.com.The format should be https://login.microsoftonline.com/contoso.onmicrosoft.com.
  • Sostituire INSERT-RESOURCE-ID-HERE con l'ID client per il back-end dell'app per dispositivi mobili.Replace INSERT-RESOURCE-ID-HERE with the client ID for your mobile app backend. L'ID client è disponibile nella scheda Avanzate in Impostazioni di Azure Active Directory nel portale.You can obtain the client ID from the Advanced tab under Azure Active Directory Settings in the portal.
  • Sostituire INSERT-CLIENT-ID-HERE con l'ID client copiato dall'applicazione client nativa.Replace INSERT-CLIENT-ID-HERE with the client ID you copied from the native client application.
  • Sostituire INSERT-REDIRECT-URI-HERE con l'endpoint /.auth/login/done del sito, usando lo schema HTTPS.Replace INSERT-REDIRECT-URI-HERE with your site's /.auth/login/done endpoint, using the HTTPS scheme. Questo valore deve essere simile a 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);
    }
}

Modificare la comunicazione client-serverAdjust the Client-Server Communication

La connessione client è in genere una connessione HTTP di base che usa la libreria HTTP sottostante fornita con Android SDK.The Client connection is normally a basic HTTP connection using the underlying HTTP library supplied with the Android SDK. I motivi per cui potrebbe essere necessario modificarla sono diversi:There are several reasons why you would want to change that:

  • Si vuole usare una libreria HTTP alternativa per modificare i timeout.You wish to use an alternate HTTP library to adjust timeouts.
  • Si vuole fornire un indicatore di stato.You wish to provide a progress bar.
  • Si vuole aggiungere un'intestazione personalizzata per supportare la funzionalità di gestione API.You wish to add a custom header to support API management functionality.
  • Si vuole intercettare una risposta non riuscita per poter implementare la riautenticazione.You wish to intercept a failed response so that you can implement reauthentication.
  • Si vogliono registrare le richieste del back-end in un servizio di analisi.You wish to log backend requests to an analytics service.

Uso di una libreria HTTP alternativaUsing an alternate HTTP Library

Chiamare il metodo .setAndroidHttpClientFactory() immediatamente dopo avere creato il riferimento al client.Call the .setAndroidHttpClientFactory() method immediately after creating your client reference. Ad esempio, per impostare il timeout di connessione su 60 secondi, invece dei 10 secondi predefiniti: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 OkHttpClinet();
        client.setReadTimeout(60, TimeUnit.SECONDS);
        client.setWriteTimeout(60, TimeUnit.SECONDS);
        return client;
    }
});

Implementare un filtro per l'avanzamentoImplement a Progress Filter

È possibile implementare un intercettazione di ogni richiesta implementando ServiceFilter.You can implement an intercept of every request by implementing a ServiceFilter. Il codice seguente ad esempio aggiorna un indicatore stato già creato: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
                    pubic void run() {
                        if (mProgressBar != null)
                            mProgressBar.setVisibility(ProgressBar.GONE);
                    }
                });
                resultFuture.set(response);
            }
        });
        return resultFuture;
    }
}

È possibile collegare questo filtro al client come segue:You can attach this filter to the client as follows:

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

Personalizzare le intestazioni delle richiesteCustomize Request Headers

Usare l'elemento ServiceFilter seguente e collegare il filtro come per 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);
        }
    }
}

Configurare la serializzazione automaticaConfigure Automatic Serialization

È possibile specificare una strategia di conversione che si applica a tutte le colonne tramite l'API gson.You can specify a conversion strategy that applies to every column by using the gson API. La libreria client Android usa gson in modo invisibile per serializzare oggetti Java in dati JSON, prima che i dati vengano inviati al Servizio app di Azure.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. Il codice seguente usa setFieldNamingStrategy() per impostare la strategia.The following code uses the setFieldNamingStrategy() method to set the strategy. Questo esempio elimina il carattere iniziale (una "m") e quindi applica il minuscolo al carattere successivo per ogni nome di campo,This example will delete the initial character (an "m"), and then lower-case the next character, for every field name. ad esempio, trasforma "mId" in "id".For example, it would turn "mId" into "id." Implementare una strategia di conversione per ridurre la necessità di annotazioni SerializedName() nella maggior parte dei campi.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(namingStategy)
);

Questo codice deve essere eseguito prima di creare un riferimento al client per dispositivi mobili usando MobileServiceClient.This code must be executed before creating a mobile client reference using the MobileServiceClient.