Como utilizar o Azure Mobile Apps SDK para Android

Este guia mostra-lhe como usar o cliente Android SDK para aplicações móveis para implementar cenários comuns, tais como:

  • Consulta de dados (inserção, atualização e eliminação).
  • Autenticação.
  • Erros de manipulação.
  • Personalizar o cliente.

Este guia foca-se no Android SDK do lado do cliente. Para saber mais sobre os SDKs do lado do servidor para aplicações móveis, consulte Work with .NET backend SDK ou Como utilizar o backend SDK Node.js.

Documentação de Referência

Pode encontrar a referência API javadocs para a biblioteca de clientes Android no GitHub.

Plataformas Suportadas

O Azure Mobile Apps SDK para Android suporta os níveis de API 19 a 24 (KitKat através de Nougat) para fatores de forma de telefone e tablet. A autenticação, em particular, utiliza uma abordagem comum da web framework para recolher credenciais. A autenticação do fluxo do servidor não funciona com pequenos dispositivos de fator de forma, como relógios.

Configuração e Pré-Requisitos

Complete o tutorial de arranque rápido de Aplicações Móveis . Esta tarefa garante que todos os pré-requisitos para o desenvolvimento de Aplicações Móveis Azure foram cumpridos. O Quickstart também o ajuda a configurar a sua conta e a criar o seu primeiro backend mobile.

Se decidir não completar o tutorial quickstart, complete as seguintes tarefas:

Atualizar o ficheiro de construção Gradle

Alterar ambos os ficheiros build.gradle :

  1. Adicione este código ao Project ficheiro level build.gradle:

    buildscript {
        repositories {
            jcenter()
            google()
        }
    }
    
    allprojects {
        repositories {
            jcenter()
            google()
        }
    }
    
  2. Adicione este código ao nível de app do Módulobuild.gradle file dentro da etiqueta dependências :

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

    Atualmente a versão mais recente é 3.4.0. As versões suportadas estão listadas na bintray.

Ativar a permissão para a Internet

Para aceder ao Azure, a sua aplicação tem de ter a permissão internet ativada. Se ainda não estiver ativado, adicione a seguinte linha de código ao seu ficheiro AndroidManifest.xml :

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

Criar uma ligação ao cliente

A Azure Mobile Apps fornece quatro funções para a sua aplicação móvel:

  • Acesso a dados e sincronização offline com um Serviço de Aplicações Móveis Azure.
  • Ligue para APIs personalizados escritos com o Azure Mobile Apps Server SDK.
  • Autenticação com Serviço de Aplicações do Azure Autenticação e Autorização.
  • Push Notification Registration with Notification Hubs.

Cada uma destas funções requer primeiro que crie um MobileServiceClient objeto. Apenas um MobileServiceClient objeto deve ser criado dentro do seu cliente móvel (isto é, deve ser um padrão Singleton). Para criar um MobileServiceClient objeto:

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

Ou <MobileAppUrl> é uma corda ou um objeto URL que aponta para o seu backend móvel. Se estiver a utilizar Serviço de Aplicações do Azure para hospedar o seu backend móvel, certifique-se de que utiliza a versão segura https:// do URL.

O cliente também requer acesso à Atividade ou Contexto - o this parâmetro no exemplo. A construção mobileServiceClient deve ocorrer dentro do onCreate() método da Atividade referenciada no AndroidManifest.xml ficheiro.

Como uma boa prática, você deve abstrair a comunicação do servidor na sua própria classe (singleton-pattern). Neste caso, deverá passar a Atividade dentro do construtor para configurar adequadamente o serviço. Por exemplo:

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 pode chamar AzureServiceAdapter.Initialize(this); o onCreate() método da sua atividade principal. Quaisquer outros métodos que necessitem de acesso ao uso AzureServiceAdapter.getInstance(); do cliente para obter uma referência ao adaptador de serviço.

Operações de Dados

