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

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

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