Como usar o SDK de aplicativos móveis do Azure para AndroidHow to use the Azure Mobile Apps SDK for Android

Observação

O Visual Studio App Center dá suporte a serviços de ponta a ponta e integrados central ao desenvolvimento de aplicativos móveis.Visual Studio App Center supports end to end and integrated services central to mobile app development. Os desenvolvedores podem usar Compilar, testar e distribuir serviços para configurar o pipeline de integração e entrega contínua.Developers can use Build, Test and Distribute services to set up Continuous Integration and Delivery pipeline. Depois que o aplicativo é implantado, os desenvolvedores podem monitorar o status e o uso de seus aplicativos usando os serviços de análise e diagnóstico e se envolver com os usuários usando o serviço de envio por 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. Os desenvolvedores também podem aproveitar a autenticação para autenticar seus usuários e o serviço de dados para manter e sincronizar dados de aplicativos na nuvem.Developers can also leverage Auth to authenticate their users and Data service to persist and sync app data in the cloud.

Se você estiver procurando integrar os serviços de nuvem em seu aplicativo móvel, Inscreva-se com o app Center hoje mesmo.If you are looking to integrate cloud services in your mobile application, sign up with App Center today.

Este guia mostra como usar o SDK do cliente Android para aplicativos móveis para implementar cenários comuns, como:This guide shows you how to use the Android client SDK for Mobile Apps to implement common scenarios, such as:

  • Consultando dados (inserindo, atualizando e excluindo).Querying for data (inserting, updating, and deleting).
  • Authentication.Authentication.
  • Tratamento de erros.Handling errors.
  • Personalizando o cliente.Customizing the client.

Este guia destaca o SDK do Android no lado do cliente.This guide focuses on the client-side Android SDK. Para saber mais sobre os SDKs do lado do servidor para aplicativos móveis, confira trabalhar com o SDK de back-end do .net ou como usar o SDK de back-end do node. js.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.

Documentação de referênciaReference Documentation

Você pode encontrar a referência de API de Javadoc para a biblioteca de cliente Android no github.You can find the Javadocs API reference for the Android client library on GitHub.

Plataformas com suporteSupported Platforms

O SDK de aplicativos móveis do Azure para Android dá suporte aos níveis de API de 19 a 24 (KitKat até nougat) para fatores de forma de telefone e Tablet.The Azure Mobile Apps SDK for Android supports API levels 19 through 24 (KitKat through Nougat) for phone and tablet form factors. A autenticação, em particular, utiliza uma abordagem de estrutura da Web comum para coletar credenciais.Authentication, in particular, utilizes a common web framework approach to gather credentials. A autenticação de fluxo de servidor não funciona com dispositivos de fator forma pequeno, como inspeções.Server-flow authentication does not work with small form factor devices such as watches.

Instalação e pré-requisitosSetup and Prerequisites

Conclua o tutorial Início rápido de Aplicativos Móveis .Complete the Mobile Apps quickstart tutorial. Essa tarefa garante que todos os pré-requisitos para o desenvolvimento de aplicativos móveis do Azure tenham sido atendidos.This task ensures all pre-requisites for developing Azure Mobile Apps have been met. O guia de início rápido também ajuda a configurar sua conta e criar seu primeiro back-end de aplicativo móvel.The Quickstart also helps you configure your account and create your first Mobile App backend.

Se você decidir não concluir o tutorial de início rápido, conclua as seguintes tarefas:If you decide not to complete the Quickstart tutorial, complete the following tasks:

Atualizar o arquivo de compilação do GradleUpdate the Gradle build file

Altere ambos os arquivos build.gradle :Change both build.gradle files:

  1. Adicione este código ao arquivo Build. gradle de nível de projeto :Add this code to the Project level build.gradle file:

    buildscript {
        repositories {
            jcenter()
            google()
        }
    }
    
    allprojects {
        repositories {
            jcenter()
            google()
        }
    }
    
  2. Adicione este código ao arquivo build.gradle do nível Module app dentro da marca 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'
    

    Atualmente, a versão mais recente é 3.4.0.Currently the latest version is 3.4.0. As versões com suporte são listadas em bintray.The supported versions are listed on bintray.

Habilitar a permissão de InternetEnable internet permission

Para acessar o Azure, seu aplicativo deve ter a permissão de INTERNET habilitada.To access Azure, your app must have the INTERNET permission enabled. Se ela ainda não estiver habilitada, adicione a seguinte linha de código ao arquivo 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" />

Criar uma conexão de clienteCreate a Client Connection

Os aplicativos móveis do Azure fornecem quatro funções para seu aplicativo móvel:Azure Mobile Apps provides four functions to your mobile application:

  • Acesso a dados e sincronização offline com um serviço de aplicativos móveis do Azure.Data Access and Offline Synchronization with an Azure Mobile Apps Service.
  • Chamar APIs personalizadas escritas com o SDK do servidor de aplicativos móveis do Azure.Call Custom APIs written with the Azure Mobile Apps Server SDK.
  • Autenticação com Azure App autenticação e autorização do serviço.Authentication with Azure App Service Authentication and Authorization.
  • Registro de notificação por push com hubs de notificação.Push Notification Registration with Notification Hubs.

Cada uma dessas funções exige primeiro que você crie um objeto MobileServiceClient.Each of these functions first requires that you create a MobileServiceClient object. Somente um objeto MobileServiceClient deve ser criado dentro de seu cliente móvel (ou seja, deve ser um padrão singleton).Only one MobileServiceClient object should be created within your mobile client (that is, it should be a Singleton pattern). Para criar um objeto de MobileServiceClient:To create a MobileServiceClient object:

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

O <MobileAppUrl> é uma cadeia de caracteres ou um objeto de URL que aponta para o back-end móvel.The <MobileAppUrl> is either a string or a URL object that points to your mobile backend. Se você estiver usando Azure App serviço para hospedar seu back-end móvel, certifique-se de usar a versão segura https:// da URL.If you are using Azure App Service to host your mobile backend, then ensure you use the secure https:// version of the URL.

