Udostępnij za pośrednictwem


Jak używać zestawu SDK usługi Azure Mobile Apps dla systemu Android

W tym przewodniku pokazano, jak używać zestawu SDK klienta systemu Android dla usługi Mobile Apps do implementowania typowych scenariuszy, takich jak:

  • Wykonywanie zapytań dotyczących danych (wstawianie, aktualizowanie i usuwanie).
  • Uwierzytelnianie.
  • Obsługa błędów.
  • Dostosowywanie klienta.

Ten przewodnik koncentruje się na zestawie SDK systemu Android po stronie klienta. Aby dowiedzieć się więcej na temat zestawów SDK po stronie serwera dla aplikacji mobilnych, zobacz Praca z zestawem SDK zaplecza platformy .NET lub Jak używać zestawu SDK zaplecza Node.js.

Dokumentacja referencyjna

Dokumentacja interfejsu API języka Javadocs dla biblioteki klienta systemu Android znajduje się w witrynie GitHub.

Obsługiwane platformy

Zestaw SDK usługi Azure Mobile Apps dla systemu Android obsługuje poziomy interfejsu API od 19 do 24 (KitKat through Nougat) dla telefonów i tabletów. Uwierzytelnianie, w szczególności, wykorzystuje typowe podejście platformy internetowej do zbierania poświadczeń. Uwierzytelnianie przepływu serwera nie działa z małymi urządzeniami typu form factor, takimi jak zegarki.

Konfiguracja i wymagania wstępne

Ukończ samouczek Szybki start dotyczący usługi Mobile Apps. To zadanie zapewnia spełnienie wszystkich wymagań wstępnych dotyczących tworzenia usługi Azure Mobile Apps. Przewodnik Szybki start ułatwia również skonfigurowanie konta i utworzenie pierwszego zaplecza aplikacji mobilnej.

Jeśli zdecydujesz się nie ukończyć samouczka Szybki start, wykonaj następujące zadania:

Aktualizowanie pliku kompilacji narzędzia Gradle

Zmień oba pliki build.gradle :

  1. Dodaj ten kod do pliku build.gradle na poziomie projektu:

    buildscript {
        repositories {
            jcenter()
            google()
        }
    }
    
    allprojects {
        repositories {
            jcenter()
            google()
        }
    }
    
  2. Dodaj ten kod do pliku build.gradle na poziomie aplikacji modułu w tagu zależności:

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

    Obecnie najnowsza wersja to 3.4.0. Obsługiwane wersje są wyświetlane na bintray.

Włączanie uprawnień do Internetu

Aby uzyskać dostęp do platformy Azure, aplikacja musi mieć włączone uprawnienie INTERNETOWE. Jeśli nie jest jeszcze włączona, dodaj następujący wiersz kodu do pliku AndroidManifest.xml :

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

Tworzenie połączenia klienta

Usługa Azure Mobile Apps udostępnia cztery funkcje aplikacji mobilnej:

  • Dostęp do danych i synchronizacja w trybie offline z usługą Azure Mobile Apps Service.
  • Wywoływanie niestandardowych interfejsów API napisanych za pomocą zestawu SDK serwera usługi Azure Mobile Apps.
  • Uwierzytelnianie przy użyciu uwierzytelniania i autoryzacji usługi aplikacja systemu Azure.
  • Rejestracja powiadomień wypychanych za pomocą usługi Notification Hubs.

Każda z tych funkcji najpierw wymaga utworzenia MobileServiceClient obiektu. W kliencie mobilnym należy utworzyć tylko jeden MobileServiceClient obiekt (powinien to być pojedynczy wzorzec). Aby utworzyć MobileServiceClient obiekt:

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

Jest <MobileAppUrl> to ciąg lub obiekt adresu URL wskazujący zaplecze mobilne. Jeśli używasz usługi aplikacja systemu Azure do hostowania zaplecza dla urządzeń przenośnych, upewnij się, że używasz bezpiecznej https:// wersji adresu URL.

Klient wymaga również dostępu do działania lub kontekstu — this parametru w przykładzie. Konstrukcja MobileServiceClient powinna odbywać się w onCreate() metodzie działania, do których AndroidManifest.xml odwołuje się plik .

Najlepszym rozwiązaniem jest abstrakcja komunikacji serwera z własną klasą (singleton-pattern). W takim przypadku należy przekazać działanie w konstruktorze, aby odpowiednio skonfigurować usługę. Na przykład:

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

Teraz możesz wywołać AzureServiceAdapter.Initialize(this); metodę onCreate() głównego działania. Wszelkie inne metody wymagające dostępu do klienta używane AzureServiceAdapter.getInstance(); do uzyskania odwołania do karty usługi.

Operacje na danych

