التحسين لمؤلفي المكتبات

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

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

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

التعرّف على أنواع قواعد الاحتفاظ بالبيانات

هناك نوعان مختلفان من قواعد الاحتفاظ التي يمكنك استخدامها في المكتبات:

  • يجب أن تحدّد قواعد الاحتفاظ بالمستهلك القواعد التي تحتفظ بأي شيء تعكسه المكتبة. إذا كانت إحدى المكتبات تستخدم الانعكاس أو JNI لاستدعاء الرمز الخاص بها أو الرمز الذي يحدّده تطبيق العميل، يجب أن تصف هذه القواعد الرمز الذي يجب الاحتفاظ به. يجب أن تتضمّن المكتبات قواعد الاحتفاظ بالمستهلكين التي تستخدم التنسيق نفسه المستخدَم في قواعد الاحتفاظ بالتطبيقات. يتم تجميع هذه القواعد في عناصر مكتبة (ملفات AAR أو JAR)، ويتم استخدامها تلقائيًا أثناء تحسين تطبيق Android عند استخدام المكتبة. يتم الاحتفاظ بهذه القواعد في الملف المحدّد باستخدام السمة consumerProguardFiles في ملف build.gradle.kts (أو build.gradle). لمزيد من المعلومات، يُرجى الاطّلاع على كتابة قواعد الاحتفاظ بالبيانات الخاصة بالمستهلكين.
  • يتم تطبيق قواعد الاحتفاظ بإنشاء المكتبة عند إنشاء مكتبتك. ولا تحتاج إليها إلا إذا قررت تحسين مكتبتك جزئيًا في وقت الإنشاء. ويجب أن يمنعوا إزالة واجهة برمجة التطبيقات العامة للمكتبة، وإلا لن تكون واجهة برمجة التطبيقات العامة متوفّرة في توزيع المكتبة، ما يعني أنّه لن يتمكّن مطوّرو التطبيقات من استخدام المكتبة. يتم الاحتفاظ بهذه القواعد في الملف المحدّد باستخدام السمة proguardFiles في ملف build.gradle.kts (أو build.gradle). لمزيد من المعلومات، يُرجى الاطّلاع على تحسين إنشاء مكتبة AAR.

المتطلبات والإرشادات المتعلقة بالتحسين

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

الالتزام بمتطلبات التحسين

يُعدّ عدم الكفاءة في المكتبات من الأسباب الرئيسية لتضخّم التطبيقات، وإهدار الذاكرة، وبطء عمليات بدء التشغيل، وأخطاء ANR (أخطاء "التطبيق لا يستجيب"). يجب أن تتجنّب المكتبات انتهاك المتطلبات التالية لتجنُّب خفض جودة التطبيق وتجربة المستخدم بشكل كبير.

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

    لا تُدرِج قواعد الاحتفاظ على مستوى الحزمة (مثل -keep class com.mylibrary.** {*; }) للحِزم في مكتبتك أو المكتبات الأخرى المُشار إليها. وتحدّ هذه القواعد من إمكانية تحسين هذه الحِزم في جميع التطبيقات التي تستخدم مكتبتك.

  • عدم استخدام قواعد عامة غير ملائمة: لا تستخدِم أبدًا خيارات عامة مثل -dontobfuscate أو -allowaccessmodification.

  • استخدام إنشاء الرموز البرمجية بدلاً من الانعكاس كلما أمكن ذلك: استخدِم إنشاء الرموز البرمجية (codegen) بدلاً من الانعكاس كلما أمكن ذلك. يُعدّ كل من إنشاء الرموز البرمجية تلقائيًا والانعكاس من الطرق الشائعة لتجنُّب الرموز النموذجية عند البرمجة، ولكن إنشاء الرموز البرمجية تلقائيًا يكون أكثر توافقًا مع أدوات تحسين التطبيقات، مثل R8.

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

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

    تستخدم العديد من المكتبات الحديثة إنشاء الرموز بدلاً من الانعكاس. راجِع KSP للحصول على نقطة دخول شائعة تستخدمها Room وDagger2 وغيرها الكثير.

  • التوافق مع وضع R8 الكامل: يجب ألا تتعطّل مكتبتك عند تفعيل وضع R8 الكامل. يُعدّ الوضع الكامل في R8 الوضع الذي يُنصح باستخدامه مع R8، وهو الوضع التلقائي منذ الإصدار 8.0 من Android Gradle Plugin الذي أصبح إصدارًا ثابتًا في عام 2023. إذا تعذّر تشغيل مكتبتك بسبب R8، الحل هو تحديد نقطة الدخول المحدّدة للانعكاس أو JNI وإضافة قاعدة مستهدَفة، وليس الاحتفاظ بالحزمة بأكملها.