O núcleo do Azure Mobile Apps SDK é fornecer acesso aos dados armazenados dentro de SQL Azure no backend da Aplicação Móvel. Pode aceder a estes dados utilizando aulas fortemente dactilografados (preferencialmente) ou consultas não estaloguantes (não recomendadas). A maior parte desta secção trata da utilização de classes fortemente dactilografada.

Definir classes de dados de clientes

Para aceder aos dados a partir de tabelas SQL Azure, defina as classes de dados do cliente que correspondem às tabelas no backend da Aplicação Móvel. Exemplos neste tópico assumem uma tabela chamada MyDataTable, que tem as seguintes colunas:

  • ID
  • texto
  • completar

O objeto do lado do cliente dactilografado correspondente reside num ficheiro chamado MyDataTable.java:

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

Adicione métodos de getter e setter para cada campo que adicionar. Se a sua tabela SQL Azure contiver mais colunas, adicionará os campos correspondentes a esta classe. Por exemplo, se o DTO (objeto de transferência de dados) tivesse uma coluna de prioridades, então poderia adicionar este campo, juntamente com os seus métodos de getter e setter:

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 aprender a criar tabelas adicionais no backend de Aplicações Móveis, consulte Como: Definir um controlador de mesa (.BACKend) ou Definir Tabelas utilizando um Esquema Dinâmico (Node.js backend).

Uma tabela de backend Azure Mobile Apps define cinco campos especiais, quatro dos quais estão disponíveis para os clientes:

  • String id: A identificação globalmente única para o disco. Como uma boa prática, faça do id a representação de cordas de um objeto UUID .
  • DateTimeOffset updatedAt: A data/hora da última atualização. O campo 'A' Atualizado é definido pelo servidor e nunca deve ser definido pelo código do cliente.
  • DateTimeOffset createdAt: A data/hora em que o objeto foi criado. O campo CriadoAt é definido pelo servidor e nunca deve ser definido pelo código do seu cliente.
  • byte[] version: Normalmente representada como uma corda, a versão também é definida pelo servidor.
  • boolean deleted: Indica que o registo foi apagado, mas ainda não foi purgado. Não utilize deleted como propriedade na sua classe.

O campo id é obrigatório. O updatedAt campo e version o campo são utilizados para sincronização offline (para sincronização incremental e resolução de conflitos, respectivamente). O createdAt campo é um campo de referência e não é utilizado pelo cliente. Os nomes são nomes "across-the-wire" das propriedades e não são ajustáveis. No entanto, pode criar um mapeamento entre o seu objeto e os nomes "across-the-wire" utilizando a biblioteca gson . Por exemplo:

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 tabela

Para aceder a uma tabela, primeiro crie um objeto MobileServiceTable chamando o método getTable no MobileServiceClient. Este método tem duas sobrecargas:

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

No seguinte código, o mClient é uma referência ao seu objeto MobileServiceClient. A primeira sobrecarga é usada onde o nome da classe e o nome da mesa são os mesmos, e é o usado no Quickstart:

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

A segunda sobrecarga é usada quando o nome da mesa é diferente do nome da classe: o primeiro parâmetro é o nome da tabela.

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

Consulta uma tabela de backend

Primeiro, obtenha uma referência de mesa. Em seguida, execute uma consulta na referência da tabela. Uma consulta é qualquer combinação de:

As cláusulas devem ser apresentadas na ordem anterior.

Filtrar os Resultados

A forma geral de uma consulta é:

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

O exemplo anterior devolve todos os resultados (até ao tamanho máximo da página definido pelo servidor). O .execute() método executa a consulta no backend. A consulta é convertida para uma consulta OData v3 antes da transmissão para o backend de Aplicações Móveis. No recebimento, o backend mobile Apps converte a consulta numa declaração SQL antes de executá-la no SQL Azure caso. Uma vez que a atividade da rede leva algum tempo, o .execute() método devolve a ListenableFuture<E>.

Filtrar dados devolvidos

A seguinte execução de consulta devolve todos os itens da tabela ToDoItem onde completamente é falso.

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

mToDoTable é a referência à tabela de serviços móveis que criamos anteriormente.

