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

W tym przewodniku pokazano, jak używać zestawu SDK klienta systemu Android Mobile Apps w celu zaimplementowania typowych scenariuszy, takich jak:

  • Wykonywanie zapytań o dane (wstawianie, aktualizowanie i usuwanie).
  • Uwierzytelnianie.
  • Obsługa błędów.
  • Dostosowywanie klienta.

Ten przewodnik koncentruje się na Android SDK. Aby dowiedzieć się więcej o zestawach SDK po stronie serwera dla usługi Mobile Apps, zobacz Praca z zestawem SDK zaplecza platformy .NET lub Jak używać zestawu SDK Node.js zaplecza.

Dokumentacja referencyjna

Dokumentacja interfejsu API javadocs dla biblioteki klienta systemu Android jest GitHub.

Obsługiwane platformy

Zestaw SDK usługi Azure Mobile Apps dla systemu Android obsługuje poziomy interfejsu API od 19 do 24 (KitKat do Nougat) dla telefonów i tabletów. W szczególności uwierzytelnianie korzysta ze wspólnego podejścia do struktury sieci Web w celu zbierania poświadczeń. Uwierzytelnianie przepływu serwera nie działa w przypadku urządzeń o małych kształtach, takich jak zegarki.

Instalacja i wymagania wstępne

Ukończ samouczek Mobile Apps Szybki start. To zadanie zapewnia, że zostały spełnione wszystkie wymagania wstępne dotyczące tworzenia Mobile Apps Azure. 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 gradle

Zmień oba pliki build.gradle :

  1. Dodaj ten kod do Project poziomiekompilacji.gradle:

    buildscript {
        repositories {
            jcenter()
            google()
        }
    }
    
    allprojects {
        repositories {
            jcenter()
            google()
        }
    }
    
  2. Dodaj ten kod do plikubuild.gradle na poziomie aplikacji modułu wewnątrz tagu dependencies :

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

    Obecnie najnowsza wersja to 3.4.0. Obsługiwane wersje są wymienione na stronie bintray.

Włączanie uprawnień do Internetu

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

<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łaj niestandardowe interfejsy API napisane przy użyciu zestawu SDK usługi Azure Mobile Apps Server.
  • Uwierzytelnianie za pomocą Azure App Service uwierzytelniania i autoryzacji.
  • Rejestracja powiadomień wypychanych za pomocą Notification Hubs.

Każda z tych funkcji najpierw wymaga utworzenia MobileServiceClient obiektu . W kliencie MobileServiceClient mobilnym powinien zostać utworzony tylko jeden obiekt (to oznacza, że powinien to być wzorzec singleton). 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, który wskazuje na twoje mobilne zaplecza. Jeśli używasz usługi Azure App Service do hostować zaplecza mobilnego, upewnij się, że używasz bezpiecznej https:// wersji adresu URL.

Klient wymaga również dostępu do działania lub kontekstu — parametru this w przykładzie. Konstrukcja MobileServiceClient powinna odbywać się onCreate() w ramach metody działania przywołynego w pliku AndroidManifest.xml .

Najlepszym rozwiązaniem jest abstrakcję komunikacji z serwerem do własnej klasy (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ć metodę AzureServiceAdapter.Initialize(this); w onCreate() metodzie działania głównego. Wszystkie inne metody, które wymagają dostępu do klienta, używają AzureServiceAdapter.getInstance(); metody w celu uzyskania odwołania do karty usługi.

Operacje na danych

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

Definiowanie klas danych klienta

Aby uzyskać dostęp do danych z Usługi SQL Azure, zdefiniuj klasy danych klienta, które odpowiadają tabelom w za zaplecza aplikacji mobilnej. Przykłady w tym temacie zakładają tabelę o nazwie MyDataTable, która zawiera następujące kolumny:

  • identyfikator
  • tekst
  • kończenie

Odpowiedni typizowany 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 pola, które dodajesz. 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) ma kolumnę Priorytet w postaci liczby całkowitej, możesz dodać to pole wraz z metodami getter i 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;
}