اقتراحات إضافية

بالإضافة إلى متطلبات التحسين، إليك بعض الاقتراحات الإضافية.

  • لا تستخدِم -repackageclasses في ملف قواعد الاحتفاظ بالمحتوى الخاص بالمستهلكين في مكتبتك. ومع ذلك، لتحسين عملية إنشاء المكتبة، يمكنك استخدام -repackageclasses مع اسم حزمة داخلي، مثل <your.library.package>.internal، في ملف قواعد الاحتفاظ بالإنشاء الخاص بالمكتبة. ويمكن أن يساعد ذلك في تحسين كفاءة المكتبة في التطبيقات غير المحسَّنة. ومع ذلك، ليس من الضروري عادةً إجراء ذلك، لأنّه يجب أيضًا تحسين التطبيقات.
  • عليك توضيح أي سمات تحتاج إليها لتعمل مكتبتك في ملفات قواعد الاحتفاظ بالمكتبة، حتى إذا كان هناك تداخل مع السمات المحدّدة في proguard-android-optimize.txt.
  • إذا كنت بحاجة إلى السمات التالية في توزيع مكتبتك، احتفِظ بها في ملف قواعد الاحتفاظ في إصدار مكتبتك، ولا تحتفِظ بها في ملف قواعد الاحتفاظ في إصدار مكتبتك المخصّص للمستهلكين:
    • AnnotationDefault
    • EnclosingMethod
    • Exceptions
    • InnerClasses
    • RuntimeInvisibleAnnotations
    • RuntimeInvisibleParameterAnnotations
    • RuntimeInvisibleTypeAnnotations
    • RuntimeVisibleAnnotations
    • RuntimeVisibleParameterAnnotations
    • RuntimeVisibleTypeAnnotations
    • Signature
  • على مطوّري المكتبات الاحتفاظ بالسمة RuntimeVisibleAnnotations في قواعد الاحتفاظ الخاصة بالمستهلكين إذا تم استخدام التعليقات التوضيحية في وقت التشغيل.
  • يجب ألا يستخدم مؤلفو المكتبات الخيارات العامة التالية في قواعد الاحتفاظ الخاصة بالمستهلكين:
    • -include
    • -basedirectory
    • -injars
    • -outjars
    • -libraryjars
    • -repackageclasses
    • -flattenpackagehierarchy
    • -allowaccessmodification
    • -renamesourcefileattribute
    • -ignorewarnings
    • -addconfigurationdebugging
    • -printconfiguration
    • -printmapping
    • -printusage
    • -printseeds
    • -applymapping
    • -obfuscationdictionary
    • -classobfuscationdictionary
    • -packageobfuscationdictionary

الحالات التي يكون فيها التفكير الذاتي مفيدًا

إذا كان عليك استخدام الانعكاس، يجب أن يكون ذلك في أحد الخيارين التاليين فقط:

  • أنواع مستهدَفة معيّنة (منفّذو واجهات أو فئات فرعية معيّنة)
  • كتابة الرمز باستخدام تعليق توضيحي لوقت تشغيل معيّن

يؤدي استخدام الانعكاس بهذه الطريقة إلى الحد من تكلفة وقت التشغيل، ويتيح كتابة قواعد الاحتفاظ بالمستهلكين المستهدَفين.