O cliente também requer acesso à atividade ou contexto-o parâmetro this no exemplo.The client also requires access to the Activity or Context - the this parameter in the example. A construção MobileServiceClient deve acontecer dentro do método onCreate() da atividade referenciada no arquivo AndroidManifest.xml.The MobileServiceClient construction should happen within the onCreate() method of the Activity referenced in the AndroidManifest.xml file.

Como prática recomendada, você deve abstrair a comunicação do servidor em sua própria classe (padrão singleton).As a best practice, you should abstract server communication into its own (singleton-pattern) class. Nesse caso, você deve passar a atividade dentro do construtor para configurar adequadamente o serviço.In this case, you should pass the Activity within the constructor to appropriately configure the service. Por exemplo: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.
}

Agora você pode chamar AzureServiceAdapter.Initialize(this); no método onCreate() de sua atividade principal.You can now call AzureServiceAdapter.Initialize(this); in the onCreate() method of your main activity. Quaisquer outros métodos que precisam de acesso ao cliente usam AzureServiceAdapter.getInstance(); para obter uma referência ao adaptador de serviço.Any other methods needing access to the client use AzureServiceAdapter.getInstance(); to obtain a reference to the service adapter.

Operações de dadosData Operations

O núcleo do SDK dos aplicativos móveis do Azure é fornecer acesso aos dados armazenados em SQL Azure no back-end do aplicativo móvel.The core of the Azure Mobile Apps SDK is to provide access to data stored within SQL Azure on the Mobile App backend. Você pode acessar esses dados usando classes com rigidez de tipos (preferenciais) ou consultas não tipadas (não recomendado).You can access this data using strongly typed classes (preferred) or untyped queries (not recommended). A massa desta seção lida com o uso de classes com rigidez de tipos.The bulk of this section deals with using strongly typed classes.

Definir classes de dados do clienteDefine client data classes

Para acessar dados de tabelas SQL Azure, defina as classes de dados do cliente que correspondem às tabelas no back-end do aplicativo móvel.To access data from SQL Azure tables, define client data classes that correspond to the tables in the Mobile App backend. Os exemplos neste tópico pressupõem uma tabela chamada MyDataTable, que tem as seguintes colunas:Examples in this topic assume a table named MyDataTable, which has the following columns:

  • IDid
  • textotext
  • concluícomplete

O objeto do lado do cliente digitado correspondente reside em um arquivo chamado 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;
}

Adicione os métodos getter e setter para cada campo que você adicionar.Add getter and setter methods for each field that you add. Se sua tabela de SQL Azure contiver mais colunas, você adicionaria os campos correspondentes a essa classe.If your SQL Azure table contains more columns, you would add the corresponding fields to this class. Por exemplo, se o DTO (objeto de transferência de dados) tivesse uma coluna Priority de inteiros, você poderia adicionar este campo, com seus métodos 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;
}

Para saber como criar tabelas adicionais em seu back-end de aplicativos móveis, consulte como definir um controlador de tabela (back-end do .net) ou definir tabelas usando um esquema dinâmico (back-end do 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).

Uma tabela de back-end dos aplicativos móveis do Azure define cinco campos especiais, quatro dos quais estão disponíveis para os clientes:An Azure Mobile Apps backend table defines five special fields, four of which are available to clients:

  • String id: a ID globalmente exclusiva para o registro.String id: The globally unique ID for the record. Como prática recomendada, torne a ID a representação de cadeia de caracteres de um objeto UUID .As a best practice, make the id the String representation of a UUID object.
  • DateTimeOffset updatedAt: a data/hora da última atualização.DateTimeOffset updatedAt: The date/time of the last update. O campo updatedAt é definido pelo servidor e nunca deve ser definido pelo seu código de cliente.The updatedAt field is set by the server and should never be set by your client code.
  • DateTimeOffset createdAt: a data/hora em que o objeto foi criado.DateTimeOffset createdAt: The date/time that the object was created. O campo createdAt é definido pelo servidor e nunca deve ser definido pelo seu código de cliente.The createdAt field is set by the server and should never be set by your client code.
  • byte[] version: normalmente representado como uma cadeia de caracteres, a versão também é definida pelo servidor.byte[] version: Normally represented as a string, the version is also set by the server.
  • boolean deleted: indica que o registro foi excluído, mas ainda não foi limpo.boolean deleted: Indicates that the record has been deleted but not purged yet. Não use deleted como uma propriedade em sua classe.Do not use deleted as a property in your class.

O campo id é obrigatório.The id field is required. O campo updatedAt e version campo são usados para sincronização offline (para sincronização incremental e resolução de conflitos, respectivamente).The updatedAt field and version field are used for offline synchronization (for incremental sync and conflict resolution respectively). O campo createdAt é um campo de referência e não é usado pelo cliente.The createdAt field is a reference field and is not used by the client. Os nomes são nomes "através do fio" das propriedades e não são ajustáveis.The names are "across-the-wire" names of the properties and are not adjustable. No entanto, você pode criar um mapeamento entre o objeto e os nomes "através do fio" usando a biblioteca Gson .However, you can create a mapping between your object and the "across-the-wire" names using the gson library. Por exemplo: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();
    }
}

Criar uma referência de tabelaCreate a Table Reference

Para acessar uma tabela, primeiro crie um objeto função mobileservicetable chamando o método GetTable no MobileServiceClient.To access a table, first create a MobileServiceTable object by calling the getTable method on the MobileServiceClient. Esse método tem duas sobrecargas: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);
}

No código a seguir, mClient é uma referência ao objeto MobileServiceClient.In the following code, mClient is a reference to your MobileServiceClient object. A primeira sobrecarga é usada onde o nome da classe e o nome da tabela são os mesmos, e é aquele usado no início rápido: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);

A segunda sobrecarga é usada quando o nome da tabela é diferente do nome da classe: o primeiro parâmetro é o nome da tabela.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);

Consultar uma tabela de back-endQuery a Backend Table

Primeiro, obtenha uma referência de tabela.First, obtain a table reference. Em seguida, execute uma consulta na referência de tabela.Then execute a query on the table reference. Uma consulta é qualquer combinação de:A query is any combination of:

As cláusulas devem ser apresentadas na ordem anterior.The clauses must be presented in the preceding order.

Filtrando resultadosFiltering Results

A forma geral de uma consulta é: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