Podstawowym elementem zestawu SDK usługi Azure Mobile Apps jest zapewnienie dostępu do danych przechowywanych w Usługi SQL Azure w zapleczu aplikacji mobilnej. Dostęp do tych danych można uzyskać przy użyciu silnie typiowanych klas (preferowanych) lub nietypowych zapytań (niezalecane). Większość tej sekcji dotyczy używania silnie typiowanych klas.

Definiowanie klas danych klienta

Aby uzyskać dostęp do danych z tabel Usługi SQL Azure, zdefiniuj klasy danych klienta, które odpowiadają tabelom w zapleczu aplikacji mobilnej. W przykładach w tym temacie przyjęto założenie, że tabela o nazwie MyDataTable zawiera następujące kolumny:

  • identyfikator
  • text
  • ukończono

Odpowiadający typowany obiekt po stronie klienta znajduje się w pliku o nazwie MyDataTable.java:

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

Dodaj metody getter i setter dla każdego dodawanego pola. Jeśli tabela Usługi SQL Azure zawiera więcej kolumn, należy dodać odpowiednie pola do tej klasy. Jeśli na przykład obiekt DTO (obiekt transferu danych) miał kolumnę Priorytet liczby całkowitej, możesz dodać to pole wraz z metodami pobierania i ustawiania:

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

Aby dowiedzieć się, jak tworzyć dodatkowe tabele w zapleczu usługi Mobile Apps, zobacz How to: Define a table controller (.NET backend) or Define Tables using a Dynamic Schema (Node.js backend).

Tabela zaplecza usługi Azure Mobile Apps definiuje pięć specjalnych pól, z których cztery są dostępne dla klientów:

  • String id: globalnie unikatowy identyfikator rekordu. Najlepszym rozwiązaniem jest utworzenie identyfikatora reprezentacji ciągu obiektu UUID .
  • DateTimeOffset updatedAt: data/godzina ostatniej aktualizacji. Pole updatedAt jest ustawiane przez serwer i nigdy nie powinno być ustawiane przez kod klienta.
  • DateTimeOffset createdAt: data/godzina utworzenia obiektu. Pole createdAt jest ustawiane przez serwer i nigdy nie powinno być ustawiane przez kod klienta.
  • byte[] version: zwykle reprezentowane jako ciąg, wersja jest również ustawiana przez serwer.
  • boolean deleted: wskazuje, że rekord został usunięty, ale nie został jeszcze przeczyszczone. Nie należy używać deleted jako właściwości w klasie.

Pole id jest wymagane. Pole updatedAt i version pole są używane do synchronizacji offline (odpowiednio w przypadku synchronizacji przyrostowej i rozwiązywania konfliktów). Pole createdAt jest polem referencyjnym i nie jest używane przez klienta. Nazwy są nazwami "na całej drutu" właściwości i nie są regulowane. Można jednak utworzyć mapowanie między obiektem a nazwami "across-the-wire" przy użyciu biblioteki gson . Na przykład:

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

Tworzenie odwołania do tabeli

Aby uzyskać dostęp do tabeli, najpierw utwórz obiekt MobileServiceTable, wywołując metodę getTable w obiekcie MobileServiceClient. Ta metoda ma dwa przeciążenia:

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

W poniższym kodzie obiekt mClient jest odwołaniem do obiektu MobileServiceClient. Pierwsze przeciążenie jest używane, gdy nazwa klasy i nazwa tabeli są takie same i jest używane w przewodniku Szybki start:

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

Drugie przeciążenie jest używane, gdy nazwa tabeli różni się od nazwy klasy: pierwszy parametr jest nazwą tabeli.

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

Wykonywanie zapytań względem tabeli zaplecza

Najpierw uzyskaj odwołanie do tabeli. Następnie wykonaj zapytanie w dokumentacji tabeli. Zapytanie jest dowolną kombinacją:

Klauzule muszą być przedstawione w poprzedniej kolejności.

Filtrowanie wyników

Ogólna forma zapytania to:

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

Powyższy przykład zwraca wszystkie wyniki (do maksymalnego rozmiaru strony ustawionego przez serwer). Metoda .execute() wykonuje zapytanie na zapleczu. Zapytanie jest konwertowane na zapytanie OData w wersji 3 przed przesłaniem do zaplecza usługi Mobile Apps. Po otrzymaniu zaplecze usługi Mobile Apps konwertuje zapytanie na instrukcję SQL przed wykonaniem go w wystąpieniu Usługi SQL Azure. Ponieważ działanie sieci zajmuje trochę czasu, .execute() metoda zwraca ListenableFuture<E>wartość .

Filtr zwracanych danych

Następujące wykonanie zapytania zwraca wszystkie elementy z tabeli ToDoItem , gdzie complete równa false.

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

mToDoTable to odwołanie do utworzonej wcześniej tabeli usługi mobilnej.

