كيفية استخدام Azure Mobile Apps SDK لنظام التشغيل Android

يوضح لك هذا الدليل كيفية استخدام SDK لعميل Android لتطبيقات الأجهزة المحمولة لتنفيذ سيناريوهات شائعة، مثل:

  • الاستعلام عن البيانات (إدراج وتحديث وحذف).
  • المصادقة.
  • معالجة الأخطاء.
  • تخصيص العميل.

يركز هذا الدليل على Android SDK من جانب العميل. لمعرفة المزيد حول SDKs من جانب الخادم لتطبيقات الأجهزة المحمولة، راجع العمل مع .NET backend SDK أو كيفية استخدام Node.js backend SDK.

الوثائق المرجعية

يمكنك العثور على مرجع Javadocs API لمكتبة عميل Android على GitHub.

الأنظمة الأساسية المدعومة

يدعم Azure Mobile Apps SDK for Android مستويات واجهة برمجة التطبيقات من 19 إلى 24 (KitKat من خلال Nougat) لعوامل نموذج الهاتف والكمبيوتر اللوحي. تستخدم المصادقة، على وجه الخصوص، نهجا مشتركا لإطار عمل الويب لجمع بيانات الاعتماد. لا تعمل مصادقة تدفق الخادم مع أجهزة عامل النموذج الصغيرة مثل الساعات.

الإعداد والمتطلبات الأساسية

أكمل البرنامج التعليمي للتشغيل السريع لتطبيقات الأجهزة المحمولة. تضمن هذه المهمة استيفاء جميع المتطلبات المسبقة لتطوير تطبيقات Azure Mobile. يساعدك التشغيل السريع أيضا على تكوين حسابك وإنشاء أول خلفية لتطبيق الأجهزة المحمولة.

إذا قررت عدم إكمال البرنامج التعليمي للتشغيل السريع، فأكمل المهام التالية:

تحديث ملف إنشاء Gradle

تغيير كل من ملفات build.gradle :

  1. أضف هذه التعليمة البرمجية إلى ملف Project level build.gradle :

    buildscript {
        repositories {
            jcenter()
            google()
        }
    }
    
    allprojects {
        repositories {
            jcenter()
            google()
        }
    }
    
  2. أضف هذه التعليمة البرمجية إلى ملف build.gradle على مستوى تطبيق الوحدة النمطية داخل علامة التبعيات:

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

    حاليا أحدث إصدار هو 3.4.0. يتم سرد الإصدارات المدعومة على bintray.

تمكين إذن الإنترنت

للوصول إلى Azure، يجب تمكين إذن الإنترنت لتطبيقك. إذا لم يتم تمكينه بالفعل، أضف السطر التالي من التعليمات البرمجية إلى ملف AndroidManifest.xml الخاص بك:

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

إنشاء اتصال عميل

توفر تطبيقات Azure Mobile أربع وظائف لتطبيق الجوال الخاص بك:

  • الوصول إلى البيانات والمزامنة دون اتصال مع خدمة تطبيقات الأجهزة المحمولة Azure.
  • استدعاء واجهات برمجة التطبيقات المخصصة المكتوبة باستخدام Azure Mobile Apps Server SDK.
  • المصادقة باستخدام Azure App Service Authentication and Authorization.
  • دفع تسجيل الإعلامات باستخدام مراكز الإعلامات.

تتطلب كل من هذه الدالات أولا إنشاء كائن MobileServiceClient . يجب إنشاء كائن واحد MobileServiceClient فقط داخل عميل الجوال الخاص بك (أي، يجب أن يكون نمط Singleton). لإنشاء كائن MobileServiceClient :

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

<MobileAppUrl> هو إما سلسلة أو عنصر URL يشير إلى الخلفية لهاتفك المحمول. إذا كنت تستخدم Azure App Service لاستضافة الخلفية للأجهزة المحمولة، فتأكد من استخدام الإصدار الآمن https:// من عنوان URL.

يتطلب العميل أيضا الوصول إلى النشاط أو السياق - المعلمة this في المثال. يجب أن يحدث إنشاء MobileServiceClient ضمن onCreate() أسلوب النشاط المشار إليه في AndroidManifest.xml الملف.

كأفضل ممارسة، يجب عليك تجريد اتصال الخادم في فئة (نمط مفرد) الخاصة به. في هذه الحالة، يجب تمرير النشاط داخل الدالة الإنشائية لتكوين الخدمة بشكل مناسب. على سبيل المثال:

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

يمكنك الآن استدعاء AzureServiceAdapter.Initialize(this); onCreate() أسلوب نشاطك الرئيسي. تستخدم AzureServiceAdapter.getInstance(); أي أساليب أخرى تحتاج إلى الوصول إلى العميل للحصول على مرجع إلى محول الخدمة.

عمليات البيانات

يتمثل جوهر Azure Mobile Apps SDK في توفير الوصول إلى البيانات المخزنة داخل SQL Azure على الواجهة الخلفية لتطبيق الأجهزة المحمولة. يمكنك الوصول إلى هذه البيانات باستخدام فئات مكتوبة بقوة (مفضلة) أو استعلامات غير مكتوبة (غير مستحسن). يتعامل الجزء الأكبر من هذا القسم مع استخدام فئات مكتوبة بقوة.