Defina um filtro utilizando o método de chamada na referência da tabela. O método em que o método é seguido por um método que especifica o predicado lógico. Os métodos predicados possíveis incluem eq (iguais), ne (não igual), gt (maior do que), ge (maior ou igual a), lt (menos que), le (menos ou igual a). Estes métodos permitem comparar os campos de número e de cordas com valores específicos.

Pode filtrar as datas. Os seguintes métodos permitem comparar todo o campo de data ou partes da data: ano, mês, dia, hora, minuto e segundo. O exemplo a seguir adiciona um filtro para itens cuja data de vencimento é igual a de 2013.

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

Os seguintes métodos suportam filtros complexos em campos de cordas: começaCom, extremidadesCom, concat, subString, indexOf, substitua, toLower, toUpper, trim e comprimento. O exemplo seguinte filtra para linhas de mesa onde a coluna de texto começa com "PRI0".

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

Os seguintes métodos de operador são suportados nos campos de números: adicionar, sub, mul, div, mod, chão, teto e redondo. Os seguintes filtros de exemplo para linhas de mesa onde a duração é um número par.

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

Pode combinar predicados com estes métodos lógicos: e, ounão. O exemplo a seguir combina dois dos exemplos anteriores.

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

Operadores lógicos do grupo e dos ninhos:

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

Para uma discussão mais detalhada e exemplos de filtragem, consulte Exploração da riqueza do modelo de consulta ao cliente Android.

Ordenar dados devolvidos

O código seguinte devolve todos os itens de uma tabela de ToDoItems classificados ascendentes pelo campo de texto . mToDoTable é a referência à tabela de backend que criou anteriormente:

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

O primeiro parâmetro do método orderBy é uma corda igual ao nome do campo em que se classificar. O segundo parâmetro utiliza a enumeração QueryOrder para especificar se deve ordenar ascendente ou descendente. Se estiver a filtrar utilizando o método onde , o método onde deve ser invocado antes do método orderBy .

Selecione colunas específicas

O código que se segue ilustra como devolver todos os itens de uma tabela de ToDoItems, mas apenas exibe os campos completos e de texto . mToDoTable é a referência à tabela de backend que criamos anteriormente.

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

Os parâmetros da função selecionada são os nomes de cadeia das colunas da tabela que pretende retornar. O método selecionado tem de seguir métodos como onde e encomendar. Pode ser seguido por métodos de paging como saltar e topo.

Devolução de dados em páginas

Os dados são sempre devolvidos em páginas. O número máximo de registos devolvidos é definido pelo servidor. Se o cliente solicitar mais registos, o servidor devolve o número máximo de registos. Por predefinição, o tamanho máximo da página no servidor é de 50 registos.

O primeiro exemplo mostra como selecionar os cinco itens de uma mesa. A consulta devolve os itens de uma tabela de ToDoItems. mToDoTable é a referência à tabela de backend que criou anteriormente:

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

Aqui está uma consulta que salta os primeiros cinco itens, e depois devolve os próximos cinco:

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

Se desejar obter todos os registos numa tabela, implemente o código para iterar em todas as páginas:

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

Um pedido para todos os registos usando este método cria um mínimo de dois pedidos para o backend de Aplicações Móveis.

Dica

Escolher o tamanho certo da página é um equilíbrio entre o uso da memória enquanto o pedido está a acontecer, o uso da largura de banda e o atraso na receção completa dos dados. O predefinido (50 registos) é adequado para todos os dispositivos. Se operar exclusivamente em dispositivos de memória maiores, aumente até 500. Descobrimos que o aumento do tamanho da página para além dos 500 registos resulta em atrasos inaceitáveis e grandes problemas de memória.

Como: Métodos de consulta concatenate

Os métodos utilizados na consulta das tabelas de backend podem ser concatenados. Os métodos de consulta de corrente permitem selecionar colunas específicas de linhas filtradas que são classificadas e pageed. Pode criar filtros lógicos complexos. Cada método de consulta devolve um objeto de consulta. Para terminar a série de métodos e executar a consulta, chame o método de execução . Por exemplo:

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 acorrentados devem ser encomendados da seguinte forma:

  1. Filtragem (onde) métodos.
  2. Métodos de triagem (orderBy).
  3. Seleção (selecione) métodos.
  4. métodos de paging (saltar e em cima).