هذا النوع المحدّد والمستهدَف من الانعكاس هو نمط يمكنك ملاحظته في كل من إطار عمل Android (على سبيل المثال، عند تضخيم الأنشطة وطرق العرض والرسومات القابلة للرسم) ومكتبات AndroidX (على سبيل المثال، عند إنشاء WorkManager ListenableWorkers أو RoomDatabases). في المقابل، لا يكون الانعكاس المفتوح النهاية في Gson مناسبًا للاستخدام في تطبيقات Android.

الأفكار الخاطئة الشائعة

قد تؤدي بعض المفاهيم الخاطئة الشائعة إلى إعداد R8 بشكل غير صحيح. وتشمل هذه الخدمات ما يلي:

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

  • تجاوز تحسين المكتبات التي تم تشويشها: من الأخطاء الشائعة إغفال تحسين إحدى المكتبات لأنّه تم تحسينها أو تشويشها عند تجميعها في ملف AAR (أرشيف Android) أو JAR (أرشيف Java). تكون عمليات التحسين أثناء إنشاء المكتبة محدودة، ويجب ألا يوقف تطبيقك عملية تحسين المكتبة من خلال تضمينها في قاعدة الاحتفاظ. لمزيد من المعلومات، يُرجى الاطّلاع على تحسين إنشاء مكتبة AAR.

  • الفهم غير الصحيح للخيار -keep يمنع قاعدة -keep تنفيذ أي من عمليات التحسين. لمزيد من المعلومات، يُرجى الاطّلاع على اختيار خيار الاحتفاظ المناسب.

ضبط حِزم القواعد

لضمان تطبيق قواعد الاحتفاظ بالمستهلكين بشكل صحيح، يجب تجميعها بشكل مناسب حسب تنسيق المكتبة.

مكتبات AAR

لإضافة قواعد المستهلكين إلى مكتبة AAR، استخدِم الخيار consumerProguardFiles في نص برمجة إنشاء وحدة مكتبة Android. لمزيد من المعلومات، يُرجى الاطّلاع على إرشادات إنشاء وحدات المكتبة.

Kotlin

android {
    defaultConfig {
        consumerProguardFiles("consumer-proguard-rules.pro")
    }
    ...
}

Groovy

android {
    defaultConfig {
        consumerProguardFiles 'consumer-proguard-rules.pro'
    }
    ...
}

مكتبات JAR

لتضمين القواعد مع مكتبة Kotlin أو Java التي يتم شحنها كملف JAR، ضَع ملف القواعد في الدليل META-INF/proguard/ لملف JAR النهائي، مع أي اسم ملف. على سبيل المثال، إذا كان الرمز البرمجي الخاص بك في <libraryroot>/src/main/kotlin، ضَع ملف قواعد المستهلك في <libraryroot>/src/main/resources/META-INF/proguard/consumer-proguard-rules.pro وسيتم تجميع القواعد في الموقع الصحيح في ملف JAR الناتج.

تأكَّد من صحة قواعد حِزم JAR النهائية من خلال التحقّق من أنّ القواعد موجودة في الدليل META-INF/proguard.

تحسين إنشاء مكتبة AAR (إعدادات متقدّمة)

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

إذا كنت لا تزال تريد تحسين مكتبتك في وقت الإنشاء، يتيح لك المكوّن الإضافي لنظام Gradle المتوافق مع Android إجراء ذلك.

Kotlin

android {
    buildTypes {
        release {
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
        configureEach {
            consumerProguardFiles("consumer-rules.pro")
        }
    }
}

Groovy

android {
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles
                getDefaultProguardFile('proguard-android-optimize.txt'),
                'proguard-rules.pro'
        }
        configureEach {
            consumerProguardFiles "consumer-rules.pro"
        }
    }
}