تعريف فئات بيانات العميل

للوصول إلى البيانات من جداول SQL Azure، حدد فئات بيانات العميل التي تتوافق مع الجداول في الواجهة الخلفية لتطبيق الأجهزة المحمولة. تفترض الأمثلة في هذا الموضوع جدولا يسمى MyDataTable، يحتوي على الأعمدة التالية:

  • المعرف
  • النص
  • كامل

يوجد الكائن المطابق من جانب العميل الذي تم كتابته في ملف يسمى MyDataTable.java:

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

أضف أساليب getter و setter لكل حقل تضيفه. إذا كان جدول SQL Azure يحتوي على المزيد من الأعمدة، يمكنك إضافة الحقول المقابلة إلى هذه الفئة. على سبيل المثال، إذا كان DTO (كائن نقل البيانات) يحتوي على عمود أولوية عدد صحيح، فيمكنك إضافة هذا الحقل، جنبا إلى جنب مع أساليب المحصلة والمضبط الخاصة به:

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

لمعرفة كيفية إنشاء جداول إضافية في الخلفية لتطبيقات الأجهزة المحمولة، راجع كيفية: تعريف وحدة تحكم جدول (.NET الخلفية) أو تعريف الجداول باستخدام مخطط ديناميكي (Node.js الخلفية).

يحدد الجدول الخلفي ل Azure Mobile Apps خمسة حقول خاصة، أربعة منها متاحة للعملاء:

  • String id: المعرف الفريد عالميا للسجل. كأفضل ممارسة، اجعل المعرف تمثيل السلسلة لكائن UUID .
  • DateTimeOffset updatedAt: تاريخ/وقت التحديث الأخير. يتم تعيين الحقل updatedAt بواسطة الخادم ويجب ألا يتم تعيينه أبدا بواسطة التعليمات البرمجية للعميل.
  • DateTimeOffset createdAt: التاريخ/الوقت الذي تم فيه إنشاء الكائن. يتم تعيين الحقل createdAt بواسطة الخادم ويجب ألا يتم تعيينه أبدا بواسطة التعليمات البرمجية للعميل.
  • byte[] version: يتم تمثيله عادة كسلسلة، يتم تعيين الإصدار أيضا بواسطة الخادم.
  • boolean deleted: يشير إلى أنه تم حذف السجل ولكن لم يتم إزالته بعد. لا تستخدم deleted كخاصية في الفئة الخاصة بك.

id الحقل مطلوب. updatedAt يتم استخدام الحقل و version الحقل للمزامنة دون اتصال (للمزامنة المتزايدة وحل التعارض على التوالي). createdAt الحقل هو حقل مرجعي ولا يستخدمه العميل. الأسماء هي أسماء "عبر الأسلاك" للخصائص وهي غير قابلة للتعديل. ومع ذلك، يمكنك إنشاء تعيين بين العنصر والأسماء "عبر الأسلاك" باستخدام مكتبة gson . على سبيل المثال:

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

إنشاء مرجع جدول

للوصول إلى جدول، قم أولا بإنشاء كائن MobileServiceTable عن طريق استدعاء أسلوب getTable على MobileServiceClient. يحتوي هذا الأسلوب على تحميلين زائدين:

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

في التعليمات البرمجية التالية، mClient هو مرجع إلى كائن MobileServiceClient الخاص بك. يتم استخدام التحميل الزائد الأول حيث يكون اسم الفئة واسم الجدول متماثلين، وهو الاسم المستخدم في التشغيل السريع:

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

يتم استخدام التحميل الزائد الثاني عندما يختلف اسم الجدول عن اسم الفئة: المعلمة الأولى هي اسم الجدول.

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

الاستعلام عن جدول الواجهة الخلفية

أولا، الحصول على مرجع جدول. ثم قم بتنفيذ استعلام على مرجع الجدول. الاستعلام هو أي تركيبة من:

  • عبارة .where() عامل تصفية.
  • عبارة .orderBy() طلب.
  • عبارة .select() تحديد حقل.
  • A .skip() و .top() للنتائج المصفحة.

يجب تقديم العبارات بالترتيب السابق.

تصفية النتائج

النموذج العام للاستعلام هو:

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

يرجع المثال السابق جميع النتائج (حتى الحد الأقصى لحجم الصفحة الذي تم تعيينه بواسطة الخادم). .execute() ينفذ الأسلوب الاستعلام على الواجهة الخلفية. يتم تحويل الاستعلام إلى استعلام OData v3 قبل الإرسال إلى الواجهة الخلفية لتطبيقات الجوال. عند الاستلام، تحول الواجهة الخلفية لتطبيقات الأجهزة المحمولة الاستعلام إلى عبارة SQL قبل تنفيذه على مثيل SQL Azure. نظرا لأن نشاط الشبكة يستغرق بعض الوقت، يقوم .execute() الأسلوب بإرجاع ListenableFuture<E>.

تصفية البيانات التي تم إرجاعها

يقوم تنفيذ الاستعلام التالي بإرجاع كافة العناصر من جدول ToDoItem حيث يساوي complete false.

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

mToDoTable هو المرجع إلى جدول خدمة الجوال الذي أنشأناه سابقا.