Vincular os dados à interface do utilizador

A ligação de dados envolve três componentes:

  • A fonte de dados
  • O layout do ecrã
  • O adaptador que une os dois.

No nosso código de amostra, devolvemos os dados da tabela SQL Azure De Aplicações Móveis ToDoItem numa matriz. Esta atividade é um padrão comum para aplicações de dados. As consultas de base de dados retornam frequentemente uma coleção de linhas que o cliente recebe numa lista ou matriz. Nesta amostra, a matriz é a fonte de dados. O código especifica um esquema de ecrã que define a visão dos dados que aparecem no dispositivo. Os dois estão ligados juntamente com um adaptador, que neste código é uma extensão da classe ArrayAdapterToDoItem<>.

Definir o Layout

O layout é definido por vários snippets do código XML. Dado um layout existente, o código que se segue representa o ListView que queremos preencher com os dados do nosso servidor.

    <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 o id do layout para uma linha individual na lista. Este código especifica uma caixa de verificação e o seu texto associado e é instantâneo uma vez para cada item da lista. Este layout não exibe o campo de id , e um layout mais complexo especificaria campos adicionais no visor. Este código está no ficheiro row_list_to_do.xml .

<?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 adaptador

Uma vez que a fonte de dados da nossa visão é uma matriz de ToDoItem, subclassificamos o nosso adaptador de uma classe ArrayAdapterToDoItem<>. Esta subclasse produz uma vista para cada ToDoItem utilizando o layout row_list_to_do . No nosso código, definimos a seguinte classe que é uma extensão da classe ArrayAdapterE<>:

public class ToDoItemAdapter extends ArrayAdapter<ToDoItem> {
}

Substitua os adaptadores obter Método De Visão . Por exemplo:

    @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 um exemplo desta classe na nossa Atividade da seguinte forma:

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

O segundo parâmetro do construtor ToDoItemAdapter é uma referência ao layout. Agora podemos instantaneamente o ListView e atribuir o adaptador ao ListView.

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

Utilize o adaptador para ligar à UI

Está agora pronto a utilizar a ligação de dados. O código que se segue mostra como colocar itens na mesa e enche o adaptador local com os itens devolvidos.

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

Ligue para o adaptador sempre que modificar a tabela ToDoItem . Uma vez que as modificações são feitas por registo, lida com uma única linha em vez de uma coleção. Quando inserir um item, ligue para o método de adicionar no adaptador; ao eliminar, chame o método de remoção .

Pode encontrar um exemplo completo no Project Android Quickstart.

Inserir dados no backend

Instantaneamente um exemplo da classe ToDoItem e definir as suas propriedades.

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

Em seguida , utilize o inserível() para inserir um objeto:

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

A entidade devolvida corresponde aos dados inseridos na tabela backend, incluindo o ID e quaisquer outros valores (como, por exemplo, updatedAte createdAtversion campos) definidos no backend.

As tabelas de Aplicações Móveis requerem uma coluna-chave primária chamada id. Esta coluna deve ser uma corda. O valor predefinido da coluna ID é um GUID. Pode fornecer outros valores únicos, como endereços de e-mail ou nomes de utilizador. Quando um valor de ID de cadeia não é fornecido para um registo inserido, o backend gera um novo GUID.

Os valores de ID de cadeia fornecem as seguintes vantagens:

  • Os IDs podem ser gerados sem fazer uma ida e volta à base de dados.
  • Os registos são mais fáceis de fundir a partir de diferentes tabelas ou bases de dados.
  • Os valores de ID integram-se melhor com a lógica de uma aplicação.

Os valores de ID de cadeia são necessários para suporte de sincronização offline. Não é possível alterar uma identificação uma vez que esteja guardada na base de dados de backend.