O exemplo anterior retorna todos os resultados (até o tamanho máximo de página definido pelo servidor).The preceding example returns all results (up to the maximum page size set by the server). O método .execute() executa a consulta no back-end.The .execute() method executes the query on the backend. A consulta é convertida em uma consulta OData v3 antes da transmissão para o back-end dos aplicativos móveis.The query is converted to an OData v3 query before transmission to the Mobile Apps backend. No recebimento, o back-end dos aplicativos móveis converte a consulta em uma instrução SQL antes de executá-la na instância de SQL Azure.On receipt, the Mobile Apps backend converts the query into an SQL statement before executing it on the SQL Azure instance. Como a atividade de rede leva algum tempo, o método .execute() retorna um ListenableFuture<E>.Since network activity takes some time, The .execute() method returns a ListenableFuture<E>.

Filtrar dados retornadosFilter returned data

A execução da consulta a seguir retorna todos os itens da tabela ToDoItem, em que complete é igual 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 é a referência à tabela de serviços móveis que criamos anteriormente.mToDoTable is the reference to the mobile service table that we created previously.

Defina um filtro usando a chamada ao método where na referência de tabela.Define a filter using the where method call on the table reference. O método where é seguido de um método field seguido de um método que especifica o predicado lógico.The where method is followed by a field method followed by a method that specifies the logical predicate. Os possíveis métodos de predicado incluem eq (igual),ne (diferente), gt (maior que), ge (maior que ou igual a), lt (menor que), le (menor que ou igual 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). Esses métodos permitem que você compare os campos de número e cadeia de caracteres com valores específicos.These methods let you compare number and string fields to specific values.

Você pode filtrar por datas.You can filter on dates. Os métodos a seguir permitem comparar o campo de data inteira ou partes da 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. O exemplo a seguir adiciona um filtro para itens cuja data de vencimento é igual 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();

Os métodos a seguir oferecem suporte a filtros complexos nos campos de cadeia de caracteres: 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. O exemplo a seguir filtra linhas de tabela em que a coluna text começa com "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();

Os métodos de operador a seguir têm suporte em campos de número: 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. O exemplo a seguir filtra linhas de tabela em que duration é um número par.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();

É possível combinar predicados com estes métodos lógicos: and, or e not.You can combine predicates with these logical methods: and, or and not. O exemplo a seguir combina dois dos exemplos anteriores.The following example combines two of the preceding examples.

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

Agrupar e aninhar operadores lógicos: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();

Para ver uma discussão mais detalhada e exemplos de filtragem, veja Explorando a riqueza do modelo de consulta de cliente do Android.For more detailed discussion and examples of filtering, see Exploring the richness of the Android client query model.

Classificar dados retornadosSort returned data

O código a seguir retorna todos os itens de uma tabela ToDoItem classificada em ordem crescente pelo campo text .The following code returns all items from a table of ToDoItems sorted ascending by the text field. mToDoTable é a referência à tabela de back-end criada anteriormente:mToDoTable is the reference to the backend table that you created previously:

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

O primeiro parâmetro do método orderBy é uma cadeia de caracteres igual ao nome do campo pelo qual classificar.The first parameter of the orderBy method is a string equal to the name of the field on which to sort. O segundo parâmetro usa a enumeração QueryOrder para especificar se a classificação será feita em ordem crescente ou decrescente.The second parameter uses the QueryOrder enumeration to specify whether to sort ascending or descending. Se você estiver filtrando usando o método where, o método where deverá ser invocado antes do método orderBy.If you are filtering using the where method, the where method must be invoked before the orderBy method.

Selecionar colunas específicasSelect specific columns

O código a seguir ilustra como retornar todos os itens de uma tabela de ToDoItems, mas exibe apenas os 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 é a referência à tabela de back-end criada anteriormente.mToDoTable is the reference to the backend table that we created previously.

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

Os parâmetros para a função Select são os nomes de cadeia de caracteres das colunas da tabela que você deseja retornar.The parameters to the select function are the string names of the table's columns that you want to return. O método select precisa seguir métodos como where e orderBy.The select method needs to follow methods like where and orderBy. Ele pode ser seguido por métodos de paginação como Skip e Top.It can be followed by paging methods like skip and top.

Retornar dados em páginasReturn data in pages

Os dados são sempre retornados em páginas.Data is ALWAYS returned in pages. O número máximo de registros retornados é definido pelo servidor.The maximum number of records returned is set by the server. Se o cliente solicitar mais registros, o servidor retornará o número máximo de registros.If the client requests more records, then the server returns the maximum number of records. Por padrão, o tamanho máximo da página no servidor é de 50 registros.By default, the maximum page size on the server is 50 records.

O primeiro exemplo mostra como selecionar os cinco primeiros itens de uma tabela.The first example shows how to select the top five items from a table. A consulta retorna os itens de uma tabela ToDoItems.The query returns the items from a table of ToDoItems. mToDoTable é a referência à tabela de back-end criada anteriormente:mToDoTable is the reference to the backend table that you created previously:

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

Aqui está uma consulta que ignora os cinco primeiros itens e, em seguida, retorna os cinco seguintes: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();

Se você quiser obter todos os registros em uma tabela, implemente o código para iterar em todas as páginas: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);

Uma solicitação para todos os registros que usam esse método cria um mínimo de duas solicitações para o back-end de aplicativos móveis.A request for all records using this method creates a minimum of two requests to the Mobile Apps backend.

Dica

Escolher o tamanho de página correto é um equilíbrio entre o uso de memória enquanto a solicitação está acontecendo, o uso da largura de banda e o atraso no recebimento completo dos dados.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. O padrão (registros 50) é adequado para todos os dispositivos.The default (50 records) is suitable for all devices. Se você operar exclusivamente em dispositivos de memória maiores, aumente até 500.If you exclusively operate on larger memory devices, increase up to 500. Descobrimos que aumentar o tamanho da página além de 500 registros resulta em atrasos inaceitáveis e grandes problemas de memória.We have found that increasing the page size beyond 500 records results in unacceptable delays and large memory issues.

Como concatenar métodos de consultaHow to: Concatenate query methods