Zdefiniuj filtr przy użyciu wywołania metody where w odwołaniu do tabeli. Po metodzie where następuje metoda pola , po której następuje metoda określająca predykat logiczny. Możliwe metody predykatu obejmują eq (equals), ne (nie równe), gt (większe niż), ge (większe lub równe), lt (mniejsze niż), le (mniejsze lub równe). Te metody umożliwiają porównywanie pól liczbowych i ciągów z określonymi wartościami.

Możesz filtrować daty. Poniższe metody umożliwiają porównanie całego pola daty lub części daty: rok, miesiąc, dzień, godzina, minuta i sekunda. W poniższym przykładzie dodano filtr dla elementów, których data ukończenia to 2013.

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

Następujące metody obsługują złożone filtry w polach ciągów: startsWith, endsWith, concat, subString, indexOf, replace, toLower, toUpper, trim i length. Poniższy przykład filtruje wiersze tabeli, w których kolumna tekstowa zaczyna się od "PRI0".

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

Następujące metody operatorów są obsługiwane w polach liczbowych: add, sub, mul, div, mod, floor, ceiling i round. Poniższy przykład filtruje wiersze tabeli, w których czas trwania jest liczbą parzystą.

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

Predykaty można łączyć z następującymi metodami logicznymi: i, lub nie. Poniższy przykład łączy dwa z powyższych przykładów.

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

Operatory logiczne grupowania i zagnieżdżania:

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

Aby uzyskać bardziej szczegółowe omówienie i przykłady filtrowania, zobacz Eksplorowanie bogactwa modelu zapytań klienta systemu Android.

Sortowanie zwróconych danych

Poniższy kod zwraca wszystkie elementy z tabeli ToDoItems posortowane rosnąco według pola tekstowego. mToDoTable to odwołanie do utworzonej wcześniej tabeli zaplecza:

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

Pierwszy parametr metody orderBy jest ciągiem równym nazwie pola, na którym ma być sortowane. Drugi parametr używa wyliczenia QueryOrder , aby określić, czy sortować rosnąco, czy malejąco. Jeśli filtrujesz przy użyciu metody where, metoda where musi zostać wywołana przed metodą orderBy.

Wybieranie określonych kolumn

Poniższy kod ilustruje sposób zwracania wszystkich elementów z tabeli ToDoItems, ale wyświetla tylko pełne i tekstowe pola. mToDoTable to odwołanie do utworzonej wcześniej tabeli zaplecza.

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

Parametry funkcji select to nazwy ciągów kolumn tabeli, które chcesz zwrócić. Metoda select musi postępować zgodnie z metodami, takimi jak gdzie i orderBy. Można go obserwować przez metody stronicowania, takie jak pomiń i u góry.

Zwracanie danych na stronach

Dane są zawsze zwracane na stronach. Maksymalna liczba zwracanych rekordów jest ustawiana przez serwer. Jeśli klient żąda więcej rekordów, serwer zwraca maksymalną liczbę rekordów. Domyślnie maksymalny rozmiar strony na serwerze to 50 rekordów.

W pierwszym przykładzie pokazano, jak wybrać pięć pierwszych elementów z tabeli. Zapytanie zwraca elementy z tabeli ToDoItems. mToDoTable to odwołanie do utworzonej wcześniej tabeli zaplecza:

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

Oto zapytanie, które pomija pierwsze pięć elementów, a następnie zwraca następne pięć:

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

Jeśli chcesz pobrać wszystkie rekordy w tabeli, zaimplementuj kod, aby iterować na wszystkich stronach:

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

Żądanie dla wszystkich rekordów używających tej metody tworzy co najmniej dwa żądania do zaplecza usługi Mobile Apps.

Napiwek

Wybór odpowiedniego rozmiaru strony to równowaga między użyciem pamięci, podczas gdy żądanie jest wykonywane, użycie przepustowości i opóźnienie całkowitego odbierania danych. Wartość domyślna (50 rekordów) jest odpowiednia dla wszystkich urządzeń. Jeśli korzystasz wyłącznie z większych urządzeń pamięci, zwiększ do 500. Odkryliśmy, że zwiększenie rozmiaru strony przekraczającej 500 rekordów powoduje niedopuszczalne opóźnienia i duże problemy z pamięcią.

Instrukcje: łączenie metod zapytań

Metody używane podczas wykonywania zapytań w tabelach zaplecza mogą być łączone. Metody zapytań łańcuchowych umożliwiają wybranie określonych kolumn przefiltrowanych wierszy, które są sortowane i stronicowane. Można tworzyć złożone filtry logiczne. Każda metoda kwerendy zwraca obiekt Query. Aby zakończyć serię metod i faktycznie uruchomić zapytanie, wywołaj metodę execute . Na przykład:

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

Metody zapytań łańcuchowych muszą być uporządkowane w następujący sposób:

  1. Filtrowanie metod (gdzie).
  2. Metody sortowania (orderBy).
  3. Metody wyboru (select).
  4. metody stronicowania (pomiń i u góry).

