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

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

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

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

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

اختَر المكتبات التي تستخدم إنشاء الرموز البرمجية (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 {:.external} أو Moshi مع إنشاء الرموز البرمجية.
  • تجنَّب المكتبات التي تتضمّن قواعد احتفاظ على مستوى الحزمة إن أمكن ذلك. يمكن أن تساعد قواعد الاحتفاظ على مستوى الحزمة في حلّ الأخطاء، ولكن يجب في النهاية تحسين قواعد الاحتفاظ الواسعة للاحتفاظ بالرموز البرمجية المطلوبة فقط. لمزيد من المعلومات، اطّلِع على مقالة تطبيق التحسينات تدريجيًا.
  • قبل نشر تطبيق يستخدم مكتبة تابعة لجهة خارجية، استخدِم أداة R8 Configuration Analyzer لتدقيق قواعد الاحتفاظ التي توفّرها. من خلال مراجعة التقرير، يمكنك التأكّد مما إذا كانت قواعد الاحتفاظ الخاصة بالمكتبة واسعة جدًا، ما يمنع R8 من إجراء تحسينات مهمة على قاعدة الرموز البرمجية. يضمن هذا التحقّق أنّ المكتبات التي تختارها تتوافق مع أهداف أداء تطبيقك ولا تؤدي إلى زيادة غير ضرورية في حجم الإعدادات.
  • يجب ألا تطلب منك المكتبات نسخ قواعد الاحتفاظ ولصقها من المستندات في ملف في مشروعك، وخاصةً قواعد الاحتفاظ على مستوى الحزمة. تصبح هذه القواعد عبئًا على مطوّر التطبيق على المدى الطويل، ويصعب تحسينها وتغييرها بمرور الوقت.

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

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

فلترة قواعد الاحتفاظ غير الجيدة (ميزة متقدّمة)

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

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

// 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 للحصول على قائمة بكائنات المستخدم، لا تستدعي الدالة الإنشائية أو تمرِّر مصنعًا إلى الدالة 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، يمكنه إعادة تسمية الحقول أو إزالة الدوال الإنشائية التي لا تبدو مستخدَمة، ما يؤدي إلى تعطُّل تطبيقك. إذا كنت تستخدم أي مكتبات أخرى بطرق مماثلة، عليك التأكّد من أنّها لن تتداخل مع تحسين التطبيق، وإذا كانت تتداخل، تجنَّبها.

استخدِم @SerializedName مع إصدارات Gson الأحدث

لتحديد نماذج البيانات بطريقة متوافقة مع قواعد المستهلك في Gson، أضِف تعليقًا توضيحيًا إلى حقولك باستخدام @SerializedName، كما هو موضّح في المقتطف التالي:

import com.google.gson.annotations.SerializedName

class User(@SerializedName("name") val name: String)
class UserList(@SerializedName("users") val users: List<User>)

باستخدام التعليق التوضيحي @SerializedName، تسمح لـ R8 بمطابقة فئات النموذج مع قواعد الاحتفاظ المجمّعة في الإصدار 2.11.0 من Gson والإصدارات الأحدث. يحافظ R8 تلقائيًا على الحقول التي تم وضع تعليقات توضيحية لها والدوال الإنشائية الضرورية، ما يتيح لك الاعتماد على القواعد المجمّعة في المكتبة بدون الحاجة إلى الحفاظ على إعدادات ProGuard اليدوية في مشروعك.

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