Atualizar dados numa aplicação móvel

Para atualizar os dados numa tabela, passe o novo objeto para o método de atualização().

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

Neste exemplo, o item é uma referência a uma linha na tabela ToDoItem , que teve algumas alterações no mesmo. A linha com o mesmo id é atualizada.

Eliminar dados numa aplicação móvel

O código que se segue mostra como eliminar dados de uma tabela especificando o objeto de dados.

mToDoTable
    .delete(item);

Também pode eliminar um item especificando o campo de id da linha para eliminar.

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

Procure um item específico por Id

Procure um item com um campo de id específico com o método lookUp()

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

Como: Trabalhar com dados não estadudos

O modelo de programação não modelado dá-lhe controlo exato sobre a serialização do JSON. Existem alguns cenários comuns em que poderá querer utilizar um modelo de programação não modelado. Por exemplo, se a sua tabela de backend contiver muitas colunas e só precisa de fazer referência a um subconjunto das colunas. O modelo dactilografado requer que você defina todas as colunas definidas no backend de Aplicações Móveis na sua classe de dados. A maioria das chamadas da API para aceder aos dados são semelhantes às chamadas de programação dactilografada. A principal diferença é que no modelo não modelado invoca métodos no objeto MobileServiceJsonTable , em vez do objeto MobileServiceTable .

Criar uma instância de uma tabela não estadada

Semelhante ao modelo dactilografado, começa por obter uma referência de mesa, mas neste caso é um objeto MobileServicesJsonTable . Obtenha a referência chamando o método getTable em uma instância do cliente:

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

Depois de ter criado um exemplo do MobileServiceJsonTable, tem praticamente a mesma API disponível que com o modelo de programação dactilografado. Em alguns casos, os métodos tomam um parâmetro não definido em vez de um parâmetro dactilografado.

Insira numa tabela não estalada

O seguinte código mostra como fazer uma inserção. O primeiro passo é criar um JsonObject, que faz parte da biblioteca gson .

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

Em seguida, Insira o objeto não estadado na tabela.

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

Se precisar de obter o ID do objeto inserido, utilize o método getAsJsonPrimitive( ).

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

Excluir de uma mesa não estalada

O código que se segue mostra como apagar uma instância, neste caso, a mesma instância de um JsonObject que foi criado no exemplo de inserção anterior. O código é o mesmo que com o caso dactilografado, mas o método tem uma assinatura diferente uma vez que refere um JsonObject.

mToDoTable
    .delete(insertedItem);

Também pode eliminar um caso diretamente utilizando o seu ID:

mToDoTable.delete(ID);

Devolva todas as linhas de uma mesa não estanhada

O seguinte código mostra como recuperar uma mesa inteira. Uma vez que está a utilizar uma Tabela JSON, só pode recuperar seletivamente algumas colunas da tabela.

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 paging que estão disponíveis para o modelo dactilografado estão disponíveis para o modelo não marcado.

Implementar Offline Sync

O Azure Mobile Apps Client SDK também implementa a sincronização offline de dados utilizando uma base de dados SQLite para armazenar uma cópia dos dados do servidor localmente. As operações realizadas numa tabela offline não requerem conectividade móvel para funcionar. A sincronização offline ajuda na resiliência e no desempenho em detrimento de uma lógica mais complexa para a resolução de conflitos. O Azure Mobile Apps Client SDK implementa as seguintes funcionalidades:

  • Sincronização incremental: Apenas são descarregados novos registos atualizados e novos, poupando largura de banda e consumo de memória.
  • Concordância otimista: As operações são assumidas como tendo sucesso. A Resolução de Conflitos é adiada até que as atualizações sejam realizadas no servidor.
  • Resolução de Conflitos: O SDK deteta quando uma alteração conflituosa foi feita no servidor e fornece ganchos para alertar o utilizador.
  • Soft Delete: Os registos eliminados são marcados como eliminados, permitindo que outros dispositivos atualizem a sua cache offline.

Inicializar offline Sync