تعريف عامل تصفية باستخدام استدعاء الأسلوب where على مرجع الجدول. الأسلوب where يتبعه أسلوب حقل متبوعا بأسلوب يحدد دالة التقييم المنطقية. تتضمن أساليب التقييم المحتملة eq (يساوي)، ne (غير متساوي)، gt (أكبر من)، ge (أكبر من أو يساوي)، lt (أقل من)، le (أقل من أو يساوي). تتيح لك هذه الأساليب مقارنة حقول الأرقام والسلاسل بقيم معينة.

يمكنك التصفية حسب التواريخ. تتيح لك الطرق التالية مقارنة حقل التاريخ بالكامل أو أجزاء من التاريخ: السنة والشهر واليوم والساعة والدقيقة والثانية. يضيف المثال التالي عامل تصفية للعناصر التي يساوي تاريخ استحقاقها 2013.

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

تدعم الأساليب التالية عوامل التصفية المعقدة في حقول السلسلة: startsWith، endsWith، concat، subString، indexOf، replace، toLower، toUpper، trim، و length. يقوم المثال التالي بتصفية صفوف الجدول حيث يبدأ عمود النص ب "PRI0".

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

يتم دعم أساليب عامل التشغيل التالية في حقول الأرقام: add و sub و mul و div و mod و floor و ceiling و round. يقوم المثال التالي بتصفية صفوف الجدول حيث تكون المدة رقما زوجيا.

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

يمكنك دمج دالات التقييم مع هذه الأساليب المنطقية: و أو لا. يجمع المثال التالي بين مثالين من الأمثلة السابقة.

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

تجميع وتداخل عوامل التشغيل المنطقية:

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

لمزيد من المناقشة التفصيلية وأمثلة على التصفية، راجع استكشاف ثراء نموذج استعلام عميل Android.

فرز البيانات التي تم إرجاعها

ترجع التعليمات البرمجية التالية كافة العناصر من جدول ToDoItems تم فرزها تصاعديا حسب حقل النص . mToDoTable هو المرجع إلى جدول الواجهة الخلفية الذي قمت بإنشائه مسبقا:

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

المعلمة الأولى من أسلوب orderBy هي سلسلة تساوي اسم الحقل الذي سيتم الفرز عليه. تستخدم المعلمة الثانية تعداد QueryOrder لتحديد ما إذا كنت تريد الفرز تصاعديا أو تنازليا. إذا كنت تقوم بالتصفية باستخدام الأسلوب where، يجب استدعاء أسلوب where قبل أسلوب orderBy.

تحديد أعمدة معينة

توضح التعليمات البرمجية التالية كيفية إرجاع كافة العناصر من جدول ToDoItems، ولكنها تعرض فقط الحقول الكاملة والنصية. mToDoTable هو المرجع إلى جدول الواجهة الخلفية الذي أنشأناه سابقا.

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

المعلمات لدالة التحديد هي أسماء سلاسل أعمدة الجدول التي تريد إرجاعها. يحتاج أسلوب التحديد إلى اتباع أساليب مثل مكان و orderBy. يمكن أن يتبعه طرق ترحيل مثل التخطي والأعلى.

إرجاع البيانات في الصفحات

يتم إرجاع البيانات دائما في الصفحات. يتم تعيين الحد الأقصى لعدد السجلات التي تم إرجاعها بواسطة الخادم. إذا طلب العميل المزيد من السجلات، فسيرجع الخادم الحد الأقصى لعدد السجلات. بشكل افتراضي، الحد الأقصى لحجم الصفحة على الخادم هو 50 سجلا.

يوضح المثال الأول كيفية تحديد العناصر الخمسة الأولى من جدول. يقوم الاستعلام بإرجاع العناصر من جدول ToDoItems. mToDoTable هو المرجع إلى جدول الواجهة الخلفية الذي قمت بإنشائه مسبقا:

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

فيما يلي استعلام يتخطى العناصر الخمسة الأولى، ثم يرجع العناصر الخمسة التالية:

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

إذا كنت ترغب في الحصول على جميع السجلات في جدول، فنفذ التعليمات البرمجية للتكرار عبر جميع الصفحات:

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

يؤدي طلب جميع السجلات باستخدام هذا الأسلوب إلى إنشاء طلبين على الأقل إلى الواجهة الخلفية لتطبيقات الأجهزة المحمولة.

تلميح

اختيار حجم الصفحة الصحيح هو التوازن بين استخدام الذاكرة أثناء حدوث الطلب واستخدام النطاق الترددي والتأخير في تلقي البيانات تماما. الإعداد الافتراضي (50 سجلا) مناسب لجميع الأجهزة. إذا كنت تعمل حصريا على أجهزة ذاكرة أكبر، فقم بزيادة ما يصل إلى 500. لقد وجدنا أن زيادة حجم الصفحة بعد 500 سجل يؤدي إلى تأخيرات غير مقبولة ومشكلات ذاكرة كبيرة.

كيفية: تسلسل أساليب الاستعلام

يمكن تسلسل الأساليب المستخدمة في الاستعلام عن الجداول الخلفية. تسمح لك أساليب استعلام التسلسل بتحديد أعمدة معينة من الصفوف المصفاة التي يتم فرزها وصفحة صفحاتها. يمكنك إنشاء عوامل تصفية منطقية معقدة. يقوم كل أسلوب استعلام بإرجاع كائن استعلام. لإنهاء سلسلة الأساليب وتشغيل الاستعلام فعليا، قم باستدعاء أسلوب التنفيذ . على سبيل المثال:

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