Aby dowiedzieć się, jak utworzyć dodatkowe tabele w za zaplecza usługi Mobile Apps, zobacz How to: Define a table controller (.NET backend) (Jak zdefiniować kontroler tabel (zaplecza .NET) lub Define Tables using a Dynamic Schema (Node.js backend) (Jak definiować tabele przy użyciu schematu dynamicznego Node.js zaplecza).

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

  • String id: unikatowy identyfikator rekordu w skali globalnej. Najlepszym rozwiązaniem jest, aby identyfikator był reprezentacją 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 reprezentowany jako ciąg, wersja jest również ustawiana przez serwer.
  • boolean deleted: wskazuje, że rekord został usunięty, ale nie został jeszcze przeczyszony. Nie należy używać deleted jako właściwości w klasie.

Pole id jest wymagane. Pole updatedAt i pole są version używane do synchronizacji w trybie offline (odpowiednio do synchronizacji przyrostowej i rozwiązywania konfliktów). Pole createdAt jest polem odwołania i nie jest używane przez klienta. Nazwy właściwości są nazwami "w całej sieci" i nie można ich dostosowywać. Można jednak utworzyć mapowanie między obiektem a nazwami "w sieci" 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 mClient jest odwołaniem do obiektu MobileServiceClient. Pierwsze przeciążenie jest używane, gdy nazwa klasy i nazwa tabeli są takie same i są 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 to nazwa tabeli.

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

Wykonywanie zapytań w tabeli zaplecza

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

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

Filtrowanie wyników

Ogólna forma zapytania jest:

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 za zaplecza. Zapytanie jest konwertowane na zapytanie OData v3 przed przekazaniem do Mobile Apps zaplecza. Po otrzymaniu Mobile Apps zaplecza konwertuje zapytanie na SQL przed wykonaniem go w Usługi SQL Azure danych. Ponieważ aktywność sieciowa zajmuje trochę czasu, .execute() metoda zwraca wartość ListenableFuture<E>.

Filtrowanie zwracanych danych

Poniższe wykonanie zapytania zwraca wszystkie elementy z tabeli ToDoItem , gdzie wartość completejest równa false.

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

mToDoTable to odwołanie do utworzonej wcześniej tabeli usług mobilnych.

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 to eq (równa się), ne (nie równa się), gt (większe niż), ge (większe niż lub równe), lt (mniejsze niż), le (mniejsze niż lub równe). Te metody umożliwiają porównanie pól liczb i ciągów z określonymi wartościami.

Możesz filtrować według dat. Następujące 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 płatności 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 rozpoczyna 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, div,mod, floor, ceiling i round. Poniższy przykład filtruje wiersze tabeli, w których czas trwania jest liczbą równomierną.

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

Predykaty można łączyć z tymi metodami logicznymi: i, lub , i 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żanie:

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 Exploring the richness of the Android client query model (Eksplorowanie bogatych możliwości modelu zapytań klienta systemu Android).

Sortowanie zwracanych 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, według którego ma być sortowane. Drugi parametr używa wyliczenia QueryOrder , aby określić, czy sortować rosnąco, czy malejąco. W przypadku filtrowania 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 pola pełnei tekstowe. 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 mają zostać zwrócone. Metoda select musi być stosowana zgodnie z metodami, np. where i orderBy. Po nim można na przykład utworzyć metody stronicowania , takie jak skip i top.

Zwracanie danych na stronach

Dane są ZAWSZE zwracane na stronach. Maksymalna liczba zwracanych rekordów jest ustawiana przez serwer. Jeśli klient zażąda większej liczby rekordów, serwer zwróci 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 pięć pierwszych elementów, a następnie zwraca pięć następnych:

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

Jeśli chcesz pobrać wszystkie rekordy w tabeli, zaim implementuj kod iteracyjny po 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 przy użyciu tej metody tworzy co najmniej dwa żądania do Mobile Apps zaplecza.

Porada

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

How to: Concatenate query methods (3.01.2. 2017:

Metody używane do wykonywania zapytań w tabelach zaplecza można zeskontać. Metody zapytań łańcuchowych umożliwiają wybieranie określonych kolumn filtrowanych wierszy, które są sortowane i stronicowane. Można tworzyć złożone filtry logiczne. Każda metoda zapytania 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. Metody filtrowania (gdzie).
  2. Metody sortowania (orderBy).
  3. Metody wyboru (wybierz).
  4. Metody stronicowania (pomijaniai górnego).

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 dwie karty ze sobą.

W naszym przykładowym kodzie zwracamy dane z tabeli Mobile Apps Usługi SQL Azure ToDoItem do tablicy. To działanie jest wspólnym wzorcem dla aplikacji danych. Zapytania bazy danych często zwracają kolekcję wierszy, które klient pobiera z listy 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. Oba są powiązane z adapterem, który w tym kodzie jest rozszerzeniem klasy ArrayAdapterToDoItem<>.

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 element ListView , który chcemy wypełnić 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, a jego wystąpienia są poszczególnych elementów na liście poszczególnych wystąpień. 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 row_list_to_do.xml pliku.

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

Ponieważ źródło danych naszego widoku jest tablicą ToDoItem, podklasa naszej karty jest podklasą klasy ArrayAdapterToDoItem<>. Ta podklasa tworzy widok dla każdego toDoItem przy użyciu row_list_to_do układu. W naszym kodzie definiujemy następującą klasę, która jest rozszerzeniem klasy ArrayAdapterE<>:

public class ToDoItemAdapter extends ArrayAdapter<ToDoItem> {
}

Zastąp metodę getView kart . 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;
    }

W naszym działaniu utworzymy wystąpienie tej klasy 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ąpienia listView i przypisać adapter do ListView.

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

Wiązanie z interfejsem użytkownika za pomocą adaptera

Teraz możesz używać 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 modyfikujesz tabelę ToDoItem . Ponieważ modyfikacje są wykonywane na podstawie rekordów, obsługujesz pojedynczy wiersz zamiast kolekcji. Podczas wstawiania elementu wywołaj metodę add na karcie. Podczas usuwania wywołaj metodę remove .

Pełny przykład można znaleźć w przewodniku Szybki start dla systemu Android Project.

Wstawianie danych do zaplecza

Wystąpienie klasy ToDoItem i ustawienie jego właściwości.

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

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

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

Zwrócona jednostka dopasowuje dane wstawione do tabeli zaplecza, uwzględnia identyfikator i inne wartości (createdAttakie jak pola , updatedAtversion i ) ustawione na za zaplecza.

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, zaplecza generuje nowy identyfikator GUID.

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

  • Identyfikatory mogą być generowane bez konieczności rundy w bazie danych.
  • Rekordy można łatwiej scalać z różnych tabel lub baz danych.
  • Wartości identyfikatorów lepiej integrują się z logiką aplikacji.

Wartości identyfikatorów ciągów 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 w nim w w nim w w jakichś zmianach. Wiersz o tym samym identyfikatorze zostanie zaktualizowany.

Usuwanie danych w aplikacji mobilnej

Poniższy kod pokazuje, jak usunąć dane z tabeli przez określenie obiektu danych.

mToDoTable
    .delete(item);

Możesz również usunąć element, określając pole id w wierszu do usunięcia.

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

Wyszukiwania określonego elementu według identyfikatora

Poszukaj elementu z określonym polem identyfikatora za pomocą metody lookUp( ):

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

How to: Work with untyped data (Jak pracować z nietypami danych)

Nietypowany model programowania zapewnia dokładną kontrolę nad serializacją 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 wystarczy odwołać się tylko do podzestawu kolumn. Model typowany wymaga zdefiniowania wszystkich kolumn zdefiniowanych w Mobile Apps zaplecza w klasie danych. Większość wywołań interfejsu API w celu uzyskania dostępu do danych jest podobna do wywołań programowania typistycznego. Główna różnica polega na tym, że w nietypowym modelu metody są wywoływane dla obiektu MobileServiceJsonTable , a nie obiektu MobileServiceTable .

Tworzenie wystąpienia tabeli o nietypach

Podobnie jak w przypadku modelu typowego, zaczynasz 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 ma ona praktycznie ten sam interfejs API, który jest dostępny w przypadku typistycznego modelu programowania. W niektórych przypadkach metody mają nietypowany parametr zamiast parametru typowego.

Wstawianie do nietypowej tabeli

Poniższy kod pokazuje, jak wykonać wstawianie. Pierwszym krokiem jest utworzenie projektu 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 funkcji insert(), aby wstawić nietypowany 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 wcześniejszym przykładzie wstawiania . Kod jest taki sam jak w przypadku wpisywania, 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 z typami, jest dostępny 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 lokalnego przechowywania kopii danych serwera. Operacje wykonywane na tabeli w trybie offline nie wymagają łączności mobilnej. Synchronizacja w trybie offline pomaga zwiększyć odporność i wydajność kosztem bardziej złożonej logiki rozwiązywania konfliktów. Zestaw SDK Mobile Apps Platformy Azure implementuje następujące funkcje:

  • Synchronizacja przyrostowa: pobierane są tylko zaktualizowane i nowe rekordy, co oszczędza przepustowość i zużycie pamięci.
  • Optymistyczna współbieżność: Zakłada się, że operacje zakończyły się pomyślnie. Rozwiązanie konfliktów jest odroczone do momentu wykonania aktualizacji na serwerze.
  • Rozwiązanie konfliktów: zestaw SDK wykrywa, kiedy na serwerze wdano zmianę w konflikcie, i udostępnia wpięcie w celu powiadomienia użytkownika.
  • Usuwanie nie softowe: usunięte rekordy są oznaczane jako usunięte, dzięki czemu inne urządzenia mogą aktualizować swoją pamięć podręczną w trybie offline.

Inicjowanie synchronizacji w trybie offline

Przed użyciem każda tabela w trybie offline musi być zdefiniowana 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 należy użyć ..getTable() W przypadku tabeli w trybie offline użyj funkcji .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 przypadku tabel online i offline.

Synchronizowanie lokalnej pamięci podręcznej trybu offline

Synchronizacja jest pod kontrolą 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 metoda poda .pull(query, queryname) nazwę zapytania, synchronizacja przyrostowa jest używana do zwracania tylko rekordów, które zostały utworzone lub zmienione od czasu ostatniego pomyślnego ściągnięcie.

Obsługa konfliktów podczas synchronizacji w trybie offline

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

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

Gdy wszystkie konflikty zostaną oznaczone zgodnie z chęć, wywołaj .push() ponownie wywołanie , aby rozwiązać wszystkie konflikty.

Wywołanie niestandardowego interfejsu API

Niestandardowy interfejs API umożliwia definiowanie niestandardowych punktów końcowych, które uwidoczniają funkcje serwera, które nie są mapowe na operację wstawiania, aktualizowania, usuwania ani odczytywania. Za pomocą niestandardowego interfejsu API można mieć większą kontrolę nad komunikatami, w tym odczytywanie i ustawianie nagłówków komunikatów HTTP oraz definiowanie formatu treści komunikatu innego niż JSON.

Z klienta systemu Android należy wywołać metodę invokeApi w celu wywołania niestandardowego punktu końcowego 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 zwrócony przez niestandardowy interfejs API jest wyświetlany w oknie dialogowym komunikatu, podobnie jak wszystkie błędy. Inne wersje metody invokeApi pozwalają opcjonalnie wysłać obiekt w treści żądania, określić metodę HTTP i wysłać parametry zapytania wraz z żądaniem. Dostępne są również nietypowane wersje metody invokeApi .

Dodawanie uwierzytelniania do aplikacji

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

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 do uwierzytelnionych użytkowników. Możesz również użyć tożsamości uwierzytelnionych użytkowników, aby zaimplementować reguły autoryzacji w za zaplecza.

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 specyficznym dla urządzenia, takimi jak logowanie pojedyncze, ponieważ opiera się na zestawach SDK dostarczonych przez dostawcę tożsamości. Na przykład możesz zintegrować zestaw SDK serwisu Facebook z aplikacją mobilną. Klient mobilny zamienia się z aplikacją facebookową i potwierdza logowanie przed zamianą z powrotem na aplikację mobilną.

Aby włączyć uwierzytelnianie w aplikacji, należy wykonać cztery kroki:

  • Zarejestruj aplikację do uwierzytelniania u dostawcy tożsamości.
  • Skonfiguruj App Service zaplecza.
  • Ogranicz uprawnienia tabeli do uwierzytelnionych użytkowników tylko w App Service zaplecza.
  • Dodaj kod uwierzytelniania do aplikacji.

Możesz ustawić uprawnienia do tabel, aby ograniczyć dostęp do określonych operacji tylko do uwierzytelnionych użytkowników. Możesz również użyć identyfikatora SID uwierzytelnionego użytkownika, aby zmodyfikować żądania. Aby uzyskać więcej informacji, zapoznaj się Wprowadzenie z uwierzytelnianiem i dokumentacją z uwierzytelnianiem zestawu SDK serwera.

Uwierzytelnianie: ustawienia Flow

Poniższy kod rozpoczyna proces logowania do 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");
            }
        }
    }
}