Cada tabela offline deve ser definida na cache offline antes da utilização. Normalmente, a definição de tabela é feita imediatamente após a criação do cliente:

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

Obtenha uma referência à Tabela cache offline

Para uma mesa online, você usa .getTable(). Para uma tabela offline, utilize .getSyncTable():

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

Todos os métodos disponíveis para tabelas online (incluindo filtragem, triagem, paging, inserção de dados, atualização de dados e eliminação de dados) funcionam igualmente bem em tabelas online e offline.

Sincronizar a Cache Offline Local

A sincronização está sob o controlo da sua aplicação. Aqui está um método de sincronização de exemplo:

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 .pull(query, queryname) método, então a sincronização incremental é usada para devolver apenas registos que tenham sido criados ou alterados desde a última atração concluída com sucesso.

Lidar com conflitos durante a sincronização offline

Se um conflito acontecer durante uma .push() operação, um MobileServiceConflictException é lançado. O item emitido pelo servidor está incorporado na exceção e pode ser recuperado com .getItem() exceção. Ajuste o impulso chamando os seguintes itens no objeto MobileServiceSyncContext:

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

Uma vez que todos os conflitos sejam marcados como quiser, ligue .push() novamente para resolver todos os conflitos.

Chame uma API personalizada

Uma API personalizada permite-lhe definir pontos finais personalizados que expõem a funcionalidade do servidor que não mapeia para uma operação de inserção, atualização, exclusão ou leitura. Ao utilizar uma API personalizada, pode ter mais controlo sobre mensagens, incluindo ler e definir cabeçalhos de mensagens HTTP e definir um formato corporal de mensagens diferente do JSON.

A partir de um cliente Android, você ligue para o método invocar Api para ligar para o ponto final da API personalizado. O exemplo a seguir mostra como chamar um ponto final da API nomeado completeAll, que devolve uma classe de coleção chamada 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 invocarApi é chamado ao cliente, que envia um pedido de CORREIO para a nova API personalizada. O resultado devolvido pela API personalizada é apresentado num diálogo de mensagem, assim como quaisquer erros. Outras versões de invokeApi permitem-lhe enviar opcionalmente um objeto no corpo de pedido, especificar o método HTTP e enviar parâmetros de consulta com o pedido. Também são fornecidas versões não estaspídas de invokeApi .

Adicionar autenticação à aplicação

Os tutoriais já descrevem em detalhe como adicionar estas funcionalidades.

Serviço de Aplicações suporta a autenticação de utilizadores de aplicações utilizando vários fornecedores de identidade externas: Facebook, Google, Microsoft Account, Twitter e Azure Ative Directory. Pode definir permissões em tabelas para restringir o acesso a operações específicas apenas a utilizadores autenticados. Também pode utilizar a identidade dos utilizadores autenticados para implementar regras de autorização no seu backend.

Dois fluxos de autenticação são suportados: um fluxo de servidor e um fluxo de cliente . O fluxo do servidor proporciona a experiência de autenticação mais simples, uma vez que se baseia na interface web dos fornecedores de identidade. Não são necessários SDKs adicionais para implementar a autenticação do fluxo do servidor. A autenticação do fluxo do servidor não proporciona uma integração profunda no dispositivo móvel e é apenas recomendada para a prova de cenários de conceito.

O fluxo do cliente permite uma integração mais profunda com capacidades específicas do dispositivo, tais como um único sign-on, uma vez que se baseia em SDKs fornecidos pelo fornecedor de identidade. Por exemplo, pode integrar o SDK do Facebook na sua aplicação móvel. O cliente móvel troca na aplicação do Facebook e confirma o seu login antes de voltar a trocar para a sua aplicação móvel.

São necessários quatro passos para permitir a autenticação na sua aplicação:

  • Registe a sua aplicação para autenticação junto de um fornecedor de identidade.
  • Configure o seu Serviço de Aplicações backend.
  • Restringir as permissões de mesa aos utilizadores autenticados apenas no backend Serviço de Aplicações.
  • Adicione código de autenticação à sua aplicação.