Os métodos usados na consulta de tabelas de back-end podem ser concatenados.The methods used in querying backend tables can be concatenated. Os métodos de consulta de encadeamento permitem selecionar colunas específicas de linhas filtradas que são classificadas e paginadas.Chaining query methods allows you to select specific columns of filtered rows that are sorted and paged. Você pode criar filtros lógicos complexos.You can create complex logical filters. Cada método de consulta retorna um objeto de consulta.Each query method returns a Query object. Para encerrar a série de métodos e realmente executar a consulta, chame o método execute .To end the series of methods and actually run the query, call the execute method. Por exemplo: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();

Os métodos de consulta encadeados devem ser ordenados da seguinte maneira:The chained query methods must be ordered as follows:

  1. Métodos de filtragem (where).Filtering (where) methods.
  2. Métodos de classificação (orderBy).Sorting (orderBy) methods.
  3. Métodos de seleção (select).Selection (select) methods.
  4. Métodos de paginação (skip e top).paging (skip and top) methods.

Associar dados à interface do usuárioBind data to the user interface

A vinculação de dados envolve três componentes:Data binding involves three components:

  • A fonte de dadosThe data source
  • O layout da telaThe screen layout
  • O adaptador que une os dois juntos.The adapter that ties the two together.

Em nosso código de exemplo, retornamos os dados da tabela ToDoItem do SQL Azure dos Aplicativos Móveis em uma matriz.In our sample code, we return the data from the Mobile Apps SQL Azure table ToDoItem into an array. Essa atividade é um padrão comum para aplicativos de dados.This activity is a common pattern for data applications. As consultas de banco de dados geralmente retornam uma coleção de linhas que o cliente obtém em uma lista ou matriz.Database queries often return a collection of rows that the client gets in a list or array. Neste exemplo, a matriz é a fonte de dados.In this sample, the array is the data source. O código especifica um layout de tela que define a exibição dos dados que são exibidos no dispositivo.The code specifies a screen layout that defines the view of the data that appears on the device. E os dois são associados juntos com um adaptador, que, nesse código, é uma extensão da classe ArrayAdapter<ToDoItem> .The two are bound together with an adapter, which in this code is an extension of the ArrayAdapter<ToDoItem> class.

Definir o layoutDefine the Layout

O layout é definido por vários snippets de código XML.The layout is defined by several snippets of XML code. Com base em um layout existente, o código a seguir representa a ListView que queremos preencher com nossos dados de servidor.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>

No código anterior, o atributo listitem especifica a id do layout para uma linha individual na lista.In the preceding code, the listitem attribute specifies the id of the layout for an individual row in the list. Esse código especifica uma caixa de seleção e seu texto associado e é instanciado uma vez para cada item da lista.This code specifies a check box and its associated text and gets instantiated once for each item in the list. Esse layout não exibe o campo ID , e um layout mais complexo especificaria campos adicionais na exibição.This layout does not display the id field, and a more complex layout would specify additional fields in the display. Este código está no arquivo 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>

Definir o adaptadorDefine the adapter

Como a fonte de dados da nossa exibição é uma matriz deToDoItem, podemos criar a subclasse do nosso adaptador por meio de uma classe ArrayAdapter<ToDoItem> .Since the data source of our view is an array of ToDoItem, we subclass our adapter from an ArrayAdapter<ToDoItem> class. Esta subclasse produz uma exibição para cada ToDoItem usando o layout row_list_to_do.This subclass produces a View for every ToDoItem using the row_list_to_do layout. Em nosso código, definimos a seguinte classe que é uma extensão da 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> {
}

Substitua o método getView dos adaptadores.Override the adapters getView method. Por exemplo: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;
    }

Criamos uma instância dessa classe em nossa atividade da seguinte maneira:We create an instance of this class in our Activity as follows:

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

O segundo parâmetro para o construtor ToDoItemAdapter é uma referência ao layout.The second parameter to the ToDoItemAdapter constructor is a reference to the layout. Agora podemos criar uma instância do ListView e atribuir o adaptador ao ListView.We can now instantiate the ListView and assign the adapter to the ListView.

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

Usar o adaptador para associar à interface do usuárioUse the Adapter to Bind to the UI

Agora você está pronto para usar a associação de dados.You are now ready to use data binding. O código a seguir mostra como obter itens na tabela e preenche o adaptador local com os itens retornados.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);
    }

Chame o adaptador sempre que modificar a tabela ToDoItem .Call the adapter any time you modify the ToDoItem table. Como as modificações são feitas de registro em registro, você trata de uma única linha em vez de uma coleção.Since modifications are done on a record by record basis, you handle a single row instead of a collection. Ao inserir um item, chame o método add no adaptador e, ao excluir, chame o método remove.When you insert an item, call the add method on the adapter; when deleting, call the remove method.

Você pode encontrar um exemplo completo no projeto de início rápido do Android.You can find a complete example in the Android Quickstart Project.

Inserir dados no back-endInsert data into the backend

Crie uma instância da classe ToDoItem e defina suas propriedades.Instantiate an instance of the ToDoItem class and set its properties.

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

Em seguida, use insert () para inserir um objeto:Then use insert() to insert an object:

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

A entidade retornada corresponde aos dados inseridos na tabela de back-end, incluindo a ID e quaisquer outros valores (como os campos createdAt, updatedAt e version) definidos no 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.

As tabelas de aplicativos móveis exigem uma coluna de chave primária denominada ID. Essa coluna deve ser uma cadeia de caracteres.Mobile Apps tables require a primary key column named id. This column must be a string. O valor padrão da coluna ID é um GUID.The default value of the ID column is a GUID. Você pode fornecer outros valores exclusivos, como endereços de email ou nomes de User.You can provide other unique values, such as email addresses or usernames. Quando um valor de ID de cadeia de caracteres não é fornecido para um registro inserido, o back-end gera um novo GUID.When a string ID value is not provided for an inserted record, the backend generates a new GUID.

Os valores de ID de cadeia de caracteres fornecem as seguintes vantagens:String ID values provide the following advantages:

  • As IDs podem ser geradas sem fazer uma viagem de ida e volta ao banco de dados.IDs can be generated without making a round trip to the database.
  • Os registros são mais fáceis de mesclar a partir de tabelas ou bancos de dados diferentes.Records are easier to merge from different tables or databases.
  • Os valores de ID se integram melhor com a lógica de um aplicativo.ID values integrate better with an application's logic.