Wiązanie danych z interfejsem użytkownika

Powiązanie danych obejmuje trzy składniki:

  • Źródło danych
  • Układ ekranu
  • Adapter, który łączy te dwa ze sobą.

W naszym przykładowym kodzie zwracamy dane z tabeli Mobile Apps Usługi SQL Azure ToDoItem do tablicy. To działanie jest typowym wzorcem dla aplikacji danych. Zapytania bazy danych często zwracają kolekcję wierszy, które klient otrzymuje na liście lub tablicy. W tym przykładzie tablica jest źródłem danych. Kod określa układ ekranu, który definiuje widok danych wyświetlanych na urządzeniu. Te dwa są powiązane z adapterem, który w tym kodzie jest rozszerzeniem klasy ArrayAdapter<ToDoItem> .

Definiowanie układu

Układ jest definiowany przez kilka fragmentów kodu XML. Biorąc pod uwagę istniejący układ, poniższy kod reprezentuje obiekt ListView , który chcemy wypełnić naszymi danymi serwera.

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

W poprzednim kodzie atrybut listitem określa identyfikator układu dla pojedynczego wiersza na liście. Ten kod określa pole wyboru i skojarzony z nim tekst i jest tworzone raz dla każdego elementu na liście. Ten układ nie wyświetla pola id , a bardziej złożony układ określa dodatkowe pola na ekranie. Ten kod znajduje się w pliku 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>

Definiowanie karty

Ponieważ źródłem danych naszego widoku jest tablica ToDoItem, podklasujemy naszą kartę z klasy ArrayAdapter<ToDoItem> . Ta podklasa tworzy widok dla każdego elementu ToDoItem przy użyciu układu row_list_to_do . W naszym kodzie definiujemy następującą klasę, która jest rozszerzeniem klasy ArrayAdapter<E> :

public class ToDoItemAdapter extends ArrayAdapter<ToDoItem> {
}

Zastąpij adaptery metodę getView . Na przykład:

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

Utworzymy wystąpienie tej klasy w naszym działaniu w następujący sposób:

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

Drugi parametr konstruktora ToDoItemAdapter jest odwołaniem do układu. Teraz możemy utworzyć wystąpienie obiektu ListView i przypisać kartę do obiektu ListView.

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

Tworzenie powiązania z interfejsem użytkownika za pomocą adaptera

Teraz możesz przystąpić do korzystania z powiązania danych. Poniższy kod pokazuje, jak pobrać elementy w tabeli i wypełnić kartę lokalną zwróconymi elementami.

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

Wywołaj kartę za każdym razem, gdy zmodyfikujesz tabelę ToDoItem . Ponieważ modyfikacje są wykonywane na rekordzie według rekordów, obsługujesz pojedynczy wiersz zamiast kolekcji. Podczas wstawiania elementu wywołaj metodę add na karcie. Podczas usuwania wywołaj metodę remove .

Kompletny przykład można znaleźć w projekcie Szybkiego startu dla systemu Android.

Wstawianie danych do zaplecza

Utwórz wystąpienie klasy ToDoItem i ustaw jego właściwości.

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

Następnie użyj metody insert(), aby wstawić obiekt:

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

Zwrócona jednostka jest zgodna z danymi wstawianymi do tabeli zaplecza, uwzględniła identyfikator i inne wartości (takie jak createdAtpola , updatedAti version ) ustawione na zapleczu.

Tabele usługi Mobile Apps wymagają kolumny klucza podstawowego o nazwie id. Ta kolumna musi być ciągiem. Wartość domyślna kolumny ID to identyfikator GUID. Możesz podać inne unikatowe wartości, takie jak adresy e-mail lub nazwy użytkowników. Jeśli nie podano wartości identyfikatora ciągu dla wstawionego rekordu, zaplecze generuje nowy identyfikator GUID.

Wartości identyfikatora ciągu zapewniają następujące korzyści:

  • Identyfikatory można wygenerować bez dokonywania rundy w bazie danych.
  • Rekordy są łatwiejsze do scalenia z różnych tabel lub baz danych.
  • Wartości identyfikatorów lepiej integrują się z logiką aplikacji.

Wartości identyfikatora ciągu są wymagane do obsługi synchronizacji w trybie offline. Nie można zmienić identyfikatora, gdy jest on przechowywany w bazie danych zaplecza.

Aktualizowanie danych w aplikacji mobilnej

Aby zaktualizować dane w tabeli, przekaż nowy obiekt do metody update().

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

W tym przykładzie element jest odwołaniem do wiersza w tabeli ToDoItem , w którym wprowadzono pewne zmiany. Wiersz o tym samym identyfikatorze jest aktualizowany.

Usuwanie danych w aplikacji mobilnej

Poniższy kod pokazuje, jak usunąć dane z tabeli, określając obiekt danych.

mToDoTable
    .delete(item);