Pode definir permissões em tabelas para restringir o acesso a operações específicas apenas a utilizadores autenticados. Também pode utilizar o SID de um utilizador autenticado para modificar pedidos. Para mais informações, reveja Introdução com autenticação e a documentação do Server SDK HOWTO.

Autenticação: Flow do servidor

O seguinte código inicia um processo de login de fluxo de servidor utilizando o fornecedor Google. É necessária uma configuração adicional devido aos requisitos de segurança para o fornecedor Google:

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

Além disso, adicione o seguinte método à classe principal de Atividade:

// 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 na sua atividade principal é usado para o login() método e dentro do onActivityResult() método. Pode escolher qualquer número único, desde que o mesmo número seja utilizado dentro do login() método e do onActivityResult() método. Se resumir o código do cliente num adaptador de serviço (como mostrado anteriormente), deve ligar para os métodos apropriados no adaptador de serviço.

Você também precisa configurar o projeto para personalizados. Primeiro, especifique um REorientar URL. Adicione o seguinte corte a 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 ficheiro para a build.gradle sua aplicação:

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

Finalmente, adicione com.android.support:customtabs:28.0.0 à lista de dependências no build.gradle ficheiro:

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 o ID do utilizador iniciado com sessão de um MobileServiceUser utilizando o método getUserId . Para um exemplo de como usar o Futures para ligar para as APIs de login assíncronos, consulte Introdução com autenticação.

Aviso

O esquema url mencionado é sensível a casos. Certifique-se de que todas as ocorrências de caso de {url_scheme_of_you_app} correspondência.

Fichas de autenticação de cache

Colocação em Cache fichas de autenticação requer que você guarde o ID do Utilizador e o token de autenticação localmente no dispositivo. Da próxima vez que a aplicação começar, verifique a cache, e se estes valores estiverem presentes, pode saltar o procedimento de login e reidratar o cliente com estes dados. No entanto, estes dados são sensíveis e devem ser armazenados encriptados por segurança no caso de o telefone ser roubado. Pode ver um exemplo completo de como cache fichas de autenticação na secção de fichas de autenticação cache.

Quando tentas usar um token expirado, recebes uma resposta não autorizada 401 . Pode lidar com erros de autenticação utilizando filtros. Os filtros intercetam pedidos para o Serviço de Aplicações backend. O código de filtro testa a resposta para um 401, aciona o processo de inscrição e, em seguida, retoma o pedido que gerou o 401.

Use Refresh Tokens

O token devolvido por Serviço de Aplicações do Azure Autenticação e Autorização tem um tempo de vida definido de uma hora. Após este período, deve reautenticar o utilizador. Se estiver a utilizar um sinal de longa duração que recebeu através da autenticação de fluxo de clientes, então pode reautenticar com Serviço de Aplicações do Azure Autenticação e Autorização utilizando o mesmo token. Outro Serviço de Aplicações do Azure token é gerado com uma nova vida.

Também pode registar o fornecedor para utilizar a Refresh Tokens. Um Refresh Token nem sempre está disponível. É necessária uma configuração adicional:

  • Para Azure Ative Directory, configuure um segredo de cliente para a App Azure Ative Directory. Especifique o segredo do cliente no Serviço de Aplicações do Azure ao configurar Azure Ative Directory Autenticação. Ao ligar .login(), passe response_type=code id_token como parâmetro:

    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 parâmetro:

    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 a Conta Microsoft, selecione o wl.offline_access âmbito.

Para refrescar um símbolo, ligue .refreshUser():

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

Como uma boa prática, crie um filtro que detete uma resposta 401 a partir do servidor e tente refrescar o token do utilizador.

Iniciar sessão com autenticação de fluxo de cliente