Element GOOGLE_LOGIN_REQUEST_CODE zdefiniowany w głównym działaniu jest używany dla login() metody i metody onActivityResult() . Możesz wybrać dowolną unikatową liczbę, login() o ile w metodzie i metodzie jest używana ta onActivityResult() sama liczba. 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.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>

Dodaj redirectUriScheme do build.gradle pliku 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 użytkownika MobileServiceUser przy użyciu metody getUserId . Aby uzyskać przykład sposobu używania funkcji Futures do wywołania asynchronicznych interfejsów API logowania, zobacz Wprowadzenie z uwierzytelnianiem.

Ostrzeżenie

W schemacie adresu URL, o którym wspomniano, jest zróżnicowana wielkość liter. Upewnij się, że wszystkie wystąpienia przypadków {url_scheme_of_you_app} dopasowania.

Tokeny uwierzytelniania pamięci podręcznej

Buforowanie tokeny uwierzytelniania wymagają przechowywania identyfikatora użytkownika i tokenu uwierzytelniania lokalnie na urządzeniu. Podczas następnego uruchamiania aplikacji sprawdzasz pamięć podręczną, a jeśli te wartości są obecne, możesz pominąć procedurę logowania i ponownie zasyłać klienta tymi danymi. Jednak te dane są poufne i powinny być przechowywane w postaci zaszyfrowanej w celu zapewnienia bezpieczeństwa w przypadku kradzieży telefonu. Pełny przykład 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 brak autoryzacji . Błędy uwierzytelniania można obsługiwać przy użyciu filtrów. Filtry przechwytają żądania do App Service zaplecza. Kod filtru testuje odpowiedź dla błędu 401, wyzwala proces logowania, a następnie wznawia żądanie, które wygenerował kod 401.