Element można również usunąć, określając pole id wiersza do usunięcia.

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

Wyszukiwanie określonego elementu według identyfikatora

Wyszukaj element z określonym polem identyfikatora za pomocą metody lookUp():

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

Instrukcje: praca z nietypowymi danymi

Nietypowy model programowania zapewnia dokładną kontrolę nad serializacji JSON. Istnieją pewne typowe scenariusze, w których można użyć nietypowego modelu programowania. Jeśli na przykład tabela zaplecza zawiera wiele kolumn i musisz odwoływać się tylko do podzbioru kolumn. Typowany model wymaga zdefiniowania wszystkich kolumn zdefiniowanych w zapleczu usługi Mobile Apps w klasie danych. Większość wywołań interfejsu API do uzyskiwania dostępu do danych jest podobna do typowych wywołań programowania. Główną różnicą jest to, że w nietypowym modelu metody wywoływane w obiekcie MobileServiceJsonTable zamiast obiektu MobileServiceTable .

Tworzenie wystąpienia nietypowej tabeli

Podobnie jak w przypadku modelu wpisanego, zacznij od uzyskania odwołania do tabeli, ale w tym przypadku jest to obiekt MobileServicesJsonTable . Uzyskaj odwołanie, wywołując metodę getTable w wystąpieniu klienta:

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

Po utworzeniu wystąpienia tabeli MobileServiceJsonTable jest on praktycznie taki sam interfejs API, jak w przypadku modelu programowania typizowanego. W niektórych przypadkach metody przyjmują nietypowy parametr zamiast typizowanego parametru.

Wstaw do nietypowej tabeli

Poniższy kod pokazuje, jak wykonać wstawianie. Pierwszym krokiem jest utworzenie obiektu JsonObject, który jest częścią biblioteki gson .

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

Następnie użyj insert(), aby wstawić nietypowy obiekt do tabeli.

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

Jeśli musisz uzyskać identyfikator wstawionego obiektu, użyj metody getAsJsonPrimitive().

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

Usuwanie z nietypowej tabeli

Poniższy kod pokazuje, jak usunąć wystąpienie, w tym przypadku to samo wystąpienie obiektu JsonObject , które zostało utworzone w poprzednim przykładzie wstawiania . Kod jest taki sam jak w przypadku typizowanego przypadku, ale metoda ma inny podpis, ponieważ odwołuje się do obiektu JsonObject.

mToDoTable
    .delete(insertedItem);

Wystąpienie można również usunąć bezpośrednio przy użyciu jego identyfikatora:

mToDoTable.delete(ID);

Zwracanie wszystkich wierszy z nietypowej tabeli

Poniższy kod pokazuje, jak pobrać całą tabelę. Ponieważ używasz tabeli JSON, możesz selektywnie pobrać tylko niektóre kolumny tabeli.

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

Ten sam zestaw metod filtrowania, filtrowania i stronicowania, które są dostępne dla modelu typizowanego, są dostępne dla nietypowego modelu.

Implementowanie synchronizacji w trybie offline

Zestaw SDK klienta usługi Azure Mobile Apps implementuje również synchronizację danych w trybie offline przy użyciu bazy danych SQLite do przechowywania kopii danych serwera lokalnie. Operacje wykonywane w tabeli offline nie wymagają łączności mobilnej z pracą. Synchronizacja w trybie offline ułatwia odporność i wydajność kosztem bardziej złożonej logiki rozwiązywania konfliktów. Zestaw SDK klienta usługi Azure Mobile Apps implementuje następujące funkcje:

  • Synchronizacja przyrostowa: pobierane są tylko zaktualizowane i nowe rekordy, co pozwala zaoszczędzić przepustowość i zużycie pamięci.
  • Optymistyczna współbieżność: zakłada się, że operacje kończą się powodzeniem. Rozwiązanie konfliktów jest odroczone do czasu wykonania aktualizacji na serwerze.
  • Rozwiązywanie konfliktów: zestaw SDK wykrywa, kiedy na serwerze została wprowadzona zmiana powodująca konflikt i udostępnia zaczepienia w celu powiadamiania użytkownika.
  • Usuwanie nietrwałe: usunięte rekordy są oznaczane jako usunięte, co umożliwia innym urządzeniom aktualizowanie pamięci podręcznej w trybie offline.

Inicjowanie synchronizacji w trybie offline

Przed użyciem każdej tabeli offline należy zdefiniować w pamięci podręcznej trybu offline. Zwykle definicja tabeli jest wykonywana natychmiast po utworzeniu klienta:

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

Uzyskiwanie odwołania do tabeli pamięci podręcznej trybu offline

W przypadku tabeli online użyj polecenia .getTable(). W przypadku tabeli offline użyj polecenia .getSyncTable():

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

Wszystkie metody dostępne dla tabel online (w tym filtrowanie, sortowanie, stronicowanie, wstawianie danych, aktualizowanie danych i usuwanie danych) działają równie dobrze w tabelach online i offline.