Valores de ID de cadeia de caracteres são OBRIGATÓRIOS para suporte de sincronização offline.String ID values are REQUIRED for offline sync support. Você não pode alterar uma ID depois que ela é armazenada no banco de dados de back-end.You cannot change an Id once it is stored in the backend database.

Atualizar dados em um aplicativo móvelUpdate data in a mobile app

Para atualizar dados em uma tabela, passe o novo objeto para o método update () .To update data in a table, pass the new object to the update() method.

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

Neste exemplo, item é uma referência a uma linha na tabela ToDoItem, a qual sofreu algumas alterações.In this example, item is a reference to a row in the ToDoItem table, which has had some changes made to it. A linha com a mesma id é atualizada.The row with the same id is updated.

Excluir dados em um aplicativo móvelDelete data in a mobile app

O código a seguir mostra como excluir dados de uma tabela especificando o objeto de dados.The following code shows how to delete data from a table by specifying the data object.

mToDoTable
    .delete(item);

Você também pode excluir um item especificando a id da linha a ser excluída.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);

Pesquisar um item específico por IDLook up a specific item by Id

Pesquise um item com determinado campo id com o método lookUp() :Look up an item with a specific id field with the lookUp() method:

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

Como trabalhar com dados não tipadosHow to: Work with untyped data

O modelo de programação não tipado oferece um controle exato sobre a serialização JSON.The untyped programming model gives you exact control over JSON serialization. Há alguns cenários comuns em que você talvez queira usar um modelo de programação não tipado.There are some common scenarios where you may wish to use an untyped programming model. Por exemplo, se sua tabela de back-end contém muitas colunas e você só precisa fazer referência a um subconjunto das colunas.For example, if your backend table contains many columns and you only need to reference a subset of the columns. O modelo tipado exige que você defina todas as colunas definidas no back-end de aplicativos móveis em sua classe de dados.The typed model requires you to define all the columns defined in the Mobile Apps backend in your data class. A maioria das chamadas à API para acessar dados é semelhante às chamadas de programação tipadas.Most of the API calls for accessing data are similar to the typed programming calls. A principal diferença é que o modelo não tipado você invocar métodos no objeto MobileServiceJsonTable, em vez do objeto MobileServiceTable.The main difference is that in the untyped model you invoke methods on the MobileServiceJsonTable object, instead of the MobileServiceTable object.

Criar uma instância de uma tabela não tipadaCreate an instance of an untyped table

Semelhante ao modelo tipado, você começa obtendo uma referência de tabela, mas, nesse caso, é um objeto MobileServicesJsonTable .Similar to the typed model, you start by getting a table reference, but in this case it's a MobileServicesJsonTable object. Obtenha a referência ao chamar o método getTable em uma instância do cliente:Obtain the reference by calling the getTable method on an instance of the client:

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

Depois de criar uma instância do MobileServiceJsonTable, ela tem praticamente a mesma API disponível que o modelo de programação tipado.Once you have created an instance of the MobileServiceJsonTable, it has virtually the same API available as with the typed programming model. Em alguns casos, os métodos usam um parâmetro não tipado em vez de um parâmetro com tipo.In some cases, the methods take an untyped parameter instead of a typed parameter.

Inserir em uma tabela não tipadaInsert into an untyped table

O código a seguir mostra como fazer uma inserção.The following code shows how to do an insert. A primeira etapa é criar um jsonobject, que faz parte da biblioteca 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);

Em seguida, use insert() para inserir o objeto não tipado na tabela.Then, Use insert() to insert the untyped object into the table.

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

Se você precisa obter a ID do objeto inserido, use o método getAsJsonPrimitive() .If you need to get the ID of the inserted object, use the getAsJsonPrimitive() method.

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

Excluir de uma tabela não tipadaDelete from an untyped table

O código a seguir mostra como excluir uma instância, neste caso, a mesma instância de um JsonObject criado no exemplo insert anterior.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. O código é igual ao do caso tipado, mas o método tem uma assinatura diferente, uma vez que ele faz referência a um 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);

Você também pode excluir uma instância diretamente usando sua ID:You can also delete an instance directly by using its ID:

mToDoTable.delete(ID);

Retornar todas as linhas de uma tabela não tipadaReturn all rows from an untyped table

O código a seguir mostra como recuperar uma tabela inteira.The following code shows how to retrieve an entire table. Como você está usando uma tabela JSON, você pode recuperar seletivamente apenas algumas das colunas da tabela.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();
}

O mesmo conjunto de métodos de filtragem, filtragem e paginação que estão disponíveis para o modelo digitado estão disponíveis para o modelo não tipado.The same set of filtering, filtering and paging methods that are available for the typed model are available for the untyped model.

Implementar sincronização offlineImplement Offline Sync

O SDK do cliente dos aplicativos móveis do Azure também implementa a sincronização offline de dados usando um banco de dados SQLite para armazenar uma cópia dos dados do servidor localmente.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. As operações executadas em uma tabela offline não exigem que a conectividade móvel funcione.Operations performed on an offline table do not require mobile connectivity to work. A sincronização offline auxilia na resiliência e no desempenho às custas de lógica mais complexa para a resolução de conflitos.Offline sync aids in resilience and performance at the expense of more complex logic for conflict resolution. O SDK do cliente dos aplicativos móveis do Azure implementa os seguintes recursos:The Azure Mobile Apps Client SDK implements the following features:

  • Sincronização incremental: somente os registros atualizados e novos são baixados, economizando a largura de banda e o consumo de memória.Incremental Sync: Only updated and new records are downloaded, saving bandwidth and memory consumption.
  • Simultaneidade otimista: presume-se que as operações tenham sucesso.Optimistic Concurrency: Operations are assumed to succeed. A resolução de conflitos é adiada até que as atualizações sejam executadas no servidor.Conflict Resolution is deferred until updates are performed on the server.
  • Resolução de conflitos: o SDK detecta quando uma alteração conflitante foi feita no servidor e fornece ganchos para alertar o usuário.Conflict Resolution: The SDK detects when a conflicting change has been made at the server and provides hooks to alert the user.
  • Exclusão reversível: registros excluídos são marcados como excluídos, permitindo que outros dispositivos atualizem seu cache offline.Soft Delete: Deleted records are marked deleted, allowing other devices to update their offline cache.