Używanie tokenów odświeżania

Token zwrócony przez usługę Azure App Service uwierzytelnianie i autoryzacja ma zdefiniowany czas życia na jedną godzinę. Po tym czasie 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 i autoryzacji usługi Azure App Service przy użyciu tego samego tokenu. Inny Azure App Service jest generowany z nowym okresem istnienia.

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

  • Na Azure Active Directory należy skonfigurować klucz tajny klienta dla Azure Active Directory aplikacji. Określ klucz tajny klienta w Azure App Service podczas konfigurowania Azure Active Directory uwierzytelniania. Podczas wywoływania .login()funkcji 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ż access_type=offline parametr jako parametr:

    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 przypadku konta Microsoft wybierz wl.offline_access zakres.

Aby odświeżyć token, wywołaj :.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 Azure App Service uwierzytelnianie i autoryzację, tak jak w przypadku uwierzytelniania przepływu serwera.

  • Zintegruj zestaw SDK dostawcy uwierzytelniania na celu uwierzytelnianie w celu uzyskania tokenu dostępu.

  • Wywołaj .login() metodę w następujący sposób (result powinien być 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");
        }
    });
    

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

Zastąp metodę onSuccess() kodem, który 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żna również użyć niestandardowego tagu dostawcy uwierzytelniania.

Uwierzytelnianie użytkowników przy użyciu Active Directory Authentication Library (ADAL)

Możesz użyć tej Active Directory Authentication Library (ADAL), aby zalogować użytkowników do aplikacji przy użyciu Azure Active Directory. Użycie identyfikatora logowania przepływu klienta jest często preferowane loginAsync() w przypadku korzystania z metod , ponieważ zapewnia bardziej natywny interfejs użytkownika i umożliwia dodatkowe dostosowywanie.

  1. Skonfiguruj zaplecza aplikacji mobilnej na AAD logowania, korzystając z samouczka How to configure App Service for Active Directory login (Jak skonfigurować logowanie do usługi Active Directory). Pamiętaj, aby wykonać opcjonalny krok rejestrowania natywnej aplikacji klienckiej.

  2. Zainstaluj ADAL, modyfikując plik build.gradle tak, aby zawierał następujące definicje:

    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, zastępując następujące elementy:

    • Zastąp pozycję INSERT-AUTHORITY-HERE nazwą dzierżawy, w której aprowizowana jest aplikacja. Format powinien być .https://login.microsoftonline.com/contoso.onmicrosoft.com
    • Zastąp pozycję INSERT-RESOURCE-ID-HERE identyfikatorem klienta zaplecza aplikacji mobilnej. Identyfikator klienta można uzyskać na karcie Zaawansowane w obszarze Azure Active Directory Ustawienia portalu.
    • Zastąp pozycję INSERT-CLIENT-ID-HERE identyfikatorem klienta skopiowanym z natywnej aplikacji klienckiej.
    • Zastąp pozycję 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 Client-Server danych