Synchronizowanie lokalnej pamięci podręcznej offline

Synchronizacja jest w ramach kontroli aplikacji. Oto przykładowa metoda synchronizacji:

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

Jeśli do metody jest podana .pull(query, queryname) nazwa zapytania, synchronizacja przyrostowa jest używana do zwracania tylko rekordów, które zostały utworzone lub zmienione od czasu ostatniego pomyślnego zakończenia ściągania.

Obsługa konfliktów podczas synchronizacji offline

Jeśli podczas operacji wystąpi .push() konflikt, zostanie zgłoszony element MobileServiceConflictException . Element wystawiony przez serwer jest osadzony w wyjątku i może zostać pobrany przez .getItem() wyjątek. Dostosuj wypychanie, wywołując następujące elementy w obiekcie MobileServiceSyncContext:

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

Po oznaczeniu wszystkich konfliktów jako chcesz, ponownie zadzwoń .push() , aby rozwiązać wszystkie konflikty.

Wywoływanie niestandardowego interfejsu API

Niestandardowy interfejs API umożliwia definiowanie niestandardowych punktów końcowych, które uwidaczniają funkcje serwera, które nie są mapujące na operację wstawiania, aktualizowania, usuwania ani odczytu. Korzystając z niestandardowego interfejsu API, możesz mieć większą kontrolę nad obsługą komunikatów, w tym odczytywanie i ustawianie nagłówków komunikatów HTTP oraz definiowanie formatu treści komunikatu innego niż JSON.

Z poziomu klienta systemu Android wywołasz metodę invokeApi , aby wywołać niestandardowy punkt końcowy interfejsu API. W poniższym przykładzie pokazano, jak wywołać punkt końcowy interfejsu API o nazwie completeAll, który zwraca klasę kolekcji o nazwie 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();
        }
    });
}

Metoda invokeApi jest wywoływana na kliencie, który wysyła żądanie POST do nowego niestandardowego interfejsu API. Wynik zwracany przez niestandardowy interfejs API jest wyświetlany w oknie dialogowym komunikatu, podobnie jak wszelkie błędy. Inne wersje invokeApi umożliwiają opcjonalne wysyłanie obiektu w treści żądania, określanie metody HTTP i wysyłanie parametrów zapytania za pomocą żądania. Podano również nietypowe wersje interfejsu invokeApi .

Dodawanie uwierzytelniania do aplikacji

W samouczkach opisano już szczegółowo sposób dodawania tych funkcji.

Usługa App Service obsługuje uwierzytelnianie użytkowników aplikacji przy użyciu różnych zewnętrznych dostawców tożsamości: Facebook, Google, Konto Microsoft, Twitter i Azure Active Directory. Możesz ustawić uprawnienia do tabel, aby ograniczyć dostęp do określonych operacji tylko uwierzytelnieni użytkownicy. Możesz również użyć tożsamości uwierzytelnionych użytkowników do zaimplementowania reguł autoryzacji w zapleczu.

Obsługiwane są dwa przepływy uwierzytelniania: przepływ serwera i przepływ klienta . Przepływ serwera zapewnia najprostsze środowisko uwierzytelniania, ponieważ opiera się na interfejsie internetowym dostawców tożsamości. Do zaimplementowania uwierzytelniania przepływu serwera nie są wymagane żadne dodatkowe zestawy SDK. Uwierzytelnianie przepływu serwera nie zapewnia głębokiej integracji z urządzeniem przenośnym i jest zalecane tylko w przypadku scenariuszy weryfikacji koncepcji.

Przepływ klienta umożliwia głębszą integrację z funkcjami specyficznymi dla urządzenia, takimi jak logowanie jednokrotne, ponieważ opiera się na zestawach SDK udostępnianych przez dostawcę tożsamości. Możesz na przykład zintegrować zestaw SDK serwisu Facebook z aplikacją mobilną. Klient mobilny zamienia się w aplikację Facebook i potwierdza logowanie przed zamianą z powrotem do aplikacji mobilnej.

Do włączenia uwierzytelniania w aplikacji wymagane są cztery kroki:

  • Zarejestruj aplikację do uwierzytelniania za pomocą dostawcy tożsamości.
  • Skonfiguruj zaplecze usługi App Service.
  • Ogranicz uprawnienia tabeli tylko do uwierzytelnionych użytkowników w zapleczu usługi App Service.
  • Dodaj kod uwierzytelniania do aplikacji.

Możesz ustawić uprawnienia do tabel, aby ograniczyć dostęp do określonych operacji tylko uwierzytelnieni użytkownicy. Do modyfikowania żądań można również użyć identyfikatora SID uwierzytelnionego użytkownika. Aby uzyskać więcej informacji, zapoznaj się z dokumentacją Rozpoczynanie pracy z uwierzytelnianiem i dokumentacją instrukcji zestawu SDK serwera.