يُرجى العِلم أنّ سلوك proguardFiles يختلف كثيرًا عن سلوك consumerProguardFiles:

  • يتم استخدام proguardFiles في وقت الإنشاء، وغالبًا ما يتم استخدامها مع getDefaultProguardFile("proguard-android-optimize.txt")، لتحديد الجزء الذي يجب الاحتفاظ به من مكتبتك أثناء إنشاء المكتبة. في الحد الأدنى، هذه هي واجهة برمجة التطبيقات العامة.
  • في المقابل، يتم تجميع consumerProguardFiles في المكتبة للتأثير في التحسينات التي تحدث لاحقًا أثناء إنشاء تطبيق يستخدم مكتبتك.

على سبيل المثال، إذا كانت مكتبتك تستخدم الانعكاس لإنشاء فئات داخلية، قد تحتاج إلى تحديد قواعد الإبقاء في كل من proguardFiles وconsumerProguardFiles.

إذا كنت تستخدم -repackageclasses في إصدار مكتبتك، أعِد تجميع الفئات في حزمة فرعية داخل حزمة مكتبتك. على سبيل المثال، استخدِم -repackageclasses 'com.example.mylibrary.internal' بدلاً من -repackageclasses 'internal'.

استخدام إصدارات مختلفة من R8 (إعدادات متقدّمة)

يمكنك تخصيص القواعد لاستهداف إصدارات معيّنة من R8. يتيح ذلك لمكتبتك العمل على النحو الأمثل في المشاريع التي تستخدم إصدارات أحدث من R8، مع السماح بمواصلة استخدام القواعد الحالية في المشاريع التي تستخدم إصدارات أقدم من R8.

لتحديد قواعد R8 المستهدَفة، عليك تضمينها في الدليل META-INF/com.android.tools داخل classes.jar الخاص بملف AAR أو في الدليل META-INF/com.android.tools الخاص بملف JAR.

In an AAR library:
    proguard.txt (legacy location, the file name must be "proguard.txt")
    classes.jar
    └── META-INF
        └── com.android.tools (location of targeted R8 rules)
            ├── r8-from-<X>-upto-<Y>/<R8-rule-files>
            └── ... (more directories with the same name format)

In a JAR library:
    META-INF
    ├── proguard/<ProGuard-rule-files> (legacy location)
    └── com.android.tools (location of targeted R8 rules)
        ├── r8-from-<X>-upto-<Y>/<R8-rule-files>
        └── ... (more directories with the same name format)

في الدليل META-INF/com.android.tools، يمكن أن تتوفّر عدة أدلة فرعية بأسماء على شكل r8-from-<X>-upto-<Y> للإشارة إلى إصدارات R8 التي تم إنشاء القواعد لها. يمكن أن يحتوي كل دليل فرعي على ملف واحد أو أكثر يتضمّن قواعد R8، مع أي أسماء ملفات وامتدادات.

يُرجى العِلم أنّ الأجزاء -from-<X> و-upto-<Y> اختيارية، وأنّ الإصدار <Y> حصري، وأنّ نطاقات الإصدارات تكون عادةً متواصلة ولكن يمكن أن تتداخل أيضًا.

على سبيل المثال، r8 وr8-upto-8.0.0 وr8-from-8.0.0-upto-8.2.0 وr8-from-8.2.0 هي أسماء أدلة تمثّل مجموعة من قواعد R8 المستهدَفة. يمكن لأي إصدار من R8 استخدام القواعد ضمن الدليل r8. يمكن استخدام القواعد ضمن الدليل r8-from-8.0.0-upto-8.2.0 من خلال R8 من الإصدار 8.0.0 حتى الإصدار 8.2.0 باستثناء الإصدار 8.2.0.

يستخدم المكوّن الإضافي لنظام Gradle المتوافق مع Android هذه المعلومات لاختيار جميع القواعد التي يمكن استخدامها مع إصدار R8 الحالي. إذا لم تحدّد المكتبة قواعد R8 مستهدَفة، سيختار المكوّن الإضافي Android Gradle القواعد من المواقع الجغرافية القديمة (proguard.txt لملف AAR أو META-INF/proguard/<ProGuard-rule-files> لملف JAR).