يجب ترتيب أساليب الاستعلام المتسلسل على النحو التالي:

  1. أساليب التصفية (حيث).
  2. أساليب الفرز (orderBy).
  3. أساليب التحديد (تحديد).
  4. أساليب الترحيل (تخطي وأعلى).

ربط البيانات بواجهة المستخدم

يتضمن ربط البيانات ثلاثة مكونات:

  • مصدر البيانات
  • تخطيط الشاشة
  • المحول الذي يربط الاثنين معا.

في نموذج التعليمات البرمجية، نقوم بإعادة البيانات من جدول Mobile Apps SQL Azure ToDoItem إلى صفيف. هذا النشاط هو نمط شائع لتطبيقات البيانات. غالبا ما ترجع استعلامات قاعدة البيانات مجموعة من الصفوف التي يحصل عليها العميل في قائمة أو صفيف. في هذا النموذج، الصفيف هو مصدر البيانات. تحدد التعليمات البرمجية تخطيط شاشة يحدد طريقة عرض البيانات التي تظهر على الجهاز. يرتبط الاثنان بمحول، والذي في هذه التعليمة البرمجية هو امتداد لفئة ArrayAdapter<ToDoItem> .

تعريف التخطيط

يتم تعريف التخطيط بواسطة العديد من القصاصات البرمجية ل XML. نظرا لتخطيط موجود، تمثل التعليمات البرمجية التالية ListView الذي نريد تعبئته ببيانات الخادم.

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

في التعليمات البرمجية السابقة، تحدد سمة listitem معرف التخطيط لصف فردي في القائمة. تحدد هذه التعليمة البرمجية خانة اختيار والنص المقترن بها ويتم إنشاء مثيل لها مرة واحدة لكل عنصر في القائمة. لا يعرض هذا التخطيط حقل المعرف ، وسيحدد التخطيط الأكثر تعقيدا حقولا إضافية في العرض. هذه التعليمة البرمجية موجودة في ملف 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>

تعريف المحول

نظرا لأن مصدر البيانات لعرضنا هو صفيف من ToDoItem، فإننا نصنف محولنا من فئة ArrayAdapter<ToDoItem> . تنتج هذه الفئة الفرعية طريقة عرض لكل ToDoItem باستخدام تخطيط row_list_to_do . في التعليمات البرمجية الخاصة بنا، نحدد الفئة التالية التي تعد امتدادا لفئة ArrayAdapter<E> :

public class ToDoItemAdapter extends ArrayAdapter<ToDoItem> {
}

تجاوز أسلوب getView للمحولات. على سبيل المثال:

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

نقوم بإنشاء مثيل لهذه الفئة في نشاطنا كما يلي:

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

المعلمة الثانية إلى الدالة الإنشائية ToDoItemAdapter هي مرجع إلى التخطيط. يمكننا الآن إنشاء مثيل ListView وتعيين المحول إلى ListView.

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

استخدام المحول للربط بواجهة المستخدم

أنت الآن جاهز لاستخدام ربط البيانات. توضح التعليمات البرمجية التالية كيفية الحصول على العناصر في الجدول وتملأ المحول المحلي بالعناصر التي تم إرجاعها.

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

اتصل بالمحول في أي وقت تقوم فيه بتعديل جدول ToDoItem . نظرا لأن التعديلات يتم إجراؤها على سجل حسب السجل، فإنك تتعامل مع صف واحد بدلا من مجموعة. عند إدراج عنصر، قم باستدعاء أسلوب الإضافة على المحول؛ عند الحذف، قم باستدعاء أسلوب الإزالة.

يمكنك العثور على مثال كامل في مشروع التشغيل السريع ل Android.

إدراج البيانات في الخلفية

إنشاء مثيل لفئة ToDoItem وتعيين خصائصه.

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

ثم استخدم insert() لإدراج كائن:

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

يطابق الكيان الذي تم إرجاعه البيانات المدرجة في جدول الواجهة الخلفية، مضمنا المعرف وأي قيم أخرى (مثل createdAtupdatedAtالحقول و وversion) التي تم تعيينها على الخلفية.

تتطلب جداول تطبيقات الأجهزة المحمولة عمود مفتاح أساسي يسمى المعرف. يجب أن يكون هذا العمود سلسلة. القيمة الافتراضية لعمود المعرف هي GUID. يمكنك توفير قيم فريدة أخرى، مثل عناوين البريد الإلكتروني أو أسماء المستخدمين. عندما لا يتم توفير قيمة معرف سلسلة لسجل مدرج، تقوم الخلفية بإنشاء GUID جديد.

توفر قيم معرف السلسلة المزايا التالية:

  • يمكن إنشاء المعرف دون القيام برحلة ذهابا وإيابا إلى قاعدة البيانات.
  • يسهل دمج السجلات من جداول أو قواعد بيانات مختلفة.
  • تتكامل قيم المعرف بشكل أفضل مع منطق التطبيق.

قيم معرف السلسلة مطلوبة لدعم المزامنة دون اتصال. لا يمكنك تغيير معرف بمجرد تخزينه في قاعدة البيانات الخلفية.