Uwierzytelnianie: Przepływ serwera

Poniższy kod uruchamia proces logowania przepływu serwera przy użyciu dostawcy Google. Wymagana jest dodatkowa konfiguracja ze względu na wymagania dotyczące zabezpieczeń dostawcy Google:

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

Ponadto dodaj następującą metodę do głównej klasy Activity:

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

Zdefiniowana GOOGLE_LOGIN_REQUEST_CODE w głównym działaniu jest używana dla login() metody i w metodzie onActivityResult() . Można wybrać dowolną unikatową liczbę, o ile ta sama liczba jest używana w metodzie login() i metodzie onActivityResult() . W przypadku abstrakcji kodu klienta do karty usługi (jak pokazano wcześniej), należy wywołać odpowiednie metody na karcie usługi.

Należy również skonfigurować projekt dla kart niestandardowych. Najpierw określ adres URL przekierowania. Dodaj następujący fragment kodu do AndroidManifest.xmlpliku :

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

Dodaj element redirectUriScheme do build.gradle pliku dla aplikacji:

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

Na koniec dodaj com.android.support:customtabs:28.0.0 do listy zależności w build.gradle pliku:

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

Uzyskaj identyfikator zalogowanego użytkownika z klasy MobileServiceUser przy użyciu metody getUserUser . Aby zapoznać się z przykładem używania usługi Futures do wywoływania asynchronicznych interfejsów API logowania, zobacz Wprowadzenie do uwierzytelniania.

Ostrzeżenie

Wymieniony schemat adresów URL uwzględnia wielkość liter. Upewnij się, że wszystkie wystąpienia {url_scheme_of_you_app} wielkości liter.

Tokeny uwierzytelniania pamięci podręcznej

Buforowanie tokenów uwierzytelniania wymaga przechowywania identyfikatora użytkownika i tokenu uwierzytelniania lokalnie na urządzeniu. Przy następnym uruchomieniu aplikacji sprawdzisz pamięć podręczną i jeśli te wartości są obecne, możesz pominąć procedurę logowania i przywrócić klienta przy użyciu tych danych. Jednak te dane są poufne i powinny być przechowywane zaszyfrowane w celu bezpieczeństwa w przypadku kradzieży telefonu. Pełny przykład sposobu buforowania tokenów uwierzytelniania można znaleźć w sekcji Tokeny uwierzytelniania pamięci podręcznej.

Podczas próby użycia wygasłego tokenu otrzymasz odpowiedź 401 nieautoryzowaną . Błędy uwierzytelniania można obsługiwać przy użyciu filtrów. Filtry przechwytuje żądania do zaplecza usługi App Service. Kod filtru testuje odpowiedź 401, wyzwala proces logowania, a następnie wznawia żądanie, które wygenerowało 401.

Używanie tokenów odświeżania

Token zwracany przez uwierzytelnianie usługi aplikacja systemu Azure i autoryzacja ma zdefiniowany czas życia o jedną godzinę. Po upływie tego okresu należy ponownie uwierzytelnić użytkownika. Jeśli używasz długotrwałego tokenu otrzymanego za pośrednictwem uwierzytelniania przepływu klienta, możesz ponownie uwierzytelnić się przy użyciu uwierzytelniania usługi aplikacja systemu Azure i autoryzacji przy użyciu tego samego tokenu. Inny token usługi aplikacja systemu Azure jest generowany przy użyciu nowego okresu istnienia.

Możesz również zarejestrować dostawcę, aby użyć tokenów odświeżania. Token odświeżania nie zawsze jest dostępny. Wymagana jest dodatkowa konfiguracja:

  • W przypadku usługi Azure Active Directory skonfiguruj wpis tajny klienta dla aplikacji usługi Azure Active Directory. Określ klucz tajny klienta w usłudze aplikacja systemu Azure podczas konfigurowania uwierzytelniania usługi Azure Active Directory. Podczas wywoływania parametru .login()przekaż response_type=code id_token jako parametr:

    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);
    
  • W przypadku usługi Google przekaż parametr access_type=offline jako:

    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);
    
  • W polu Konto Microsoft wybierz wl.offline_access zakres.

Aby odświeżyć token, wywołaj metodę .refreshUser():

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

Najlepszym rozwiązaniem jest utworzenie filtru, który wykrywa odpowiedź 401 z serwera i próbuje odświeżyć token użytkownika.

Logowanie przy użyciu uwierzytelniania przepływu klienta

Ogólny proces logowania przy użyciu uwierzytelniania przepływu klienta jest następujący:

  • Skonfiguruj uwierzytelnianie i autoryzację usługi aplikacja systemu Azure tak, jak w przypadku uwierzytelniania przepływu serwera.

  • Zintegruj zestaw SDK dostawcy uwierzytelniania na potrzeby uwierzytelniania, aby utworzyć token dostępu.

  • Wywołaj metodę w .login() następujący sposób (result powinna mieć AuthenticationResultwartość ):

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