Inicializar sincronização offlineInitialize Offline Sync

Cada tabela offline deve ser definida no cache offline antes do uso.Each offline table must be defined in the offline cache before use. Normalmente, a definição de tabela é feita imediatamente após a criação do cliente: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);
}

Obter uma referência à tabela de cache offlineObtain a reference to the Offline Cache Table

Para uma tabela online, você usa .getTable().For an online table, you use .getTable(). Para uma tabela offline, use .getSyncTable():For an offline table, use .getSyncTable():

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

Todos os métodos que estão disponíveis para tabelas online (incluindo filtragem, classificação, paginação, inserção de dados, atualização de dados e exclusão de dados) funcionam igualmente bem em tabelas online e 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.

Sincronizar o cache offline localSynchronize the Local Offline Cache

A sincronização está dentro do controle do seu aplicativo.Synchronization is within the control of your app. Veja um exemplo de método de sincronização: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 um nome de consulta for fornecido ao método .pull(query, queryname), a sincronização incremental será usada para retornar somente os registros que foram criados ou alterados desde o último pull concluído com êxito.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.

Tratar conflitos durante a sincronização offlineHandle Conflicts during Offline Synchronization

Se ocorrer um conflito durante uma operação de .push(), um MobileServiceConflictException será gerado.If a conflict happens during a .push() operation, a MobileServiceConflictException is thrown. O item emitido pelo servidor é inserido na exceção e pode ser recuperado por .getItem() na exceção.The server-issued item is embedded in the exception and can be retrieved by .getItem() on the exception. Ajuste o envio por push chamando os seguintes itens no objeto MobileServiceSyncContext:Adjust the push by calling the following items on the MobileServiceSyncContext object:

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

Depois que todos os conflitos forem marcados como desejar, chame .push() novamente para resolver todos os conflitos.Once all conflicts are marked as you wish, call .push() again to resolve all the conflicts.

Chamar uma API personalizadaCall a custom API

Uma API personalizada permite que você defina pontos de extremidade personalizados que expõem a funcionalidade do servidor que não é mapeada para uma operação de inserção, atualização, exclusão ou leitura.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. Usando uma API personalizada, você pode ter mais controle sobre mensagens, incluindo leitura e configuração de cabeçalhos de mensagens HTTP e definição de um formato de corpo de mensagem diferente de 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.

Em um cliente Android, você chama o método invokeApi para chamar o ponto de extremidade da API personalizada.From an Android client, you call the invokeApi method to call the custom API endpoint. O exemplo a seguir mostra como chamar um ponto de extremidade de API denominado completeAll, que retorna uma classe de coleção chamada 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();
        }
    });
}

O método invokeApi é chamado no cliente, que envia uma solicitação POST para a nova API personalizada.The invokeApi method is called on the client, which sends a POST request to the new custom API. O resultado retornado pela API personalizada é exibido em uma caixa de diálogo de mensagem, assim como erros.The result returned by the custom API is displayed in a message dialog, as are any errors. Outras versões de invokeApi permitem que você, opcionalmente, envie um objeto no corpo da solicitação, especifique o método HTTP e envie parâmetros de consulta com a solicitação.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. Versões não tipadas de invokeApi também são fornecidas.Untyped versions of invokeApi are provided as well.

Adicionar autenticação ao seu aplicativoAdd authentication to your app

Os tutoriais já descrevem em detalhes como adicionar esses recursos.Tutorials already describe in detail how to add these features.

O serviço de aplicativo dá suporte à autenticação de usuários de aplicativos usando vários provedores de identidade externos: Facebook, Google, conta da 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. Você pode definir permissões em tabelas para restringir o acesso para operações específicas somente a usuários autenticados.You can set permissions on tables to restrict access for specific operations to only authenticated users. Você também pode usar a identidade de usuários autenticados para implementar regras de autorização no seu back-end.You can also use the identity of authenticated users to implement authorization rules in your backend.

Dois fluxos de autenticação são suportados: um server flow e um client flow.Two authentication flows are supported: a server flow and a client flow. O fluxo do servidor fornece a experiência de autenticação mais simples, pois depende da interface da Web dos provedores de identidade.The server flow provides the simplest authentication experience, as it relies on the identity providers web interface. Nenhum SDKs adicional é necessário para implementar a autenticação de fluxo de servidor.No additional SDKs are required to implement server flow authentication. A autenticação de fluxo de servidor não fornece uma integração profunda ao dispositivo móvel e é recomendada somente para cenários de prova de conceito.Server flow authentication does not provide a deep integration into the mobile device and is only recommended for proof of concept scenarios.

O fluxo de cliente permite uma integração mais profunda com recursos específicos do dispositivo, como o logon único, pois ele depende de SDKs fornecidos pelo provedor de identidade.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. Por exemplo, você pode integrar o SDK do Facebook em seu aplicativo móvel.For example, you can integrate the Facebook SDK into your mobile application. O cliente móvel alterna para o aplicativo do Facebook e confirma seu logon antes de alternar de volta para seu aplicativo móvel.The mobile client swaps into the Facebook app and confirms your sign-on before swapping back to your mobile app.

Quatro etapas são necessárias para habilitar a autenticação em seu aplicativo:Four steps are required to enable authentication in your app:

  • Registre seu aplicativo para autenticação com um provedor de identidade.Register your app for authentication with an identity provider.
  • Configure o back-end do serviço de aplicativo.Configure your App Service backend.
  • Restrinja as permissões de tabela a usuários autenticados somente no back-end do serviço de aplicativo.Restrict table permissions to authenticated users only on the App Service backend.
  • Adicione o código de autenticação ao seu aplicativo.Add authentication code to your app.

Você pode definir permissões em tabelas para restringir o acesso para operações específicas somente a usuários autenticados.You can set permissions on tables to restrict access for specific operations to only authenticated users. Você também pode usar o SID de um usuário autenticado para modificar solicitações.You can also use the SID of an authenticated user to modify requests. Para saber mais, confira Comece a usar a autenticação e a documentação TUTORIAL do SDK do Servidor.For more information, review Get started with authentication and the Server SDK HOWTO documentation.

Autenticação: fluxo de servidorAuthentication: Server Flow

O código a seguir inicia um processo de logon de fluxo do servidor usando o provedor do Google.The following code starts a server flow login process using the Google provider. A configuração adicional é necessária devido aos requisitos de segurança para o provedor do 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);

Além disso, adicione o seguinte método à classe de atividade principal: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");
            }
        }
    }
}

O GOOGLE_LOGIN_REQUEST_CODE definido em sua atividade principal é usado para o método login() e dentro do método onActivityResult().The GOOGLE_LOGIN_REQUEST_CODE defined in your main Activity is used for the login() method and within the onActivityResult() method. Você pode escolher qualquer número exclusivo, desde que o mesmo número seja usado no método login() e no método onActivityResult().You can choose any unique number, as long as the same number is used within the login() method and the onActivityResult() method. Se você abstrair o código do cliente em um adaptador de serviço (como mostrado anteriormente), deverá chamar os métodos apropriados no adaptador de serviço.If you abstract the client code into a service adapter (as shown earlier), you should call the appropriate methods on the service adapter.

Você também precisa configurar o projeto para customtabs.You also need to configure the project for customtabs. Primeiro, especifique uma URL de redirecionamento.First specify a redirect-URL. Adicione o seguinte trecho ao 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>

Adicione o redirectUriScheme ao arquivo de build.gradle para seu aplicativo: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']
        }
    }
}

Por fim, adicione com.android.support:customtabs:28.0.0 à lista de dependências no arquivo 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'
}

Obtenha a ID do usuário conectado de um MobileServiceUser usando o método getUserId.Obtain the ID of the logged-in user from a MobileServiceUser using the getUserId method. Para ver um exemplo de como usar Futures para chamar as APIs de logon assíncronas, veja Comece a usar a autenticação.For an example of how to use Futures to call the asynchronous login APIs, see Get started with authentication.

Aviso

O esquema de URL mencionado diferencia maiúsculas de minúsculas.The URL Scheme mentioned is case-sensitive. Verifique se todas as ocorrências de {url_scheme_of_you_app} ocorrência de correspondência.Ensure that all occurrences of {url_scheme_of_you_app} match case.

Tokens de autenticação de cacheCache authentication tokens

Os tokens de autenticação de cache exigem que você armazene a ID de usuário e o token de autenticação localmente no dispositivo.Caching authentication tokens requires you to store the User ID and authentication token locally on the device. Na próxima vez que o aplicativo for iniciado, você verificará o cache e, se esses valores estiverem presentes, você poderá ignorar o procedimento de logon e reidratar o cliente com esses dados.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. No entanto, esses dados são confidenciais e, para segurança, devem ser armazenados criptografados caso o telefone seja roubado.However this data is sensitive, and it should be stored encrypted for safety in case the phone gets stolen. Você pode ver um exemplo completo de como armazenar em cache tokens de autenticação na seção de tokens de autenticação de cache.You can see a complete example of how to cache authentication tokens in Cache authentication tokens section.

Ao tentar usar um token expirado, você receberá uma resposta 401 não autorizado .When you try to use an expired token, you receive a 401 unauthorized response. Você pode manipular erros de autenticação usando filtros.You can handle authentication errors using filters. Os filtros interceptam solicitações para o back-end do serviço de aplicativo.Filters intercept requests to the App Service backend. O código de filtro testa a resposta de um 401, dispara o processo de entrada e, em seguida, retoma a solicitação que gerou a 401.The filter code tests the response for a 401, triggers the sign-in process, and then resumes the request that generated the 401.

Usar tokens de atualizaçãoUse Refresh Tokens

O token retornado por Azure App autenticação e autorização do serviço tem um tempo de vida definido de uma hora.The token returned by Azure App Service Authentication and Authorization has a defined life time of one hour. Após esse período, você deve autenticar novamente o usuário.After this period, you must reauthenticate the user. Se você estiver usando um token de vida útil longa recebido por meio da autenticação de fluxo de cliente, poderá autenticar novamente com Azure App autenticação de serviço e autorização usando o mesmo 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. Outro token de serviço Azure App é gerado com um novo tempo de vida.Another Azure App Service token is generated with a new lifetime.

Você também pode registrar o provedor para usar tokens de atualização.You can also register the provider to use Refresh Tokens. Um token de atualização nem sempre está disponível.A Refresh Token is not always available. É necessária uma configuração adicional:Additional configuration is required:

  • Para Azure Active Directory, configure um segredo do cliente para o aplicativo Azure Active Directory.For Azure Active Directory, configure a client secret for the Azure Active Directory App. Especifique o segredo do cliente no serviço de Azure App ao configurar Azure Active Directory autenticação.Specify the client secret in the Azure App Service when configuring Azure Active Directory Authentication. Ao chamar .login(), passe response_type=code id_token como um parâmetro: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);
    
  • Para o Google, passe o access_type=offline como um parâmetro: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);
    
  • Para conta da Microsoft, selecione o escopo de wl.offline_access.For Microsoft Account, select the wl.offline_access scope.

Para atualizar um token, chame .refreshUser():To refresh a token, call .refreshUser():

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

Como prática recomendada, crie um filtro que detecte uma resposta 401 do servidor e tente atualizar o token do usuário.As a best practice, create a filter that detects a 401 response from the server and tries to refresh the user token.

Fazer logon com a autenticação de fluxo de clienteLog in with Client-flow Authentication

O processo geral para fazer logon com a autenticação de fluxo de cliente é o seguinte:The general process for logging in with client-flow authentication is as follows:

  • Configure Azure App autenticação e autorização do serviço como faria com a autenticação do servidor.Configure Azure App Service Authentication and Authorization as you would server-flow authentication.

  • Integre o SDK do provedor de autenticação para autenticação para produzir um token de acesso.Integrate the authentication provider SDK for authentication to produce an access token.

  • Chame o método .login() da seguinte maneira (result deve ser um 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");
        }
    });
    