تحديث البيانات في تطبيق الأجهزة المحمولة

لتحديث البيانات في جدول، قم بتمرير الكائن الجديد إلى أسلوب update().

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

في هذا المثال، العنصر هو مرجع إلى صف في جدول ToDoItem ، الذي تم إجراء بعض التغييرات عليه. يتم تحديث الصف بنفس المعرف .

حذف البيانات في تطبيق الأجهزة المحمولة

توضح التعليمات البرمجية التالية كيفية حذف البيانات من جدول عن طريق تحديد كائن البيانات.

mToDoTable
    .delete(item);

يمكنك أيضا حذف عنصر عن طريق تحديد حقل معرف الصف المراد حذفه.

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

البحث عن عنصر معين حسب المعرف

ابحث عن عنصر مع حقل معرف معين باستخدام أسلوب lookUp() :

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

كيفية: العمل مع البيانات غير المصنفة

يمنحك نموذج البرمجة غير المصمم تحكما دقيقا في تسلسل JSON. هناك بعض السيناريوهات الشائعة حيث قد ترغب في استخدام نموذج برمجة غير مبرمج. على سبيل المثال، إذا كان الجدول الخلفي يحتوي على العديد من الأعمدة وتحتاج فقط إلى الرجوع إلى مجموعة فرعية من الأعمدة. يتطلب منك النموذج الذي تم كتابته تعريف جميع الأعمدة المحددة في الواجهة الخلفية لتطبيقات الأجهزة المحمولة في فئة البيانات الخاصة بك. معظم استدعاءات واجهة برمجة التطبيقات للوصول إلى البيانات مشابهة لاستدعاءات البرمجة التي تم كتابتها. الفرق الرئيسي هو أنه في النموذج غير المصمم يمكنك استدعاء أساليب على كائن MobileServiceJsonTable ، بدلا من كائن MobileServiceTable .

إنشاء مثيل لجدول غير نمطي

على غرار النموذج الذي تم كتابته، تبدأ بالحصول على مرجع جدول، ولكن في هذه الحالة هو كائن MobileServicesJsonTable . احصل على المرجع عن طريق استدعاء أسلوب getTable على مثيل للعميل:

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

بمجرد إنشاء مثيل ل MobileServiceJsonTable، فإنه يحتوي تقريبا على نفس واجهة برمجة التطبيقات المتوفرة كما هو الحال مع نموذج البرمجة الذي تمت كتابته. في بعض الحالات، تأخذ الأساليب معلمة غير مكتوبة بدلا من معلمة مكتوبة.

إدراج في جدول غير مضمن

توضح التعليمات البرمجية التالية كيفية إجراء إدراج. الخطوة الأولى هي إنشاء JsonObject، وهو جزء من مكتبة gson .

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

ثم استخدم insert() لإدراج الكائن غير المصمم في الجدول.

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

إذا كنت بحاجة إلى الحصول على معرف الكائن المدرج، فاستخدم الأسلوب getAsJsonPrimitive().

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

الحذف من جدول غير نمطي

توضح التعليمات البرمجية التالية كيفية حذف مثيل، في هذه الحالة، نفس مثيل JsonObject الذي تم إنشاؤه في مثال الإدراج السابق. التعليمات البرمجية هي نفسها كما هو الحال مع الحالة التي تمت كتابتها، ولكن الأسلوب له توقيع مختلف لأنه يشير إلى JsonObject.

mToDoTable
    .delete(insertedItem);

يمكنك أيضا حذف مثيل مباشرة باستخدام معرفه:

mToDoTable.delete(ID);

إرجاع كافة الصفوف من جدول غير نمطي

توضح التعليمات البرمجية التالية كيفية استرداد جدول بأكمله. نظرا لأنك تستخدم جدول JSON، يمكنك استرداد بعض أعمدة الجدول فقط بشكل انتقائي.

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

تتوفر نفس مجموعة أساليب التصفية والتصفية والترحيل المتوفرة للنموذج الذي تمت كتابته للنموذج غير المصنف.

تنفيذ المزامنة دون اتصال

يقوم Azure Mobile Apps Client SDK أيضا بتنفيذ مزامنة البيانات دون اتصال باستخدام قاعدة بيانات SQLite لتخزين نسخة من بيانات الخادم محليا. لا تتطلب العمليات التي يتم إجراؤها على جدول غير متصل اتصالا بالهاتف المحمول للعمل. تساعد المزامنة دون اتصال في المرونة والأداء على حساب منطق أكثر تعقيدا لحل التعارض. يقوم Azure Mobile Apps Client SDK بتنفيذ الميزات التالية:

  • المزامنة التزايدية: يتم تنزيل السجلات المحدثة والجديدة فقط، مما يوفر عرض النطاق الترددي واستهلاك الذاكرة.
  • التزامن المتفائل: يفترض نجاح العمليات. يتم تأجيل حل التعارض حتى يتم تنفيذ التحديثات على الخادم.
  • حل التعارض: تكتشف SDK وقت إجراء تغيير متعارض على الخادم وتوفر خطافات لتنبيه المستخدم.
  • حذف مبدئي: يتم وضع علامة على السجلات المحذوفة محذوفة، مما يسمح للأجهزة الأخرى بتحديث ذاكرة التخزين المؤقت الخاصة بها دون اتصال.

