الاتصال بالشبكة

لإجراء عمليات الشبكة في التطبيق، يجب أن يتضمن البيان الأذونات التالية:

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

أفضل الممارسات للاتصال بالشبكة الآمنة

قبل إضافة وظائف الشبكات إلى تطبيقك، عليك التأكّد من تظل البيانات والمعلومات داخل التطبيق آمنة عند النقل عبر الشبكة. ولإجراء ذلك، اتبع أفضل ممارسات أمان الشبكات التالية:

  • عليك تقليل كمية بيانات المستخدِم الحساسة أو الشخصية التي تُرسِلها عبر الشبكة.
  • إرسال جميع حركات بيانات الشبكة من تطبيقك عبر طبقة المقابس الآمنة:
  • ننصحك بإنشاء نظام أمان شبكة والضبط، والتي تتيح لتطبيقك الوثوق بمراجع التصديق المخصّصة (CAs) أو تقييد مجموعة مراجع التصديق للنظام التي يثق بها للتواصل الآمن.

لمزيد من المعلومات حول كيفية تطبيق مبادئ الشبكات الآمنة، يُرجى مراجعة نصائح حول أمان الشبكات

اختيار عميل HTTP

تستخدم معظم التطبيقات المرتبطة بالشبكة HTTP لإرسال البيانات واستلامها. نظام التشغيل Android المنصة HttpsURLConnection، والذي يدعم بروتوكول أمان طبقة النقل (TLS)، وعمليات تحميل وتنزيل البث، والمدد القابلة للتهيئة، وIPv6 وتجميع الاتصال.

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

  • إجراء مراجعة: HTTP آمن من حيث النوع العميل لآلة متجه الدعم من Square، والذي تم إنشاؤه في الجزء العلوي من OkHttp. تتيح لك عملية التحديث إنشاء واجهة عميل صراحةً وتدعم العديد من لمكتبات التسلسل.
  • Ktor: هو برنامج HTTP من JetBrains تم إنشاؤه بالكامل بلغة Kotlin ويدعمها الكوروتينات. يدعم برنامج Ktor محركات مختلفة، والمتسلسلات والمنصات.

حلّ طلبات البحث في نظام أسماء النطاقات

الأجهزة التي تعمل بنظام التشغيل Android 10 (المستوى 29 من واجهة برمجة التطبيقات) والإصدارات الأحدث تتضمن دعمًا مضمّنًا عمليات بحث نظام أسماء النطاقات المتخصصة من خلال كل من عمليات بحث النص الواضح ونظام أسماء النطاقات عبر بروتوكول أمان طبقة النقل. توفّر واجهة برمجة التطبيقات DnsResolver نظرة عامة حول درجة الدقة غير المتزامنة، التي تتيح لك البحث عن SRV وNAPTR وغير ذلك السجلات. تحليل الاستجابة يُترك للتطبيق لتنفيذه.

على الأجهزة التي تعمل بالإصدار 9 من نظام التشغيل Android (المستوى 28 لواجهة برمجة التطبيقات) والإصدارات الأقدم، لا يتيح "محوِّل عنوان DNS" في النظام الأساسي سوى سجلَّي A وAAAA. يتيح لك ذلك البحث عن عناوين IP المرتبطة باسم معيّن، ولكنّه لا يتيح أي أنواع أخرى من السجلّات.

بالنسبة إلى التطبيقات المستندة إلى NDK، يُرجى مراجعة android_res_nsend

تغليف عمليات الشبكة باستخدام المستودع

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

يمكنك استخدام ميزة "إعادة التحديث" للإعلان عن واجهة تحدد طريقة HTTP، عنوان URL والوسيطات ونوع الاستجابة لعمليات الشبكة، كما في ما يلي مثال:

Kotlin

interface UserService {
    @GET("/users/{id}")
    suspend fun getUser(@Path("id") id: String): User
}

Java

public interface UserService {
    @GET("/user/{id}")
    Call<User> getUserById(@Path("id") String id);
}

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

Kotlin

class UserRepository constructor(
    private val userService: UserService
) {
    suspend fun getUserById(id: String): User {
        return userService.getUser(id)
    }
}

Java

class UserRepository {
    private UserService userService;

    public UserRepository(
            UserService userService
    ) {
        this.userService = userService;
    }

    public Call<User> getUserById(String id) {
        return userService.getUser(id);
    }
}

لتجنُّب إنشاء واجهة مستخدم لا تستجيب، لا تُجري عمليات على الشبكة في سلسلت الرسائل الرئيسية. يطلب منك نظام التشغيل Android بشكل تلقائي إجراء عمليات على الشبكة على سلسلة التعليمات بخلاف سلسلة واجهة المستخدم الرئيسية. إذا حاولت إجراء عمليات على الشبكة في سلسلة التعليمات الرئيسية NetworkOnMainThreadException التي تم رميها.

في مثال التعليمة البرمجية السابق، أن عملية الشبكة لا يتم تشغيلها بالفعل. على المُرسِل لطلب UserRepository تنفيذ ميزة "المعالجة المتعدّدة المواضيع" إما باستخدام وظائف coroutines أو باستخدام الدالة enqueue() . لمزيد من المعلومات، يُرجى الاطّلاع على الدرس التطبيقي حول الترميز الحصول على بيانات من الإنترنت، الذي يوضح كيفية تنفيذ سلاسل المحادثات باستخدام الكوروتينات في لغة Kotlin.

الحفاظ على الأداء في حال تغيير الإعدادات

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

يمكنك استخدام ViewModel للسماح تبقى بياناتك على قيد الحياة بعد التغييرات في التكوين. المكوِّن ViewModel هو مصمَّمة لتخزين وإدارة البيانات المتعلقة بواجهة المستخدم في إطار نفسها. باستخدام UserRepository السابق، يمكن للعنصر ViewModel إرسال طلبات الشبكة اللازمة وتقديم النتيجة إلى المقتطف أو النشاط باستخدام LiveData:

Kotlin

class MainViewModel constructor(
    savedStateHandle: SavedStateHandle,
    userRepository: UserRepository
) : ViewModel() {
    private val userId: String = savedStateHandle["uid"] ?:
        throw IllegalArgumentException("Missing user ID")

    private val _user = MutableLiveData<User>()
    val user = _user as LiveData<User>

    init {
        viewModelScope.launch {
            try {
                // Calling the repository is safe as it moves execution off
                // the main thread
                val user = userRepository.getUserById(userId)
                _user.value = user
            } catch (error: Exception) {
                // Show error message to user
            }

        }
    }
}

Java

class MainViewModel extends ViewModel {

    private final MutableLiveData<User> _user = new MutableLiveData<>();
    LiveData<User> user = (LiveData<User>) _user;

    public MainViewModel(
            SavedStateHandle savedStateHandle,
            UserRepository userRepository
    ) {
        String userId = savedStateHandle.get("uid");
        Call<User> userCall = userRepository.getUserById(userId);
        userCall.enqueue(new Callback<User>() {
            @Override
            public void onResponse(Call<User> call, Response<User> response) {
                if (response.isSuccessful()) {
                    _user.setValue(response.body());
                }
            }

            @Override
            public void onFailure(Call<User> call, Throwable t) {
                // Show error message to user
            }
        });
    }
}

لمزيد من المعلومات عن هذا الموضوع، اطّلِع على الأدلة التالية ذات الصلة: