اختيار المكتبات بعناية

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

نصائح عامة عند اختيار المكتبات

استخدِم هذه النصائح للمساعدة في ضمان توافق مكتباتك مع ميزة "تحسين التطبيق".

تفضيل إنشاء الرموز البرمجية على استخدام الانعكاس

اختَر المكتبات التي تستخدم إنشاء الرموز البرمجية (codegen) بدلاً من الانعكاس. باستخدام codegen، يمكن للمحسِّن تحديد الرمز الذي يتم استخدامه فعليًا في وقت التشغيل والرمز الذي يمكن إزالته بسهولة أكبر. قد يكون من الصعب معرفة ما إذا كانت المكتبة تستخدم إنشاء الرموز أو الانعكاس، ولكن هناك بعض المؤشرات التي يمكن أن تساعدك في ذلك، لذا راجِع النصائح.

لمزيد من المعلومات حول إنشاء الرموز البرمجية مقابل الانعكاس، يُرجى الاطّلاع على التحسين لمؤلفي المكتبات.

التحقّق من استخدام الانعكاس (ميزة متقدّمة)

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

  • يستخدم هذا النوع فئات أو طرقًا من حزمتَي kotlin.reflect أو java.lang.reflect.
  • يستخدم الدالتَين Class.forName أو classLoader.getClass.
  • تقرأ التعليقات التوضيحية في وقت التشغيل، مثلاً إذا كانت تخزّن قيمة تعليق توضيحي باستخدام val value = myClass.getAnnotation() أو val value = myMethod.getAnnotation() ثم تنفّذ إجراءً باستخدام value.
  • يتم استدعاء الطرق باستخدام اسم الطريقة كسلسلة، كما في المثال التالي:

    // Calls the private `processData` API with reflection
    myObject.javaClass.getMethod("processData", DataType::class.java)
    ?.invoke(myObject, data)
    

التحقّق من مشاكل التحسين

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

  • تعمل مكتبات AndroidX والمكتبات الأخرى، مثل Hilt، بشكل جيد مع تحسين التطبيقات لأنّها تستخدم في الغالب إنشاء الرموز البرمجية بدلاً من الانعكاس. وعند استخدامها، يوفّرون الحد الأدنى من قواعد الاحتفاظ لإبقاء الرمز البرمجي المطلوب فقط.
  • تستخدم مكتبات التسلسل غالبًا ميزة "الانعكاس" لتجنُّب الرموز النموذجية عند إنشاء مثيل للكائنات أو تسلسلها. بدلاً من الطرق المستندة إلى الانعكاس (مثل Gson لملفات JSON)، ابحث عن المكتبات التي تستخدم إنشاء الرموز البرمجية لتجنُّب هذه المشاكل، مثلاً باستخدام Kotlin Serialization أو Moshi مع إنشاء الرموز البرمجية.
  • تجنَّب المكتبات التي تتضمّن قواعد الاحتفاظ على مستوى الحزمة إن أمكن ذلك. يمكن أن تساعد قواعد الاحتفاظ على مستوى الحزمة في حلّ الأخطاء، ولكن يجب في النهاية تحسين قواعد الاحتفاظ الواسعة النطاق للاحتفاظ فقط بالرمز البرمجي المطلوب. لمزيد من المعلومات، اطّلِع على تطبيق التحسينات بشكل تدريجي.
  • يجب ألا تتطلّب المكتبات نسخ قواعد الاحتفاظ بالبيانات ولصقها من المستندات في ملف في مشروعك، خاصةً قواعد الاحتفاظ بالبيانات على مستوى الحزمة. وتشكّل هذه القواعد عبئًا على مطوّر التطبيق على المدى الطويل، كما يصعب تحسينها وتغييرها بمرور الوقت.

تفعيل التحسين بعد إضافة مكتبة جديدة

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

فلترة قواعد الاحتفاظ بالبيانات غير الصالحة (متقدّمة)

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

يجب تجنُّب المكتبات التي تتضمّن قواعد إبقاء تحتفظ برمز برمجي يجب إزالته. ولكن إذا كان عليك استخدامها، يمكنك فلترة القواعد كما هو موضّح في الرمز التالي:

// If you're using AGP 8.4 and higher
buildTypes {
    release {
        optimization.keepRules {
          it.ignoreFrom("com.somelibrary:somelibrary")
        }
    }
}

// If you're using AGP 7.3-8.3
buildTypes {
    release {
        optimization.keepRules {
          it.ignoreExternalDependencies("com.somelibrary:somelibrary")
        }
    }
}

دراسة حالة: لماذا يتعطّل Gson عند إجراء تحسينات؟

‫Gson هي مكتبة تسلسل غالبًا ما تتسبّب في حدوث مشاكل في تحسين التطبيق لأنّها تستخدم الانعكاس بشكل كبير. يوضّح مقتطف الرمز التالي كيفية استخدام Gson عادةً، ما قد يؤدي بسهولة إلى حدوث أعطال أثناء وقت التشغيل. لاحظ أنّه عند استخدام Gson للحصول على قائمة بعناصر User، لن تحتاج إلى استدعاء الدالة الإنشائية أو تمرير مصنع إلى الدالة fromJson(). إنّ إنشاء فئات يحدّدها التطبيق أو استخدامها بدون أيّ مما يلي يشير إلى أنّ إحدى المكتبات قد تستخدم انعكاسًا مفتوحًا:

  • فئة التطبيق التي تنفّذ مكتبة أو واجهة أو فئة عادية
  • مكوّن إضافي لإنشاء الرموز البرمجية مثل KSP
class User(val name: String)
class UserList(val users: List<User>)

// This code runs in debug mode, but crashes when optimizations are enabled
Gson().fromJson("""[{"name":"myname"}]""", User::class.java).toString()

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

لتحديد الفئات بطريقة متوافقة مع قواعد المستهلك في Gson، استخدِم المقتطف التالي كمرجع:

class User(@com.google.gson.annotations.SerializedName("name") val name: String)
class UserList(@com.google.gson.annotations.SerializedName("users") val users: List<User>)

يُرجى العِلم أنّ Room وHilt وMoshi مع إنشاء الرموز تنشئ أنواعًا محدّدة في التطبيق، ولكنها تستخدم إنشاء الرموز لتجنُّب الحاجة إلى الانعكاس.