Consulte o exemplo de código completo na próxima seção.See the complete code example in the next section.

Substitua o método onSuccess() com qualquer código que você queira usar em um logon bem-sucedido.Replace the onSuccess() method with whatever code you wish to use on a successful login. O {provider} cadeia de caracteres é um provedor válido: AAD (Azure Active Directory), Facebook, Google, MicrosoftAccountou Twitter.The {provider} string is a valid provider: aad (Azure Active Directory), facebook, google, microsoftaccount, or twitter. Se você tiver implementado a autenticação personalizada, também poderá usar a marca do provedor de autenticação personalizada.If you have implemented custom authentication, then you can also use the custom authentication provider tag.

Autenticar usuários com o Biblioteca de Autenticação do Active Directory (ADAL)Authenticate users with the Active Directory Authentication Library (ADAL)

Você pode usar o Biblioteca de Autenticação do Active Directory (ADAL) para conectar usuários ao seu aplicativo usando Azure Active Directory.You can use the Active Directory Authentication Library (ADAL) to sign users into your application using Azure Active Directory. Normalmente, é melhor usar um logon de fluxo de cliente em vez dos métodos loginAsync() , pois ele fornece uma aparência mais nativa de UX e permite uma maior personalização.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. Configure o seu back-end de aplicativo móvel para entrada no AAD seguindo o tutorial Como configurar o Serviço de Aplicativo para logon no Active Directory .Configure your mobile app backend for AAD sign-in by following the How to configure App Service for Active Directory login tutorial. Certifique-se de concluir a etapa opcional de registrar um aplicativo cliente nativo.Make sure to complete the optional step of registering a native client application.

  2. Instale o ADAL modificando o arquivo Build. gradle para incluir as seguintes definições: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. Adicione o código a seguir ao seu aplicativo, fazendo as seguintes substituições:Add the following code to your application, making the following replacements:

    • Substitua INSERT-AUTHORITY-HERE pelo nome do locatário no qual o aplicativo foi provisionado.Replace INSERT-AUTHORITY-HERE with the name of the tenant in which you provisioned your application. O formato deve ser https://login.microsoftonline.com/contoso.onmicrosoft.com.The format should be https://login.microsoftonline.com/contoso.onmicrosoft.com.
    • Substitua Insert-Resource-ID-aqui pela ID do cliente para o back-end do aplicativo móvel.Replace INSERT-RESOURCE-ID-HERE with the client ID for your mobile app backend. Você pode obter a ID do cliente na guia avançado em configurações de Azure Active Directory no Portal.You can obtain the client ID from the Advanced tab under Azure Active Directory Settings in the portal.
    • Substitua INSERT-CLIENT-ID-HERE pela ID do cliente copiada do aplicativo cliente nativo.Replace INSERT-CLIENT-ID-HERE with the client ID you copied from the native client application.
    • Substitua INSERT-REDIRECT-URI-HERE pelo ponto de extremidade /.auth/login/done do site, usando o esquema HTTPS.Replace INSERT-REDIRECT-URI-HERE with your site's /.auth/login/done endpoint, using the HTTPS scheme. Esse valor deve ser semelhante 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);
    }
}

Ajustar a comunicação cliente-servidorAdjust the Client-Server Communication

A conexão do cliente normalmente é uma conexão HTTP básica usando a biblioteca HTTP subjacente fornecida com o SDK do Android.The Client connection is normally a basic HTTP connection using the underlying HTTP library supplied with the Android SDK. Há várias razões pelas quais você desejaria alterar isso:There are several reasons why you would want to change that:

  • Você deseja usar uma biblioteca HTTP alternativa para ajustar os tempos limite.You wish to use an alternate HTTP library to adjust timeouts.
  • Você deseja fornecer uma barra de progresso.You wish to provide a progress bar.
  • Você deseja adicionar um cabeçalho personalizado para dar suporte à funcionalidade de gerenciamento de API.You wish to add a custom header to support API management functionality.
  • Você deseja interceptar uma resposta com falha para que possa implementar a reautenticação.You wish to intercept a failed response so that you can implement reauthentication.
  • Você deseja registrar em log as solicitações de back-end para um serviço de análise.You wish to log backend requests to an analytics service.

Usando uma biblioteca HTTP alternativaUsing an alternate HTTP Library

Chame o método .setAndroidHttpClientFactory() imediatamente após criar a referência do cliente.Call the .setAndroidHttpClientFactory() method immediately after creating your client reference. Por exemplo, para definir o tempo limite da conexão como 60 segundos (em vez do padrão de 10 segundos):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;
    }
});

Implementar um filtro de progressoImplement a Progress Filter

Você pode implementar uma interceptação de cada solicitação implementando uma ServiceFilter.You can implement an intercept of every request by implementing a ServiceFilter. Por exemplo, o seguinte atualiza uma barra de progresso criada previamente: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;
    }
}

Você pode anexar esse filtro ao cliente da seguinte maneira:You can attach this filter to the client as follows:

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

Personalizar cabeçalhos de solicitaçãoCustomize Request Headers

Use os ServiceFilter a seguir e anexe o filtro da mesma maneira que o 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);
        }
    }
}

Configurar a serialização automáticaConfigure Automatic Serialization

Você pode especificar uma estratégia de conversão que se aplica a todas as colunas usando a API Gson .You can specify a conversion strategy that applies to every column by using the gson API. A biblioteca de cliente Android usa Gson nos bastidores para serializar objetos Java para dados JSON antes que os dados sejam enviados para Azure app serviço.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. O código a seguir usa o método setFieldNamingStrategy () para definir a estratégia.The following code uses the setFieldNamingStrategy() method to set the strategy. Este exemplo excluirá o caractere inicial (um "m") e, em seguida, o caractere seguinte, para cada nome de campo.This example will delete the initial character (an "m"), and then lower-case the next character, for every field name. Por exemplo, ele transforma "mId" em "ID".For example, it would turn "mId" into "id." Implemente uma estratégia de conversão para reduzir a necessidade de SerializedName() anotações na maioria dos campos.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)
);

Esse código deve ser executado antes de criar uma referência de cliente móvel usando o MobileServiceClient.This code must be executed before creating a mobile client reference using the MobileServiceClient.