تهيئة المزامنة دون اتصال

يجب تعريف كل جدول دون اتصال في ذاكرة التخزين المؤقت دون اتصال قبل الاستخدام. عادة، يتم تعريف الجدول مباشرة بعد إنشاء العميل:

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

الحصول على مرجع إلى جدول ذاكرة التخزين المؤقت دون اتصال

بالنسبة لجدول عبر الإنترنت، يمكنك استخدام .getTable(). بالنسبة لجدول غير متصل، استخدم .getSyncTable():

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

تعمل جميع الأساليب المتوفرة للجداول عبر الإنترنت (بما في ذلك التصفية والفرز والترحيل وإدراج البيانات وتحديث البيانات وحذفها) بشكل جيد على الجداول المتصلة وغير المتصلة.

مزامنة ذاكرة التخزين المؤقت المحلية دون اتصال

المزامنة ضمن التحكم في تطبيقك. فيما يلي مثال على أسلوب المزامنة:

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

إذا تم توفير اسم استعلام للأسلوب، استخدام المزامنة .pull(query, queryname) التزايدية لإرجاع السجلات التي تم إنشاؤها أو تغييرها فقط منذ آخر سحب تم إكماله بنجاح.

معالجة التعارضات أثناء المزامنة دون اتصال

إذا حدث تعارض أثناء .push() عملية ما، MobileServiceConflictException يتم طرح . يتم تضمين العنصر الصادر عن الخادم في الاستثناء ويمكن استرداده بواسطة .getItem() في الاستثناء. اضبط الدفع عن طريق استدعاء العناصر التالية على كائن MobileServiceSyncContext:

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

بمجرد وضع علامة على جميع التعارضات كما تريد، اتصل .push() مرة أخرى لحل جميع التعارضات.

استدعاء واجهة برمجة تطبيقات مخصصة

تمكنك واجهة برمجة التطبيقات المخصصة من تحديد نقاط النهاية المخصصة التي تعرض وظائف الخادم التي لا تعين إلى عملية إدراج أو تحديث أو حذف أو قراءة. باستخدام واجهة برمجة تطبيقات مخصصة، يمكنك التحكم بشكل أكبر في المراسلة، بما في ذلك قراءة رؤوس رسائل HTTP وإعدادها وتعريف تنسيق نص الرسالة بخلاف JSON.

من عميل Android، يمكنك استدعاء أسلوب invokeApi لاستدعاء نقطة نهاية واجهة برمجة التطبيقات المخصصة. يوضح المثال التالي كيفية استدعاء نقطة نهاية واجهة برمجة التطبيقات المسماة completeAll، والتي ترجع فئة مجموعة تسمى 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();
        }
    });
}

يتم استدعاء أسلوب invokeApi على العميل، والذي يرسل طلب POST إلى واجهة برمجة التطبيقات المخصصة الجديدة. يتم عرض النتيجة التي تم إرجاعها بواسطة واجهة برمجة التطبيقات المخصصة في مربع حوار رسالة، وكذلك أي أخطاء. تتيح لك الإصدارات الأخرى من invokeApi إرسال عنصر اختياريا في نص الطلب، وتحديد أسلوب HTTP، وإرسال معلمات الاستعلام مع الطلب. يتم توفير إصدارات غير ذات كتابة من invokeApi أيضا.

إضافة مصادقة إلى تطبيقك

تصف البرامج التعليمية بالفعل بالتفصيل كيفية إضافة هذه الميزات.

تدعم App Service مصادقة مستخدمي التطبيق باستخدام موفري هوية خارجيين مختلفين: Facebook وGoogle وحساب Microsoft وتويتر وAzure Active Directory. يمكنك تعيين أذونات على الجداول لتقييد الوصول لعمليات معينة للمستخدمين المصادق عليهم فقط. يمكنك أيضا استخدام هوية المستخدمين المصادق عليهم لتنفيذ قواعد التخويل في الخلفية.

يتم دعم تدفقي مصادقة: تدفق خادم وتدفق عميل . يوفر تدفق الخادم أبسط تجربة مصادقة، لأنه يعتمد على واجهة الويب لموفري الهوية. لا يلزم وجود SDKs إضافية لتنفيذ مصادقة تدفق الخادم. لا توفر مصادقة تدفق الخادم تكاملا عميقا في الجهاز المحمول ويوصى به فقط لإثبات سيناريوهات المفهوم.

يسمح تدفق العميل بتكامل أعمق مع الإمكانات الخاصة بالجهاز مثل تسجيل الدخول الأحادي لأنه يعتمد على SDKs الذي يوفره موفر الهوية. على سبيل المثال، يمكنك دمج Facebook SDK في تطبيق الجوال الخاص بك. يقوم عميل الأجهزة المحمولة بالتبديل إلى تطبيق Facebook ويؤكد تسجيل الدخول قبل التبديل مرة أخرى إلى تطبيق الأجهزة المحمولة.

هناك أربع خطوات مطلوبة لتمكين المصادقة في تطبيقك:

  • سجل تطبيقك للمصادقة مع موفر هوية.
  • تكوين الواجهة الخلفية لخدمة التطبيقات.
  • تقييد أذونات الجدول للمستخدمين المصادق عليهم فقط على الواجهة الخلفية ل App Service.
  • أضف رمز المصادقة إلى تطبيقك.

يمكنك تعيين أذونات على الجداول لتقييد الوصول لعمليات معينة للمستخدمين المصادق عليهم فقط. يمكنك أيضا استخدام SID لمستخدم مصادق عليه لتعديل الطلبات. لمزيد من المعلومات، راجع بدء استخدام المصادقة ووثائق Server SDK HOWTO.

المصادقة: تدفق الخادم

تبدأ التعليمات البرمجية التالية عملية تسجيل الدخول إلى تدفق الخادم باستخدام موفر Google. مطلوب تكوين إضافي بسبب متطلبات الأمان لموفر Google:

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

بالإضافة إلى ذلك، أضف الأسلوب التالي إلى فئة النشاط الرئيسي:

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

يتم استخدام المعرف GOOGLE_LOGIN_REQUEST_CODE في نشاطك الرئيسي للأسلوب login() وضمن onActivityResult() الأسلوب . يمكنك اختيار أي رقم فريد، طالما يتم استخدام نفس الرقم داخل login() الأسلوب والأسلوب onActivityResult() . إذا قمت بتجريد التعليمات البرمجية للعميل إلى محول خدمة (كما هو موضح سابقا)، يجب عليك استدعاء الأساليب المناسبة على محول الخدمة.

تحتاج أيضا إلى تكوين المشروع للمهام المخصصة. حدد أولا عنوان URL لإعادة التوجيه. أضف القصاصة البرمجية التالية إلى 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>

أضف redirectUriScheme إلى build.gradle ملف التطبيق الخاص بك:

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

وأخيرا، أضف com.android.support:customtabs:28.0.0 إلى قائمة التبعيات في build.gradle الملف:

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

احصل على معرف المستخدم الذي قام بتسجيل الدخول من MobileServiceUser باستخدام أسلوب getUserId . للحصول على مثال حول كيفية استخدام Futures لاستدعاء واجهات برمجة تطبيقات تسجيل الدخول غير المتزامنة، راجع بدء استخدام المصادقة.

تحذير

نظام URL المذكور حساس لحالة الأحرف. تأكد من أن جميع تكرارات حالة المطابقة {url_scheme_of_you_app} .

رموز المصادقة المميزة لذاكرة التخزين المؤقت

يتطلب التخزين المؤقت الرموز المميزة للمصادقة تخزين معرف المستخدم ورمز المصادقة المميز محليا على الجهاز. في المرة التالية التي يبدأ فيها التطبيق، يمكنك التحقق من ذاكرة التخزين المؤقت، وإذا كانت هذه القيم موجودة، يمكنك تخطي إجراء تسجيل الدخول وإعادة ترطيب العميل بهذه البيانات. ومع ذلك، فإن هذه البيانات حساسة، ويجب تخزينها مشفرة للأمان في حالة سرقة الهاتف. يمكنك مشاهدة مثال كامل لكيفية تخزين رموز المصادقة المميزة مؤقتا في قسم الرموز المميزة لمصادقة ذاكرة التخزين المؤقت.

عند محاولة استخدام رمز مميز منتهية الصلاحية، تتلقى استجابة 401 غير مصرح بها . يمكنك معالجة أخطاء المصادقة باستخدام عوامل التصفية. تصفية طلبات اعتراض الواجهة الخلفية ل App Service. يختبر رمز عامل التصفية الاستجابة ل 401، ويشغل عملية تسجيل الدخول، ثم يستأنف الطلب الذي أنشأ 401.

استخدام الرموز المميزة للتحديث

الرمز المميز الذي تم إرجاعه بواسطة Azure App Service Authentication and Authorization له وقت حياة محدد من ساعة واحدة. بعد هذه الفترة، يجب إعادة مصادقة المستخدم. إذا كنت تستخدم رمزا مميزا طويل الأمد تلقيته عبر مصادقة تدفق العميل، فيمكنك إعادة المصادقة باستخدام Azure App Service Authentication and Authorization باستخدام نفس الرمز المميز. يتم إنشاء رمز مميز آخر لخدمة تطبيقات Azure بمدة بقاء جديدة.

يمكنك أيضا تسجيل الموفر لاستخدام رموز التحديث المميزة. رمز التحديث المميز غير متوفر دائما. التكوين الإضافي مطلوب:

  • بالنسبة إلى Azure Active Directory، قم بتكوين سر عميل لتطبيق Azure Active Directory. حدد سر العميل في Azure App Service عند تكوين مصادقة Azure Active Directory. عند استدعاء .login()، مرر response_type=code id_token كمعلمة:

    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);
    
  • بالنسبة إلى Google، مرر access_type=offline كمعلمة:

    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);
    
  • بالنسبة إلى حساب Microsoft، حدد wl.offline_access النطاق.

لتحديث رمز مميز، اتصل ب .refreshUser():

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

كأفضل ممارسة، قم بإنشاء عامل تصفية يكتشف استجابة 401 من الخادم ويحاول تحديث رمز المستخدم المميز.

تسجيل الدخول باستخدام مصادقة تدفق العميل

