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

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

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

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

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

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

اختيار عميل HTTP

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

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

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

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

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

على الأجهزة التي تعمل بنظام التشغيل Android 9 (المستوى 28 لواجهة برمجة التطبيقات) والإصدارات الأقدم، يمكن لنظام أسماء النطاقات يتوافق برنامج التعيين مع سجلات 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 تنفيذ سلسلة المحادثات إمّا باستخدام الكوروتين أو باستخدام السمة 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
            }
        });
    }
}

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