تصغير تطبيقك وتعتيمه وتحسينه

لجعل تطبيقك صغيرًا وسريعًا قدر الإمكان، عليك تحسين ملف الإصدار تقليل حجمه باستخدام isMinifyEnabled = true.

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

عند إنشاء مشروعك باستخدام الإصدار 3.4.0 من "مكوّن Android Gradle الإضافي" أو إصدار أحدث، لم يعُد المكوّن الإضافي يستخدم ProGuard لإجراء تحسين على الرموز البرمجية في وقت الترجمة. بدلاً من ذلك، يعمل المكوّن الإضافي مع مُجمِّع R8 لمعالجة المهام التالية في وقت الترجمة:

  • تصغير الرموز (أو إزالة العناصر غير الضرورية من الشجرة): وهي عملية ترصد بأمان الفئات والحقول والأساليب والسمات غير المستخدَمة وتزيلها من تطبيقك ومكوّنات مكتبته التابعة (ما يجعلها أداة قيّمة للتعامل مع الحد الأقصى لعدد الإحالات التي تبلغ 64 ألفًا). على سبيل المثال، إذا كنت تستخدم بضع واجهات برمجة تطبيقات فقط من مكتبة تابعة، يمكن أن يؤدي تقليل حجم الرموز إلى تحديد رمز المكتبة الذيلا يستخدمه تطبيقك وإزالة هذا الرمز فقط من تطبيقك. لقراءة مزيد من المعلومات، انتقِل إلى القسم الذي يتناول كيفية تقليل حجم الرموز.
  • تصغير حجم الموارد: تزيل هذه العملية الموارد غير المستخدَمة من تطبيقك المُحزم، بما في ذلك الموارد غير المستخدَمة في العناصر الاعتمادية للمكتبة في تطبيقك. ويتم ذلك بالتزامن مع تقليل حجم الرموز البرمجية، بحيث يمكن إزالة أي موارد لم يعُد يتمّ الرجوع إليها بأمان بعد إزالة الرموز البرمجية غير المستخدَمة. لمعرفة المزيد من المعلومات، انتقِل إلى القسم الخاص بكيفية تقليل الموارد.
  • التحسين: يفحص الرمز البرمجي ويعيد كتابته لتحسين أداء بيئة التشغيل وتقليل حجم ملفات DEX الخاصة بالتطبيق. يؤدي ذلك إلى تحسين أداء الرمز البرمجي في وقت تشغيل الرمز بنسبة تصل إلى 30%، ما يؤدي إلى تحسين كبير عند بدء التشغيل وتوقيت عرض الإطارات. على سبيل المثال، إذا رصدت أداة R8 أنّه لا يتم اتّخاذ فرع else {} لعبارة if/else معيّنة مطلقًا، تزيل أداة R8 الرمز البرمجي لفرع else {}. للاطّلاع على مزيد من المعلومات، يمكنك الانتقال إلى القسم الذي يتناول تحسين الرموز.
  • التشويش (أو تصغير المعرّف): يُخفِّض هذا الإجراء اسم الفصول والعناصر، ما يؤدي إلى تقليل أحجام ملفات DEX. لمزيد من المعلومات، انتقِل إلى القسم المخصص لكيفية تشويش الرمز البرمجي.

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

تفعيل ميزة تقليل الحجم وإخفاء مفاتيح فك التشفير والتحسين

عند استخدام الإصدار 3.4 من "استوديو Android" أو الإصدار 3.4.0 من المكوّن الإضافي لـ Gradle في Android والإصدارات الأحدث، يكون R8 هو compilador التلقائي الذي يحوّل الرمز الثنائي لمشروعك البرمجي المكتوب بلغة Java إلى تنسيق DEX الذي يعمل على نظام Android الأساسي. ومع ذلك، عند إنشاء مشروع جديد باستخدام Android Studio، لا يتم تفعيل تصغير الرموز البرمجية وتشويشها وتحسينها تلقائيًا. ويرجع ذلك إلى أنّ تحسينات وقت التجميع هذه تزيد من وقت إنشاء مشروعك وقد تظهر أخطاءً في حال عدم تخصيص الرمز البرمجي الذي تريد الاحتفاظ به بالشكل الكافي.

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

Kotlin

android {
    buildTypes {
        getByName("release") {
            // Enables code shrinking, obfuscation, and optimization for only
            // your project's release build type. Make sure to use a build
            // variant with `isDebuggable=false`.
            isMinifyEnabled = true

            // Enables resource shrinking, which is performed by the
            // Android Gradle plugin.
            isShrinkResources = true

            proguardFiles(
                // Includes the default ProGuard rules files that are packaged with
                // the Android Gradle plugin. To learn more, go to the section about
                // R8 configuration files.
                getDefaultProguardFile("proguard-android-optimize.txt"),

                // Includes a local, custom Proguard rules file
                "proguard-rules.pro"
            )
        }
    }
    ...
}

رائع

android {
    buildTypes {
        release {
            // Enables code shrinking, obfuscation, and optimization for only
            // your project's release build type. Make sure to use a build
            // variant with `debuggable false`.
            minifyEnabled true

            // Enables resource shrinking, which is performed by the
            // Android Gradle plugin.
            shrinkResources true

            // Includes the default ProGuard rules files that are packaged with
            // the Android Gradle plugin. To learn more, go to the section about
            // R8 configuration files.
            proguardFiles getDefaultProguardFile(
                    'proguard-android-optimize.txt'),
                    'proguard-rules.pro'
        }
    }
    ...
}

ملفات إعداد R8

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

المصدر الموقع الجغرافي الوصف
استوديو Android <module-dir>/proguard-rules.pro عند إنشاء وحدة جديدة باستخدام "استوديو Android"، تنشئ بيئة التطوير المتكاملة (IDE) ملف proguard-rules.pro في الدليل الجذري لتلك الوحدة.

لا يطبّق هذا الملف أي قواعد تلقائيًا. لذلك، أدرِج قواعد ProGuard الخاصة بك هنا، مثل قواعد الحفظ المخصّصة.

المكوّن الإضافي لنظام Gradle المتوافق مع Android يتم إنشاؤها بواسطة المكوّن الإضافي لنظام Gradle المتوافق مع Android في وقت الترجمة. ينشئ المكوّن الإضافي لنظام Gradle المتوافق مع Android proguard-android-optimize.txt، والذي يتضمّن قواعد مفيدة لمعظم مشاريع Android ويفعِّل @Keep* التعليقات التوضيحية.

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

ملاحظة: يتضمّن المكوّن الإضافي لنظام Gradle المتوافق مع Android ملفات قواعد ProGuard إضافية محدّدة مسبقًا، ولكن ننصحك باستخدام proguard-android-optimize.txt.

العناصر التابعة للمكتبة

في إحدى مكتبات الاقتراحات المطبّقة تلقائيًا:
proguard.txt

في مكتبة JAR:
META-INF/proguard/<ProGuard-rules-file>

بالإضافة إلى هذه المواقع، يتيح المكوّن الإضافي لـ Android Gradle 3.6 أو الإصدارات الأحدث أيضًا قواعد التصغير المستهدفة.

إذا تم نشر مكتبة AAR أو JAR باستخدام ملف القواعد الخاص بها، وضمّنت تلك المكتبة كتبعية لوقت التجميع، يطبّق R8 تلقائيًا هذه القواعد عند تجميع مشروعك.

بالإضافة إلى قواعد ProGuard التقليدية، يتيح المكوّن الإضافي لنظام Gradle المتوافق مع Android 3.6 أو الإصدارات الأحدث أيضًا استخدام قواعد تصغير مستهدفة. هذه هي القواعد التي تستهدِف أدوات تصغير الحجم (R8 أو ProGuard)، بالإضافة إلى إصدارات معيّنة لأدوات تقليص الحجم.

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

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

أداة تجميع حِزم الموارد في Android 2 (AAPT2) بعد إنشاء مشروعك باستخدام minifyEnabled true: <module-dir>/build/intermediates/aapt_proguard_file/.../aapt_rules.txt ينشئ AAPT2 قواعد الاحتفاظ بالبيانات استنادًا إلى الإشارات إلى الفئات فيملف التسمية والتنسيقات وموارد التطبيق الأخرى. على سبيل المثال، يتضمّن ملف AAPT2 قاعدة الاحتفاظ لكل نشاط تسجِّله في بيان تطبيقك كنقطة دخول.
ملفات الإعداد المخصّصة عندما تنشئ وحدة جديدة تلقائيًا باستخدام "استوديو Android"، ينشئ IDE <module-dir>/proguard-rules.pro لك لإضافة قواعدك الخاصة. يمكنك تضمين إعدادات إضافية، وتطبّق R8 هذه الإعدادات في وقت الترجمة.

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

لعرض تقرير كامل عن جميع القواعد التي تطبّقها أداة R8 عند إنشاء مشروعك، أدرِج ما يلي في ملف proguard-rules.pro الخاص بالوحدة:

// You can specify any path and filename.
-printconfiguration ~/tmp/full-r8-config.txt

قواعد التصغير المستهدَفة

يتوافق المكوّن الإضافي لـ Android Gradle 3.6 أو الإصدارات الأحدث مع قواعد المكتبات التي تستهدف أدوات تصغير معيّنة (R8 أو ProGuard)، بالإضافة إلى إصدارات معيّنة من أدوات التصغير. يتيح ذلك لمطوّري المكتبات تخصيص قواعدهم للعمل على النحو الأمثل في المشاريع التي تستخدم إصدارات جديدة من أداة تصغير الملفات، مع السماح بمواصلة استخدام القواعد الحالية في المشاريع التي تستخدم إصدارات قديمة من أداة تصغير الملفات.

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

In an AAR library:
    proguard.txt (legacy location)
    classes.jar
    └── META-INF
        └── com.android.tools (targeted shrink rules location)
            ├── r8-from-<X>-upto-<Y>/<R8-rules-file>
            └── proguard-from-<X>-upto-<Y>/<ProGuard-rules-file>

In a JAR library:
    META-INF
    ├── proguard/<ProGuard-rules-file> (legacy location)
    └── com.android.tools (targeted shrink rules location)
        ├── r8-from-<X>-upto-<Y>/<R8-rules-file>
        └── proguard-from-<X>-upto-<Y>/<ProGuard-rules-file>

وهذا يعني أنّه يتم تخزين قواعد التصغير المستهدفة في الدليل META-INF/com.android.tools لملف JAR أو في الدليل META-INF/com.android.tools داخل classes.jar لملف AAR.

ضمن هذا الدليل، يمكن أن يكون هناك أدلة متعددة بأسماء على شكل r8-from-<X>-upto-<Y> أو proguard-from-<X>-upto-<Y> للإشارة إلى إصدارات برامج التصغير التي تم وضع القواعد داخل الأدلة من أجلها. يُرجى العِلم أنّ الجزءَين -from-<X> و-upto-<Y> اختياريان، وأنّ الإصدار <Y> حصري، ويجب أن تكون نطاقات الإصدارات مستمرة.

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

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

يمكن لمطوّري المكتبات اختيار تضمين إما قواعد تقليص متوافقة أو قواعد ProGuard القديمة في مكتباتهم، أو كلا النوعين إذا كانوا يريدون الحفاظ على التوافق مع المكوّن الإضافي لنظام Gradle المتوافق مع Android الأقدم من 3.6 أو الأدوات الأخرى.

تضمين إعدادات إضافية

عند إنشاء مشروع أو وحدة جديدة باستخدام "استوديو Android"، ينشئ IDE ملف <module-dir>/proguard-rules.pro لك لتضمين قواعدك الخاصة. يمكنك أيضًا تضمين قواعد إضافية من ملفات أخرى من خلال إضافتها إلى سمة proguardFiles في نص إنشاء الوحدة.

على سبيل المثال، يمكنك إضافة قواعد خاصة بكل صيغة إصدار من خلال إضافة سمة proguardFiles أخرى في مجموعة productFlavor المقابلة. يضيف ملف Gradle التالي flavor2-rules.pro إلى نكهة المنتج flavor2. الآن، يستخدم flavor2 جميع قواعد ProGuard الثلاث لأنّه يتم أيضًا تطبيق القواعد من release الحزمة.

يمكنك أيضًا إضافة السمة testProguardFiles التي تحدّد قائمة بملفات ProGuard المضمّنة في حِزمة APK الاختبارية فقط:

Kotlin

android {
    ...
    buildTypes {
        getByName("release") {
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                // List additional ProGuard rules for the given build type here. By default,
                // Android Studio creates and includes an empty rules file for you (located
                // at the root directory of each module).
                "proguard-rules.pro"
            )
            testProguardFiles(
                // The proguard files listed here are included in the
                // test APK only.
                "test-proguard-rules.pro"
            )
        }
    }
    flavorDimensions.add("version")
    productFlavors {
        create("flavor1") {
            ...
        }
        create("flavor2") {
            proguardFile("flavor2-rules.pro")
        }
    }
}

رائع

android {
    ...
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles
                getDefaultProguardFile('proguard-android-optimize.txt'),
                // List additional ProGuard rules for the given build type here. By default,
                // Android Studio creates and includes an empty rules file for you (located
                // at the root directory of each module).
                'proguard-rules.pro'
            testProguardFiles
                // The proguard files listed here are included in the
                // test APK only.
                'test-proguard-rules.pro'
        }
    }
    flavorDimensions "version"
    productFlavors {
        flavor1 {
            ...
        }
        flavor2 {
            proguardFile 'flavor2-rules.pro'
        }
    }
}

تصغير الرمز

يتم تفعيل تقليص الرمز باستخدام R8 تلقائيًا عند ضبط السمة minifyEnabled على true.

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

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

يعرض الشكل 1 تطبيقًا يتضمّن تبعية مكتبة وقت التشغيل. أثناء فحص رمز التطبيق، يحدِّد R8 أنّه يمكن الوصول إلى الطرق foo() وfaz() وbar() من نقطة الدخول MainActivity.class. مع ذلك، لا يستخدم تطبيقك الفئة OkayApi.class أو الطريقة baz() الخاصة به مطلقًا في وقت التشغيل، وتزيل R8 هذا الرمز عند تقليص حجم التطبيق.

الشكل 1: في وقت الترجمة، ينشئ R8 رسمًا بيانيًا استنادًا إلى قواعد الاحتفاظ المجمّعة في مشروعك لتحديد الرمز البرمجي الذي لا يمكن الوصول إليه.

تحدِّد R8 نقاط الدخول من خلال -keep القواعد في ملفات إعدادات R8 الخاصة بالمشروع. وهذا يعني أنّ قواعد الاحتفاظ تحدّد الفئات التي يجب ألا يتخلّص منها R8 عند تصغير تطبيقك، ويعتبر R8 هذه الفئات بمثابة نقاط دخول محتملة إلى تطبيقك. ينشئ المكوّن الإضافي لـ Android Gradle وAAPT2 تلقائيًا قواعد الاحتفاظ التي تتطلّبها معظم مشاريع التطبيقات، مثل الأنشطة والعروض والخدمات في تطبيقك. ومع ذلك، إذا كنت بحاجة إلى تخصيص هذا السلوك التلقائي باستخدام قواعد احتفاظ إضافية، اطّلِع على القسم حول كيفية تخصيص الرمز الذي يجب الاحتفاظ به.

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

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

تخصيص الرمز المطلوب الاحتفاظ به

في معظم الحالات، يكون ملف قواعد ProGuard التلقائي (proguard-android-optimize.txt) كافيًا لتزيل R8 الرمز غير المستخدَم فقط. ومع ذلك، يصعب على R8 تحليل بعض الحالات بشكل صحيح وقد يزيل الرمز البرمجي الذي يحتاجه تطبيقك فعليًا. في ما يلي بعض الأمثلة على الحالات التي قد تؤدي فيها الأداة إلى إزالة الرمز البرمجي بشكل غير صحيح:

  • عندما يستدعي تطبيقك طريقة من Java Native Interface (JNI)
  • عندما يبحث تطبيقك عن رمز أثناء وقت التشغيل (مثلاً باستخدام ميزة "الاستكشاف")

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

لإصلاح الأخطاء وإجبار R8 على الاحتفاظ برمز معيّن، أضِف سطرًا يليه -keep في ملف قواعد ProGuard. مثلاً:

-keep public class MyClass

بدلاً من ذلك، يمكنك إضافة التعليق التوضيحي @Keep إلى الرمز الذي تريده الاحتفاظ به. تؤدي إضافة @Keep إلى فئة إلى إبقاء الفئة بأكملها كما هي. وإضافة الرمز إلى طريقة أو حقل سيحافظ على الطريقة أو الحقل (واسمه) intact بالإضافة إلى اسم الفئة. يُرجى العلم أنّ هذا التعليق التوضيحي لا يتوفّر إلا عند استخدام مكتبة التعليقات التوضيحية في AndroidX وعند تضمين ملف قواعد ProGuard المُجمَّع مع المكوّن الإضافي Gradle لنظام التشغيل Android، كما هو موضّح في القسم حول كيفية تفعيل التقليل.

هناك العديد من الاعتبارات التي يجب مراعاتها عند استخدام الخيار -keep. للحصول على مزيد من المعلومات عن تخصيص ملف القواعد، يُرجى قراءة دليل ProGuard. يوضّح قسم تحديد المشاكل وحلّها المشاكل الشائعة الأخرى التي قد تواجهها عند إزالة الرمز البرمجي.

إزالة المكتبات الأصلية

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

فريق الدعم المعني بالأعطال الداخلية في نظام التشغيل

تُبلغ أداة Google Play Console عن الأعطال الأصلية ضمن مؤشرات Android الحيوية. من خلال اتّباع بضع خطوات، يمكنك إنشاء وتحميل ملف أصلي لتصحيح أخطاء الترميز لتطبيقك. يتيح هذا الملف تفعيل عمليات تتبُّع تسلسُل استدعاء الدوال البرمجية الأصلية لتعطُّل التطبيق (التي تتضمّن أسماء الفئات والوظائف) في "مؤشرات Android الحيوية" لمساعدتك على تصحيح الأخطاء في تطبيقك أثناء مرحلة الإنتاج. تختلف هذه الخطوات حسب إصدار المكوّن الإضافي لنظام Gradle المتوافق مع Android المستخدَم في مشروعك ومخرجات التصميم لمشروعك.

الإصدار 4.1 من المكوّن الإضافي لنظام Gradle المتوافق مع Android أو إصدار أحدث

إذا كان مشروعك ينشئ مجموعة حزمات تطبيق Android، يمكنك تلقائيًا تضمين الملف الأصلي لتصحيح أخطاء الترميز فيها. لتضمين هذا الملف في إصدارات التطبيق، أضِف الخطوات التالية إلى ملف build.gradle.kts في تطبيقك:

android.buildTypes.release.ndk.debugSymbolLevel = { SYMBOL_TABLE | FULL }

اختَر مستوى رمز تصحيح الأخطاء من الخيارات التالية:

  • استخدِم SYMBOL_TABLE للحصول على أسماء الدوالّ في عمليات تتبُّع تسلسل استدعاء الدوالّ البرمجية التي تم ترميزها في Play Console. يتيح هذا المستوى استخدام العلامات المرجعية.
  • استخدِم FULL للحصول على أسماء الدوال البرمجية والملفات وأرقام الأسطر في عمليات تتبُّع تسلسل استدعاء الدوال البرمجية التي تم ترميزها في Play Console.

إذا كان مشروعك ينشئ حزمة APK، استخدِم إعداد إصدار build.gradle.kts المعروض أعلاه لإنشاء ملف تصحيح أخطاء الترميز الأصلي بشكل منفصل. حمِّل يدويًا ملف تصحيح أخطاء الترميز الأصلي إلى Google Play Console. وكجزء من عملية التصميم، يعمل المكوّن الإضافي لنظام Gradle المتوافق مع Android على إخراج هذا الملف في موقع المشروع التالي:

app/build/outputs/native-debug-symbols/variant-name/native-debug-symbols.zip

الإصدار 4.0 من المكوّن الإضافي لنظام Gradle المتوافق مع Android أو الأقدم (وأنظمة التصميم الأخرى)

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

app/build/intermediates/cmake/universal/release/obj/
├── armeabi-v7a/
│   ├── libgameengine.so
│   ├── libothercode.so
│   └── libvideocodec.so
├── arm64-v8a/
│   ├── libgameengine.so
│   ├── libothercode.so
│   └── libvideocodec.so
├── x86/
│   ├── libgameengine.so
│   ├── libothercode.so
│   └── libvideocodec.so
└── x86_64/
    ├── libgameengine.so
    ├── libothercode.so
    └── libvideocodec.so
  1. اضغط محتوى هذا الدليل:

    cd app/build/intermediates/cmake/universal/release/obj
    zip -r symbols.zip .
    
  2. يُرجى تحميل ملف symbols.zip يدويًا إلى Google Play Console.

تقليص الموارد

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

لتفعيل تقليص الموارد، يجب ضبط السمة shrinkResources على true في النص البرمجي للإصدار (بجانب minifyEnabled لتقليل حجم الرموز البرمجية). مثلاً:

Kotlin

android {
    ...
    buildTypes {
        getByName("release") {
            isShrinkResources = true
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android.txt"),
                "proguard-rules.pro"
            )
        }
    }
}

رائع

android {
    ...
    buildTypes {
        release {
            shrinkResources true
            minifyEnabled true
            proguardFiles
                getDefaultProguardFile('proguard-android.txt'),
                'proguard-rules.pro'
        }
    }
}

إذا لم يسبق لك إنشاء تطبيقك باستخدام minifyEnabled لتقليص حجم الرموز، جرِّب ذلك قبل تفعيل shrinkResources، لأنّك قد تحتاج إلى تعديل ملف proguard-rules.pro للاحتفاظ بالصفوف أو الطرق التي يتم إنشاؤها أو استدعاؤها ديناميكيًا قبل البدء في إزالة الموارد.

تخصيص الموارد التي تريد الاحتفاظ بها

إذا كانت هناك موارد معيّنة تريد الاحتفاظ بها أو تجاهلها، أنشئ ملف XML في مشروعك باستخدام علامة <resources> وحدِّد كل موارد للاحتفاظ بها في السمة tools:keep وكل موارد للتجاهلها في السمة tools:discard. تقبل كلتا السمتَين قائمة مفصولة بفواصل لأسماء الموارد. يمكنك استخدام علامة النجمة كأحد أحرف البدل.

مثلاً:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"
    tools:discard="@layout/unused2" />

احفظ هذا الملف في موارد مشروعك، على سبيل المثال، في res/raw/my.package.keep.xml. لا يُدرج هذا الملف في حِزمة الإصدار لتطبيقك.

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

قد يبدو تحديد الموارد التي تريد تجاهلها أمرًا غير منطقي عندما يمكنك بدلاً من ذلك حذفها، ولكن قد يكون ذلك مفيدًا عند استخدام أنواع الإصدارات. على سبيل المثال، يمكنك وضع جميع مواردك في دليل المشروع المشترك، ثم إنشاء ملف my.package.build.variant.keep.xml مختلف لكل نوع من أنواع الإصدارات عندما يكون من الواضح أنّ أحد الموارد المستخدَمة في الرمز البرمجي (وبالتالي لا تتم إزالته بواسطة أداة تصغير الملفات) ولكنك تعلم أنّه لن يتم استخدامه في نوع الإصدار المحدّد. من الممكن أيضًا أن تكون أدوات الإنشاء قد رصدت أحد الموارد بشكل غير صحيح على النحو المطلوب، وهو أمر ممكن لأنّ المُجمِّع يضيف أرقام تعريف الموارد مضمّنة، ثم قد لا يعرف محلل الموارد الفرق بين المورد المُشار إليه بشكل صحيح والقيمة الصحيحة المضمّنة في الرمز البرمجي والتي تُصادف أنّها تحمل القيمة نفسها.

تفعيل عمليات التحقّق الصارمة من المراجع

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

على سبيل المثال، تؤدي التعليمة البرمجية التالية إلى وضع علامة على جميع الموارد التي تحتوي على البادئة img_ على أنّها مستخدَمة.

Kotlin

val name = String.format("img_%1d", angle + 1)
val res = resources.getIdentifier(name, "drawable", packageName)

Java

String name = String.format("img_%1d", angle + 1);
res = getResources().getIdentifier(name, "drawable", getPackageName());

يبحث أداة تصغير الموارد أيضًا في جميع سلاسل الأرقام الثابتة في الرمز البرمجي، بالإضافة إلى موارد res/raw/ المختلفة، بحثًا عن عناوين res/raw/ URL للموارد بتنسيق مشابه لfile:///android_res/drawable//ic_plus_anim_016.png. إذا عثر على سلاسل مثل هذه أو غيرها تبدو أنّه يمكن استخدامها لإنشاء عناوين URL مماثلة، لا تتم إزالتها.

في ما يلي أمثلة على "وضع التقلّص الآمن" الذي يتم تفعيله تلقائيًا. ومع ذلك، يمكنك إيقاف هذه المعالجة "الاحتياطية" وتحديد أن يحتفظ أداة تصغير الموارد بالموارد التي يتم استخدامها فقط. للقيام بذلك، اضبط shrinkMode على strict فيملف keep.xml، على النحو التالي:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:shrinkMode="strict" />

إذا فعّلت وضع التصغير الصارم وكان الرمز يشير أيضًا إلى موارد باستخدام سلاسل يتم إنشاؤها ديناميكيًا، كما هو موضّح أعلاه، عليك إبقاء هذه الموارد يدويًا باستخدام السمة tools:keep.

إزالة الموارد البديلة غير المستخدَمة

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

على سبيل المثال، إذا كنت تستخدم مكتبة تتضمّن موارد لغوية (مثل AppCompat أو Google Play Services)، سيتضمّن تطبيقك كل سلاسل اللغات المترجَمة للرسائل في تلك المكتبات، سواء كانت بقية أجزاء تطبيقك مترجَمة إلى اللغات نفسها أم لا. إذا أردت الاحتفاظ فقط باللغات التي يتيح تطبيقك استخدامها رسميًا، يمكنك تحديد هذه اللغات باستخدام السمة resConfig. تتم إزالة أيّ موارد للّغات غير المحدّدة.

يوضح المقتطف التالي كيفية تقييد موارد اللغة لتقتصر على اللغتين الإنجليزية والفرنسية فقط:

Kotlin

android {
    defaultConfig {
        ...
        resourceConfigurations.addAll(listOf("en", "fr"))
    }
}

رائع

android {
    defaultConfig {
        ...
        resConfigs "en", "fr"
    }
}

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

بالنسبة إلى التطبيقات القديمة التي يتم إصدارها باستخدام حِزم APK (التي تم إنشاؤها قبل آب/أغسطس 2021)، يمكنك تخصيص كثافة الشاشة أو موارد ABI المطلوب تضمينها في حزمة APK من خلال إنشاء حِزم APK متعددة تستهدف كلّ منها إعدادات جهاز مختلفة.

دمج المراجع المكرّرة

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

لا يحدث دمج الموارد إلا عندما يتشارك ملفان أو أكثر اسم موارد ونوعًا ومؤهّلاً متطابقَين. يختار Gradle الملف الذي يعتقد أنّه هو الخيار الأفضل بين النُسخ المكرّرة (استنادًا إلى ترتيب الأولوية الموضّح أدناه) ويمرّر هذا المورد الوحيد إلى AAPT للتوزيع في العنصر النهائي.

يبحث Gradle عن الموارد المكرّرة في المواقع التالية:

  • إنّ الموارد الرئيسية المرتبطة بمجموعة المصادر الرئيسية تكون عمومًا مضمّنة في src/main/res/.
  • تداخلات الصيغ، من نوع الإصدار ونكهاته
  • تبعيات مشروع المكتبة.

يُدمج Gradle الموارد المكرّرة بالترتيب التدرّجي التالي للأولوية:

التبعيات → الخيار الرئيسي → نوع النكهة → نوع التصميم

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

إذا ظهرت موارد متطابقة في مجموعة المصادر نفسها، سيتعذّر على Gradle دمجها وينتج عنها خطأ دمج الموارد. يمكن أن يحدث ذلك إذا حدّدت عدة مجموعات مصادر في سمة sourceSet لملف build.gradle.kts، على سبيل المثال إذا كان كلّ منsrc/main/res/ وsrc/main/res2/ يحتويان على موارد متطابقة.

تشويش الرمز البرمجي

يهدف التعتيم إلى تقليل حجم تطبيقك من خلال تقصير أسماء الفئات والأساليب والحقول في تطبيقك. في ما يلي مثال على التشويش باستخدام R8:

androidx.appcompat.app.ActionBarDrawerToggle$DelegateProvider -> a.a.a.b:
androidx.appcompat.app.AlertController -> androidx.appcompat.app.AlertController:
    android.content.Context mContext -> a
    int mListItemLayout -> O
    int mViewSpacingRight -> l
    android.widget.Button mButtonNeutral -> w
    int mMultiChoiceItemLayout -> M
    boolean mShowTitle -> P
    int mViewSpacingLeft -> j
    int mButtonPanelSideLayout -> K

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

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

فك ترميز تتبُّع تسلسل استدعاء الدوال البرمجية الذي يتضمّن تشويشًا

بعد أن يُعَمِّم R8 رمزك البرمجي، يصبح من الصعب فهم تتبع تسلسل استدعاء الدوال (إن لم يكن مستحيلًا) لأنّه قد تم تغيير أسماء الفئات والطُرق. للحصول على تسلسل استدعاء الدوال البرمجية الأصلي، عليك إعادة تتبُّع تسلسل استدعاء الدوال البرمجية.

تحسين الرموز

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

  • إذا لم يستخدم الرمز البرمجي الفرع else {} مطلقًا لعبارة if/else معيّنة، قد تزيل R8 الرمز البرمجي للفرع else {}.
  • إذا كان الرمز البرمجي يستدعي طريقة في بضعة مواضع فقط، قد يزيل R8 الطريقة ويدمجها في مواضع الاتصال القليلة.
  • إذا رصدت أداة R8 أنّ فئة معيّنة تحتوي على فئة فرعية فريدة واحدة فقط، ولم يتم إنشاء مثيل للفئة (على سبيل المثال، فئة أساسية مجردة لا تستخدمها سوى فئة واحدة لتنفيذ ملموس)، يمكن لأداة R8 دمج الفئتَين وإزالة فئة من التطبيق.
  • لمزيد من المعلومات، يمكنك قراءة مشاركات مدوّنة تحسين الإصدار 8 التي كتبها "جاك وارتون".

لا تسمح لك لغة R8 بإيقاف التحسينات المنفصلة أو تفعيلها أو تعديل سلوك التحسين. في الواقع، يتجاهل R8 أي قواعد ProGuard تحاول تعديل عمليات التحسين التلقائية، مثل -optimizations و -optimizationpasses. وهذا القيد مهم لأنّه مع استمرار تحسُّن R8، يساعد الحفاظ على سلوك عادي للتحسينات فريق "استوديو Android" في تحديد المشاكل وحلّها بسهولة.

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

التأثير في أداء وقت التشغيل

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

في حال تفعيل R8، عليك أيضًا إنشاء ملفات تعريف بدء التشغيل لتحسين أداء بدء التشغيل.

تفعيل التحسينات المحسّنة

يتضمّن R8 مجموعة من التحسينات الإضافية (يُشار إليها باسم "الوضع الكامل") التي تؤدي إلى سلوكه بشكل مختلف عن ProGuard. يتم تفعيل هذه التحسينات بشكلٍ تلقائي منذ الإصدار 8.0.0 من المكوّن الإضافي لنظام Gradle المتوافق مع Android.

يمكنك إيقاف هذه التحسينات الإضافية من خلال تضمين ما يلي في ملف gradle.properties الخاص بمشروعك:

android.enableR8.fullMode=false

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

ومع ذلك، عند استخدام "الوضع الكامل"، لا يضع R8 هذا الافتراض، وإذا أكد R8 أن رمزك البرمجي لا يستخدم الفئة أبدًا في وقت التشغيل، فإنه يزيل الفئة من رمز DEX النهائي لتطبيقك. وهذا يعني أنّه إذا أردت الاحتفاظ بالفئة ومُنشئها static، عليك تضمين قاعدة الاحتفاظ في ملف القواعد لإجراء ذلك.

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

تتبُّع عمليات تتبُّع تسلسل استدعاء الدوال البرمجية

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

لاسترداد تتبع تسلسل استدعاء الدوال البرمجية الأصلي، يقدّم R8 أداة سطر الأوامر retrace، والتي يتم تجميعها مع حزمة أدوات سطر الأوامر.

لتمكين إعادة تتبُّع عمليات تتبُّع تسلسل استدعاء الدوال البرمجية لتطبيقك، عليك التأكّد من أنّ عملية المعالجة تحتفظ بمعلومات كافية لإجراء عملية إعادة التتبُّع من خلال إضافة القواعد التالية إلى ملف proguard-rules.pro الخاص بالوحدة:

-keepattributes LineNumberTable,SourceFile
-renamesourcefileattribute SourceFile

تحتفظ سمة LineNumberTable بمعلومات مواضع العناصر في الطرق، بحيث يتم طباعة هذه المواضع في عمليات تتبُّع تسلسل استدعاء الدوال البرمجية. تضمن السمة SourceFile أن تطبع جميع أوقات التشغيل المحتمَلة معلومات مواضع الفواصل. يضبط التوجيه -renamesourcefileattribute اسم الملف المصدر في تتبعات تسلسل استدعاء الدوال البرمجية على SourceFile فقط. ليس من الضروري استخدام اسم الملف المصدر الأصلي عند إعادة التتبّع لأنّ ملف الربط يحتوي على الملف المصدر الأصلي.

ينشئ R8 ملف mapping.txt في كل مرة يتم فيها تشغيله، والذي يحتوي على المعلومات اللازمة لربط عمليات تتبُّع تسلسل استدعاء الدوال البرمجية بعمليات التتبُّع الأصلية لتسلسل استدعاء الدوال البرمجية. يحفظ "استوديو Android" الملف في الدليل <module-name>/build/outputs/mapping/<build-type>/.

عند نشر تطبيقك على Google Play، يمكنك تحميل ملف mapping.txt لكل إصدار من إصدارات تطبيقك. وعند النشر باستخدام حِزم تطبيق Android، يتم تضمين هذا الملف تلقائيًا كجزء من محتوى حِزمة التطبيق. بعد ذلك، سيعيد Google Play تتبُّع عمليات تتبُّع تسلسل استدعاء الدوال البرمجية الواردة من المشاكل التي أبلغ عنها المستخدمون حتى تتمكّن من مراجعتها في Play Console. لمزيد من المعلومات، يُرجى الاطّلاع على مقالة "مركز المساعدة" التي تتناول كيفية إزالة تشويش عمليات تتبُّع تسلسل استدعاء الدوال البرمجية المتعلّقة بتعطُّل التطبيق.

تحديد المشاكل وحلّها باستخدام R8

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

إنشاء تقرير عن التعليمات البرمجية التي تمّت إزالتها (أو الاحتفاظ بها)

لمساعدتك في تحديد وحلّ مشاكل معيّنة في R8، قد يكون من المفيد الاطّلاع على تقرير بجميع الرموز التي أزالتها R8 من تطبيقك. لكل وحدة تريد إنشاء هذا التقرير لها، أضِف -printusage <output-dir>/usage.txt إلى ملف القواعد المخصّصة. عند تفعيل R8 وإنشاء تطبيقك، تُخرج R8 تقريرًا بالمسار واسم الملف اللذين حددتهما. يشبه تقرير الرمز المحذوف ما يلي:

androidx.drawerlayout.R$attr
androidx.vectordrawable.R
androidx.appcompat.app.AppCompatDelegateImpl
    public void setSupportActionBar(androidx.appcompat.widget.Toolbar)
    public boolean hasWindowFeature(int)
    public void setHandleNativeActionModesEnabled(boolean)
    android.view.ViewGroup getSubDecor()
    public void setLocalNightMode(int)
    final androidx.appcompat.app.AppCompatDelegateImpl$AutoNightModeManager getAutoNightModeManager()
    public final androidx.appcompat.app.ActionBarDrawerToggle$Delegate getDrawerToggleDelegate()
    private static final boolean DEBUG
    private static final java.lang.String KEY_LOCAL_NIGHT_MODE
    static final java.lang.String EXCEPTION_HANDLER_MESSAGE_SUFFIX
...

إذا أردت بدلاً من ذلك الاطّلاع على تقرير بنقاط الدخول التي تحدِّدها حزمة R8 من قواعد الاحتفاظ بالبيانات في مشروعك، ضمِّن -printseeds <output-dir>/seeds.txt في ملف القواعد المخصّصة. عند تفعيل R8 وإنشاء تطبيقك، تُخرج R8 تقريرًا بالمسار واسم الملف اللذين حددتهما. يبدو تقرير نقاط الدخول المحتفظ بها مشابهًا لما يلي:

com.example.myapplication.MainActivity
androidx.appcompat.R$layout: int abc_action_menu_item_layout
androidx.appcompat.R$attr: int activityChooserViewStyle
androidx.appcompat.R$styleable: int MenuItem_android_id
androidx.appcompat.R$styleable: int[] CoordinatorLayout_Layout
androidx.lifecycle.FullLifecycleObserverAdapter
...

تحديد المشاكل وحلّها في ما يتعلّق بتقليص الموارد

عند تصغير الموارد، تعرض نافذة الإنشاء ملخّصًا عن الموارد التي تمت إزالتها من التطبيق. (عليك أولاً النقر على تبديل العرض على يمين النافذة لعرض نص مفصّل من Gradle). مثلاً:

:android:shrinkDebugResources
Removed unused resources: Resource data reduced from 2570KB to 1711KB: Removed 33%
:android:validateDebugSigning

ينشئ Gradle أيضًا ملف بيانات تشخيص باسم resources.txt في <module-name>/build/outputs/mapping/release/ (وهو المجلد نفسه الذي تتوفّر فيه ملفات إخراج ProGuard). يتضمّن هذا الملف تفاصيل مثل موارد الويب التي تشير إلى موارد أخرى والموارد التي يتم استخدامها أو إزالتها.

على سبيل المثال، لمعرفة سبب بقاء @drawable/ic_plus_anim_016 في تطبيقك، افتح ملف resources.txt وابحث عن اسم الملف هذا. قد تلاحظ أنّه تمت الإشارة إليه من مصدر آخر على النحو التالي:

16:25:48.005 [QUIET] [system.out] @drawable/add_schedule_fab_icon_anim : reachable=true
16:25:48.009 [QUIET] [system.out]     @drawable/ic_plus_anim_016

عليك الآن معرفة سبب إمكانية الوصول إلى @drawable/add_schedule_fab_icon_anim ، وإذا بحثت للأعلى، ستجد أنّ هذا المورد مُدرَج ضمن "الموارد القابلة للوصول إلى الجذر هي:". وهذا يعني أنّ هناك رمزًا يشير إلى add_schedule_fab_icon_anim (أي أنّه تم العثور على معرّف R.drawable في الرمز البرمجي الذي يمكن الوصول إليه).

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

10:32:50.590 [QUIET] [system.out] Marking drawable:ic_plus_anim_016:2130837506
    used because it format-string matches string pool constant ic_plus_anim_%1$d.

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