Zobacz kompletny przykład kodu w następnej sekcji.

Zastąp metodę onSuccess() kodem, którego chcesz użyć podczas pomyślnego logowania. Ciąg {provider} jest prawidłowym dostawcą: aad (Azure Active Directory), facebook, google, microsoftaccount lub twitter. Jeśli zaimplementowano uwierzytelnianie niestandardowe, możesz również użyć niestandardowego tagu dostawcy uwierzytelniania.

Uwierzytelnianie użytkowników za pomocą biblioteki uwierzytelniania usługi Active Directory (ADAL)

Bibliotekę uwierzytelniania usługi Active Directory (ADAL) można użyć do logowania użytkowników do aplikacji przy użyciu usługi Azure Active Directory. Korzystanie z logowania przepływu klienta jest często preferowane przy użyciu loginAsync() metod, ponieważ zapewnia bardziej natywny sposób działania środowiska użytkownika i umożliwia dodatkowe dostosowanie.

  1. Skonfiguruj zaplecze aplikacji mobilnej na potrzeby logowania do usługi AAD, postępując zgodnie z samouczkiem How to configure App Service for Active Directory login (Jak skonfigurować usługę App Service na potrzeby logowania do usługi Active Directory). Pamiętaj, aby wykonać opcjonalny krok rejestrowania natywnej aplikacji klienckiej.

  2. Zainstaluj bibliotekę ADAL, modyfikując plik build.gradle w celu uwzględnienia następujących definicji:

    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. Dodaj następujący kod do aplikacji, wykonując następujące zamiany:

    • Zastąp ciąg INSERT-AUTHORITY-HERE nazwą dzierżawy, w której aprowizowana aplikacja została aprowizowana. Format powinien mieć wartość https://login.microsoftonline.com/contoso.onmicrosoft.com.
    • Zastąp ciąg INSERT-RESOURCE-ID-HERE identyfikatorem klienta zaplecza aplikacji mobilnej. Identyfikator klienta można uzyskać z karty Zaawansowane w obszarze Ustawienia usługi Azure Active Directory w portalu.
    • Zastąp ciąg INSERT-CLIENT-ID-HERE identyfikatorem klienta skopiowanym z natywnej aplikacji klienckiej.
    • Zastąp ciąg INSERT-REDIRECT-URI-HERE punktem końcowym /.auth/login/done witryny przy użyciu schematu HTTPS. Ta wartość powinna być podobna do 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);
    }
}

Dostosowywanie komunikacji klient-serwer

Połączenie klienta jest zwykle podstawowym połączeniem HTTP przy użyciu podstawowej biblioteki HTTP dostarczonej z zestawem Android SDK. Istnieje kilka powodów, dla których warto to zmienić:

  • Aby dostosować limity czasu, chcesz użyć alternatywnej biblioteki HTTP.
  • Chcesz podać pasek postępu.
  • Chcesz dodać nagłówek niestandardowy do obsługi funkcji zarządzania interfejsem API.
  • Chcesz przechwycić niepomyślną odpowiedź, aby można było zaimplementować ponowne uwierzytelnianie.
  • Chcesz rejestrować żądania zaplecza do usługi analitycznej.

Używanie alternatywnej biblioteki HTTP

Wywołaj metodę natychmiast po utworzeniu .setAndroidHttpClientFactory() odwołania klienta. Aby na przykład ustawić limit czasu połączenia na 60 sekund (zamiast domyślnego 10 sekund):

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

Implementowanie filtru postępu

Przechwycenie każdego żądania można zaimplementować, implementując element ServiceFilter. Na przykład następujące aktualizacje wstępnie utworzonego paska postępu:

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

Ten filtr można dołączyć do klienta w następujący sposób:

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

Dostosowywanie nagłówków żądań

Użyj następującego ServiceFilter polecenia i dołącz filtr w taki sam sposób jak :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);
        }
    }
}

Konfigurowanie automatycznej serializacji

Można określić strategię konwersji, która ma zastosowanie do każdej kolumny przy użyciu interfejsu API gson . Biblioteka kliencka systemu Android używa formatu gson w tle do serializacji obiektów Java do danych JSON przed wysłaniem danych do usługi aplikacja systemu Azure Service. Poniższy kod używa metody setFieldNamingStrategy() w celu ustawienia strategii. Ten przykład spowoduje usunięcie początkowego znaku (m), a następnie małe litery następnego znaku dla każdej nazwy pola. Na przykład zamieniłaby wartość "mId" na "id". Zaimplementuj strategię konwersji, aby zmniejszyć potrzebę SerializedName() adnotacji w większości pól.

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

Ten kod należy wykonać przed utworzeniem odwołania klienta mobilnego przy użyciu elementu MobileServiceClient.