العملية العامة لتسجيل الدخول باستخدام مصادقة تدفق العميل هي كما يلي:

  • تكوين Azure App Service Authentication and Authorization كما تفعل مع مصادقة تدفق الخادم.

  • دمج SDK لموفر المصادقة للمصادقة لإنتاج رمز مميز للوصول.

  • .login() استدعاء الأسلوب كما يلي (result يجب أن يكون 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");
        }
    });
    

راجع مثال التعليمات البرمجية الكامل في القسم التالي.

onSuccess() استبدل الأسلوب بأي تعليمة برمجية ترغب في استخدامها في تسجيل دخول ناجح. السلسلة {provider} هي موفر صالح: aad (Azure Active Directory) أو facebook أو google أو microsoftaccount أو twitter. إذا قمت بتطبيق مصادقة مخصصة، فيمكنك أيضا استخدام علامة موفر المصادقة المخصصة.

مصادقة المستخدمين باستخدام مكتبة مصادقة Active Directory (ADAL)

يمكنك استخدام مكتبة مصادقة Active Directory (ADAL) لتسجيل دخول المستخدمين إلى التطبيق الخاص بك باستخدام Azure Active Directory. غالبا ما يكون استخدام تسجيل الدخول إلى تدفق العميل أفضل من استخدام الأساليب loginAsync() لأنه يوفر شعورا أكثر أصلية ل UX ويسمح بتخصيص إضافي.

  1. قم بتكوين الواجهة الخلفية لتطبيق الأجهزة المحمولة لتسجيل الدخول إلى AAD باتباع البرنامج التعليمي كيفية تكوين App Service لتسجيل الدخول إلى Active Directory. تأكد من إكمال الخطوة الاختيارية لتسجيل تطبيق عميل أصلي.

  2. تثبيت ADAL عن طريق تعديل ملف build.gradle لتضمين التعريفات التالية:

    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. أضف التعليمات البرمجية التالية إلى التطبيق الخاص بك، مع إجراء الاستبدالات التالية:

    • استبدل INSERT-AUTHORITY-HERE باسم المستأجر الذي قمت بتوفير التطبيق الخاص بك فيه. يجب أن يكون https://login.microsoftonline.com/contoso.onmicrosoft.comالتنسيق .
    • استبدل INSERT-RESOURCE-ID-HERE بمعرف العميل للواجهة الخلفية لتطبيق الأجهزة المحمولة. يمكنك الحصول على معرف العميل من علامة التبويب Advanced ضمن Azure Active Directory Settings في المدخل.
    • استبدل INSERT-CLIENT-ID-HERE بمعرف العميل الذي نسخته من تطبيق العميل الأصلي.
    • استبدل INSERT-REDIRECT-URI-HERE بنقطة نهاية /.auth/login/done لموقعك، باستخدام نظام HTTPS. يجب أن تكون هذه القيمة مشابهة ل 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);
    }
}

ضبط اتصال العميل-الخادم

عادة ما يكون اتصال العميل اتصال HTTP أساسي باستخدام مكتبة HTTP الأساسية المتوفرة مع Android SDK. هناك عدة أسباب وراء رغبتك في تغيير ذلك:

  • ترغب في استخدام مكتبة HTTP بديلة لضبط المهلات.
  • ترغب في توفير شريط تقدم.
  • ترغب في إضافة عنوان مخصص لدعم وظيفة إدارة واجهة برمجة التطبيقات.
  • ترغب في اعتراض استجابة فاشلة بحيث يمكنك تنفيذ إعادة المصادقة.
  • ترغب في تسجيل طلبات الواجهة الخلفية إلى خدمة تحليلات.

استخدام مكتبة HTTP بديلة

.setAndroidHttpClientFactory() استدعاء الأسلوب مباشرة بعد إنشاء مرجع العميل الخاص بك. على سبيل المثال، لتعيين مهلة الاتصال إلى 60 ثانية (بدلا من 10 ثوان الافتراضية):

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

تنفيذ عامل تصفية التقدم

يمكنك تنفيذ اعتراض لكل طلب عن طريق تنفيذ ServiceFilter. على سبيل المثال، يحدث التالي شريط تقدم تم إنشاؤه مسبقا:

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

يمكنك إرفاق عامل التصفية هذا بالعميل كما يلي:

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

تخصيص رؤوس الطلبات

استخدم ما يلي ServiceFilter وأرفق عامل التصفية بنفس طريقة 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);
        }
    }
}

تكوين التسلسل التلقائي

يمكنك تحديد استراتيجية تحويل تنطبق على كل عمود باستخدام واجهة برمجة تطبيقات gson . تستخدم مكتبة عميل Android gson خلف الكواليس لتسلسل كائنات Java إلى بيانات JSON قبل إرسال البيانات إلى Azure App Service. تستخدم التعليمات البرمجية التالية أسلوب setFieldNamingStrategy() لتعيين الاستراتيجية. سيحذف هذا المثال الحرف الأولي ("m")، ثم يحذف الحرف التالي بأحرف صغيرة، لكل اسم حقل. على سبيل المثال، سيحول "mId" إلى "id". تنفيذ استراتيجية تحويل لتقليل الحاجة إلى SerializedName() التعليقات التوضيحية في معظم الحقول.

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

يجب تنفيذ هذه التعليمة البرمجية قبل إنشاء مرجع عميل الجوال باستخدام MobileServiceClient.