Połączenie klienta jest zwykle podstawowym połączeniem HTTP korzystającym z podstawowej biblioteki HTTP dostarczonej z 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 w celu obsługi funkcji api management.
  • Chcesz przechwycić odpowiedź, która zakończyła się niepowodzeniem, aby można było zaimplementować ponowne uwierzytelnianie.
  • Chcesz rejestrować żądania zaplecza do usługi analizy.

Używanie alternatywnej biblioteki HTTP

Wywołaj .setAndroidHttpClientFactory() metodę natychmiast po utworzeniu odwołania do klienta. Na przykład, aby ustawić limit czasu połączenia na 60 sekund (zamiast domyślnych 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

Przecięcie każdego żądania można zaimplementować, implementując element ServiceFilter. Na przykład następujące aktualizacje mają wstępnie utworzony pasek 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 sposobu ServiceFilter i dołącz filtr w taki sam sposób, jak w przypadku funkcji 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żesz określić strategię konwersji, która ma zastosowanie do każdej kolumny, przy użyciu interfejsu API gson . Biblioteka klienta systemu Android używa gson w tle do serializacji obiektów Java do danych JSON przed ich wysłaniem do Azure App Service. Poniższy kod używa metody setFieldNamingStrategy() do ustawienia strategii. W tym przykładzie zostanie usunięty znak początkowy ("m"), a następnie małe i małe i następne znaki dla każdej nazwy pola. Na przykład może przekształcić "mId" w "id". Zaim implementuj strategię konwersji, aby ograniczyć SerializedName() potrzebę 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 dla urządzeń przenośnych przy użyciu klienta MobileServiceClient.