O processo geral de login com autenticação de fluxo cliente é o seguinte:

  • Configure Serviço de Aplicações do Azure Autenticação e Autorização como seria a autenticação do fluxo do servidor.

  • Integrar o fornecedor de autenticação SDK para a autenticação para produzir um token de acesso.

  • Ligue para o método da .login() seguinte forma (result deve ser um 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 secção seguinte.

Substitua o onSuccess() método por qualquer código que deseje utilizar num login bem sucedido. A {provider} cadeia é um fornecedor válido: aad (Azure Ative Directory), facebook, google, microsoftaccount, ou twitter. Se implementou a autenticação personalizada, também pode utilizar a etiqueta do fornecedor de autenticação personalizada.

Autenticar os utilizadores com a Biblioteca de Autenticação de Diretório Ativo (ADAL)

Pode utilizar a Biblioteca de Autenticação de Diretório Ativo (ADAL) para inscrever os utilizadores na sua aplicação utilizando Azure Ative Directory. A utilização de um login de fluxo de cliente é muitas vezes preferível a usar os loginAsync() métodos, pois proporciona uma sensação de UX mais nativa e permite uma personalização adicional.

  1. Configure o backend da sua aplicação móvel para AAD login seguindo o como configurar Serviço de Aplicações para tutorial de login do Ative Directory. Certifique-se de completar o passo opcional de registo de uma aplicação de cliente nativo.

  2. Instale o ADAL modificando o seu ficheiro build.gradle para incluir as seguintes definições:

    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 seguinte código à sua aplicação, fazendo as seguintes substituições:

    • Substitua INSERT-AUTHORITY-HERE pelo nome do arrendatário em que aprovisionou o seu pedido. O formato deve ser https://login.microsoftonline.com/contoso.onmicrosoft.com.
    • Substitua o INSERT-RESOURCE-ID-HERE pelo ID do cliente para o seu backend da aplicação móvel. Pode obter o ID do cliente a partir do separador Avançadosob Azure Ative Directory Definições no portal.
    • Substitua INSERT-CLIENT-ID-HERE pelo ID do cliente que copiou da aplicação do cliente nativo.
    • Substitua o INSERT-REDIRECT-URI-HERE pelo ponto final do seu site /.auth/login/done, utilizando o esquema HTTPS. Este valor deve ser semelhante a 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 Client-Server

A ligação ao Cliente é normalmente uma ligação HTTP básica utilizando a biblioteca HTTP subjacente fornecida com o Android SDK. Existem várias razões pelas quais quereria mudar isso:

  • Deseja utilizar uma biblioteca HTTP alternativa para ajustar os intervalos de tempo.
  • Quer providenciar uma barra de progresso.
  • Deseja adicionar um cabeçalho personalizado para suportar a funcionalidade de gestão da API.
  • Deseja intercetar uma resposta falhada para que possa implementar a reauferição.
  • Deseja registar pedidos de backend para um serviço de análise.

Usando uma biblioteca HTTP alternativa

Ligue para o .setAndroidHttpClientFactory() método imediatamente após a criação da referência ao seu cliente. Por exemplo, para definir o tempo limite de ligação para 60 segundos (em vez dos 10 segundos padrão):

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 progresso

Pode implementar uma interceção de cada pedido implementando um ServiceFilter. Por exemplo, as seguintes atualizações uma barra de progresso pré-criada:

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

Pode anexar este filtro ao cliente da seguinte forma:

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

Personalizar Cabeçalhos de Pedido

Utilize o seguinte ServiceFilter e prenda o filtro da mesma forma que o ProgressFilterseguinte:

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ática

Pode especificar uma estratégia de conversão que se aplica a cada coluna utilizando a GSON API. A biblioteca de clientes Android usa gson nos bastidores para serializar objetos Java para dados JSON antes que os dados são enviados para Serviço de Aplicações do Azure. O código seguinte utiliza o método setFieldNamingStrategy () para definir a estratégia. Este exemplo apagará o carácter inicial (um "m"), e, em seguida, diminuirá o personagem seguinte, para cada nome de campo. Por exemplo, transformaria "mId" em "id". Implementar uma estratégia de conversão para reduzir a necessidade SerializedName() de anotações na maioria dos campos.

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

Este código deve ser executado antes de criar uma referência móvel ao cliente utilizando o MobileServiceClient.