تحسين أداء التطبيق باستخدام الملفات الشخصية الأساسية

1. قبل البدء

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

المتطلبات

الإجراءات التي ستنفذّها

  • إعداد المشروع لاستخدام أدوات إنشاء "ملفات تعريف الأداء الأساسي"
  • إنشاء "ملفات شخصية للمرجع" لتحسين أداء بدء تشغيل التطبيق والتمرير
  • تحقَّق من تحسُّن الأداء باستخدام مكتبة Jetpack Macrobenchmark.

أهداف الدورة التعليمية

  • ملفات Baseline Profiles وكيفية مساهمتها في تحسين أداء التطبيق
  • كيفية إنشاء ملفات تعريف المرجع
  • تحسينات الأداء في "ملفات تعريف المرجع"

2. الإعداد

للبدء، استنسِخ مستودع Github من سطر الأوامر باستخدام الأمر التالي:

$ git clone https://github.com/android/codelab-android-performance.git

يمكنك بدلاً من ذلك تنزيل ملفَي zip:

فتح المشروع في "استوديو Android"

  1. في نافذة "مرحبًا بك في استوديو Android"، انقر على 61d0a4432ef6d396.png فتح مشروع حالي.
  2. اختَر المجلد [Download Location]/codelab-android-performance/baseline-profiles. تأكَّد من اختيار الدليل baseline-profiles.
  3. عندما يستورد "استوديو Android" المشروع، تأكَّد من إمكانية تشغيل الوحدة app لإنشاء التطبيق النموذجي الذي ستعمل عليه لاحقًا.

نموذج التطبيق

في هذا الدرس التطبيقي حول الترميز، ستعمل على تطبيق نموذج JetSnack. وهو تطبيق افتراضي لطلب الوجبات الخفيفة يستخدم Jetpack Compose.

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

23633b02ac7ce1bc.png

3- ما هي "ملفات تعريف المرجع"؟

تعمل ملفات Baseline Profiles على تحسين سرعة تنفيذ الرمز بنسبة% 30 تقريبًا من عملية التشغيل الأولى من خلال تجنُّب خطوات التفسير والتجميع أثناء التنفيذ (JIT) لمسارات الرموز البرمجية المضمّنة. من خلال تضمين "الملف الشخصي للمرجع" في تطبيق أو مكتبة، يمكن لوقت تشغيل Android (ART) تحسين مسارات الرموز البرمجية المضمّنة من خلال عملية الترجمة المسبقة (AOT)، ما يؤدي إلى تحسين الأداء لكل مستخدم جديد وفي كل تحديث للتطبيق. تتيح هذه الميزة للتطبيقات تحسين عملية بدء التشغيل وتقليل مشاكل إيقاف مؤقت لعرض واجهة المستخدم في التفاعل وتحسين أداء وقت التشغيل بشكل عام للمستخدمين النهائيين منذ عملية الإطلاق الأولى.

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

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

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

4. إعداد وحدة إنشاء "الملف الشخصي للمرجع"

يمكنك إنشاء "ملفات تعريف أساسية" باستخدام فئة اختبار لقياس حالة التطبيق التي تتطلّب إضافة وحدة Gradle جديدة إلى مشروعك. أسهل طريقة لإضافته إلى مشروعك هي استخدام معالج الوحدات في "استوديو Android" المتوفّر في الإصدار Android Studio Hedgehog أو إصدار أحدث.

افتح نافذة معالج الوحدة النمطية الجديدة من خلال النقر بزر الماوس الأيمن على مشروعك أو وحدتك النمطية في لوحة المشروع واختَر جديد > وحدة نمطية.

232b04efef485e9c.png

من النافذة التي تم فتحها، اختَر Baseline Profile Generator من لوحة "النماذج".

b191fe07969e8c26.png

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

التطبيق المستهدَف هو وحدة التطبيق المستخدَمة لإنشاء "ملفات تعريف الأداء الأساسي". إذا كان لديك أكثر من وحدة تطبيق واحدة في مشروعك، اختَر الوحدة التي تريد تشغيل أدوات الإنشاء لها.

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

بعد تحديد جميع تفاصيل الوحدة الجديدة، انقر على إنهاء للمتابعة في إنشاء الوحدة.

التغييرات التي يجريها معالج الوحدات

يُجري معالج الوحدات النمطية عدة تغييرات على مشروعك.

تتم إضافة وحدة Gradle باسم baselineprofile أو الاسم الذي تختاره في المعالج.

تستخدم هذه الوحدة الإضافية com.android.test، التي تخبر Gradle بعدم تضمينها في تطبيقك، لذا لا يمكن أن تحتوي إلا على رمز الاختبار أو مقاييس الأداء. ويطبّق أيضًا إضافة androidx.baselineprofile التي تتيح إمكانية إنشاء ملفات Baseline تلقائيًا.

يُجري المعالج أيضًا تغييرات على وحدة التطبيق المستهدَف التي تحدّدها. على وجه التحديد، يتم تطبيق المكوّن الإضافي androidx.baselineprofile، وإضافة التبعية androidx.profileinstaller، وإضافة التبعية baselineProfile إلى الوحدة التي تم إنشاؤها حديثًا build.gradle(.kts):

plugins {
  id("androidx.baselineprofile")
}

dependencies {
  // ...
  implementation("androidx.profileinstaller:profileinstaller:1.3.0")
  "baselineProfile"(project(mapOf("path" to ":baselineprofile")))
}

تتيح لك إضافة التبعية androidx.profileinstaller تنفيذ ما يلي:

  • التحقّق محليًا من تحسُّن الأداء في "ملفات Baseline" التي تم إنشاؤها
  • استخدِم "ملفات تعريف خط الأساس" على الإصدار 7 من نظام التشغيل Android (المستوى 24 لواجهة برمجة التطبيقات) والإصدار 8 من نظام التشغيل Android (المستوى 26 لواجهة برمجة التطبيقات)، اللذين لا يتيحان استخدام "ملفات التعريف على السحابة الإلكترونية".
  • استخدام "ملفات تعريف Baseline" على الأجهزة التي لا تتضمّن "خدمات Google Play"

تتيح الاعتمادية baselineProfile(project(":baselineprofile")) لـ Gradle معرفة الوحدة التي يجب أن تأخذ منها "ملفات Baseline" التي تم إنشاؤها.

بعد إعداد المشروع، اكتب فئة منشئ "ملفات Baseline".

5- كتابة أداة إنشاء ملفات Baseline Profile

عادةً، يمكنك إنشاء "ملفات تعريف خط الأساس" لتجارب المستخدمين النموذجية في تطبيقك.

ينشئ معالج الوحدة فئة اختبار أساسية BaselineProfileGenerator يمكنها إنشاء الملف الشخصي للمرجع لبدء تشغيل تطبيقك، ويكون شكلها كما يلي:

@RunWith(AndroidJUnit4::class)
@LargeTest
class BaselineProfileGenerator {

    @get:Rule
    val rule = BaselineProfileRule()

    @Test
    fun generate() {
        rule.collect("com.example.baselineprofiles_codelab") {
            // This block defines the app's critical user journey. This is where you
            // optimize for app startup. You can also navigate and scroll
            // through your most important UI.

            // Start default activity for your app.
            pressHome()
            startActivityAndWait()

            // TODO Write more interactions to optimize advanced journeys of your app.
            // For example:
            // 1. Wait until the content is asynchronously loaded.
            // 2. Scroll the feed content.
            // 3. Navigate to detail screen.

            // Check UiAutomator documentation for more information about how to interact with the app.
            // https://d.android.com/training/testing/other-components/ui-automator
        }
    }
}

يستخدم هذا الصف قاعدة اختبار BaselineProfileRule ويتضمّن طريقة اختبار واحدة لإنشاء الملف الشخصي. نقطة الدخول لإنشاء الملف الشخصي هي الدالة collect(). يتطلّب ذلك مَعلمتَين فقط:

  • packageName: حزمة تطبيقك
  • profileBlock: هي مَعلمة lambda الأخيرة.

في دالة lambda profileBlock، يمكنك تحديد التفاعلات التي تغطي تجارب المستخدم النموذجية في تطبيقك. تنفِّذ المكتبة دالة lambda profileBlock عدة مرات، وتجمع الفئات والدوال التي تم استدعاؤها، وتنشئ "الملف الشخصي للمرجع" على الجهاز باستخدام الرمز البرمجي المطلوب تحسينه.

يتضمّن فئة أداة الإنشاء التي تم إنشاؤها تلقائيًا تفاعلات لبدء Activity التلقائي، وتنتظر إلى أن يتم عرض الإطار الأول من تطبيقك باستخدام طريقة startActivityAndWait().

توسيع نطاق أداة إنشاء الرحلات باستخدام رحلات مخصّصة

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

في نموذج تطبيقنا، يمكنك تحديد هذه الرحلات من خلال اتّباع الخطوات التالية:

  1. ابدأ التطبيق. يتم تناول هذا الموضوع جزئيًا في الفئة التي تم إنشاؤها.
  2. انتظِر إلى أن يتم تحميل المحتوى بشكل غير متزامن.
  3. تصفَّح قائمة الوجبات الخفيفة.
  4. الانتقال إلى تفاصيل الوجبة الخفيفة

غيِّر أداة الإنشاء لتتضمّن الوظائف الموضّحة التي تغطي الرحلات النموذجية في المقتطف التالي:

// ...
rule.collect("com.example.baselineprofiles_codelab") {
    // This block defines the app's critical user journey. This is where you
    // optimize for app startup. You can also navigate and scroll
    // through your most important UI.

    // Start default activity for your app.
    pressHome()
    startActivityAndWait()

    // TODO Write more interactions to optimize advanced journeys of your app.
    // For example:
    // 1. Wait until the content is asynchronously loaded.
    waitForAsyncContent()
    // 2. Scroll the feed content.
    scrollSnackListJourney()
    // 3. Navigate to detail screen.
    goToSnackDetailJourney()

    // Check UiAutomator documentation for more information about how to interact with the app.
    // https://d.android.com/training/testing/other-components/ui-automator
}
// ...

الآن، اكتب تفاعلات لكل رحلة ذُكرت. يمكنك كتابتها كدالة إضافية لـ MacrobenchmarkScope، ما يتيح لك الوصول إلى المَعلمات والدوال التي توفّرها. تتيح لك كتابة التفاعلات بهذه الطريقة إعادة استخدامها مع مقاييس الأداء للتحقّق من التحسينات في الأداء.

انتظار المحتوى غير المتزامن

تتضمّن العديد من التطبيقات نوعًا من التحميل غير المتزامن عند بدء تشغيل التطبيق، ويُعرف أيضًا باسم حالة العرض الكامل، والتي تُعلم النظام عند تحميل المحتوى وعرضه، وعندما يمكن للمستخدم التفاعل معه. انتظِر الحالة في المولد (waitForAsyncContent) مع التفاعلات التالية:

  1. ابحث عن قائمة الوجبات الخفيفة في الخلاصة.
  2. انتظِر إلى أن تظهر بعض العناصر في القائمة على الشاشة.
fun MacrobenchmarkScope.waitForAsyncContent() {
   device.wait(Until.hasObject(By.res("snack_list")), 5_000)
   val contentList = device.findObject(By.res("snack_list"))
   // Wait until a snack collection item within the list is rendered.
   contentList.wait(Until.hasObject(By.res("snack_collection")), 5_000)
}

مسار القائمة القابلة للتمرير

بالنسبة إلى رحلة قائمة الوجبات الخفيفة القابلة للتمرير (scrollSnackListJourney)، يمكنك اتّباع التفاعلات التالية:

  1. ابحث عن عنصر في واجهة المستخدم الخاص بقائمة الوجبات الخفيفة.
  2. اضبط هوامش الإيماءات على ألا تؤدي إلى بدء التنقّل في النظام.
  3. مرِّر القائمة وانتظِر إلى أن تستقر واجهة المستخدم.
fun MacrobenchmarkScope.scrollSnackListJourney() {
   val snackList = device.findObject(By.res("snack_list"))
   // Set gesture margin to avoid triggering gesture navigation.
   snackList.setGestureMargin(device.displayWidth / 5)
   snackList.fling(Direction.DOWN)
   device.waitForIdle()
}

الانتقال إلى رحلة التفاصيل

تتضمّن الرحلة الأخيرة (goToSnackDetailJourney) التفاعلات التالية:

  1. ابحث عن قائمة الوجبات الخفيفة وجميع عناصر الوجبات الخفيفة التي يمكنك استخدامها.
  2. اختَر عنصرًا من القائمة.
  3. انقر على العنصر وانتظِر إلى أن يتم تحميل شاشة التفاصيل. يمكنك الاستفادة من حقيقة أنّ قائمة الوجبات الخفيفة لن تظهر على الشاشة بعد الآن.
fun MacrobenchmarkScope.goToSnackDetailJourney() {
    val snackList = device.findObject(By.res("snack_list"))
    val snacks = snackList.findObjects(By.res("snack_item"))
    // Select snack from the list based on running iteration.
    val index = (iteration ?: 0) % snacks.size
    snacks[index].click()
    // Wait until the screen is gone = the detail is shown.
    device.wait(Until.gone(By.res("snack_list")), 5_000)
}

بعد تحديد جميع التفاعلات اللازمة لتجهيز أداة إنشاء "ملف Baseline Profile"، عليك تحديد الجهاز الذي سيتم تشغيلها عليه.

6. تحضير جهاز لتشغيل المولد عليه

لإنشاء "ملفات تعريف خط الأساس"، ننصحك إما باستخدام محاكي، مثل "جهاز مُدار من Gradle"، أو جهاز يعمل بالإصدار 13 من نظام التشغيل Android (المستوى 33 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث.

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

لتحديد جهاز مُدار من Gradle، أضِف تعريف الجهاز إلى ملف :baselineprofile الوحدة build.gradle.kts كما هو موضّح في المقتطف التالي:

android {
  // ...

  testOptions.managedDevices.devices {
    create<ManagedVirtualDevice>("pixel6Api31") {
        device = "Pixel 6"
        apiLevel = 31
        systemImageSource = "aosp"
    }
  } 
}

في هذه الحالة، نستخدم الإصدار 11 من نظام التشغيل Android (المستوى 31 لواجهة برمجة التطبيقات) وتكون صورة نظام aosp قادرة على الوصول إلى الجذر.

بعد ذلك، اضبط المكوّن الإضافي لنظام Gradle الخاص بـ "الملف الشخصي للمرجع" لاستخدام جهاز Gradle المُدار الذي تم تحديده. لإجراء ذلك، أضِف اسم الجهاز إلى السمة managedDevices وأوقِف السمة useConnectedDevices كما هو موضّح في المقتطف التالي:

android {
  // ...
}

baselineProfile {
   managedDevices += "pixel6Api31"
   useConnectedDevices = false
}

dependencies {
  // ...
}

بعد ذلك، أنشئ الملف الشخصي للمرجع.

7. إنشاء ملف Baseline Profile

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

أنشأ معالج الوحدة النمطية الجديد إعداد تشغيل لتتمكّن من تنفيذ مهمة Gradle بسرعة مع جميع المَعلمات اللازمة للتنفيذ بدون الحاجة إلى التبديل بين نافذة المحطة الطرفية و"استوديو Android".

لتشغيله، ابحث عن Generate Baseline Profile إعداد التشغيل وانقر على الزر "تشغيل" 599be5a3531f863b.png.

6911ecf1307a213f.png

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

بعد أن تنتهي أداة الإنشاء بنجاح، يضع مكوّن Gradle الإضافي تلقائيًا الرمز البرمجي baseline-prof.txt الذي تم إنشاؤه في تطبيقك المستهدف (الوحدة :app) في المجلد src/release/generated/baselineProfile/.

fa0f52de5d2ce5e8.png

(اختياري) تشغيل أداة الإنشاء من سطر الأوامر

بدلاً من ذلك، يمكنك تشغيل أداة الإنشاء من سطر الأوامر. يمكنك الاستفادة من المهمة التي أنشأها الجهاز المُدار من Gradle، وهي :app:generateBaselineProfile. ينفِّذ هذا الأمر جميع الاختبارات في المشروع المحدّد من خلال التبعية baselineProfile(project(:baselineProfile)). وبما أنّ الوحدة تتضمّن أيضًا مقاييس أداء للتحقّق لاحقًا من تحسُّن الأداء، فإنّ هذه الاختبارات تفشل مع ظهور تحذير من تشغيل مقاييس الأداء على محاكي.

android
   .testInstrumentationRunnerArguments
   .androidx.benchmark.enabledRules=BaselineProfile

لإجراء ذلك، يمكنك فلترة جميع أدوات إنشاء "الملفات الشخصية للمرجع" باستخدام وسيط مشغّل قياس حالة التطبيق التالي، وسيتم تخطّي جميع مقاييس الأداء:

يبدو الأمر الكامل على النحو التالي:

./gradlew :app:generateBaselineProfile -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=BaselineProfile

توزيع تطبيقك باستخدام "ملفات تعريف خط الأساس"

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

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

توضّح الخطوة التالية كيفية التحقّق من مدى تحسُّن أداء التطبيق باستخدام "ملفات تعريف خط الأساس".

8. (اختياري) تخصيص إنشاء ملفات Baseline Profile

يتضمّن Baseline Profiles Gradle Plugin خيارات لتخصيص طريقة إنشاء الملفات الشخصية لتلبية احتياجاتك المحدّدة. يمكنك تغيير السلوك باستخدام كتلة الإعداد baselineProfile { } في نصوص البرامج الإنشائية.

تؤثّر كتلة الإعدادات ضِمن الوحدة :baselineprofile في كيفية تشغيل أدوات الإنشاء مع إمكانية إضافة managedDevices وتحديد ما إذا كان سيتم استخدام useConnectedDevices أو الأجهزة المُدارة من Gradle.

يحدّد قسم الإعدادات في الوحدة النمطية المستهدَفة :app مكان حفظ الملفات الشخصية أو طريقة إنشائها. يمكنك تغيير المَعلمات التالية:

  • automaticGenerationDuringBuild: في حال تفعيل هذه الميزة، يمكنك إنشاء "الملف الشخصي للمرجع" عند إنشاء بنية الإصدار. ويكون ذلك مفيدًا عند إنشاء تطبيقك باستخدام CI قبل إصداره.
  • saveInSrc: تحدِّد ما إذا كان سيتم تخزين ملفات Baseline Profiles التي تم إنشاؤها في المجلد src/. يمكنك بدلاً من ذلك الوصول إلى الملف من مجلد :baselineprofile.
  • baselineProfileOutputDir: تحدّد هذه السمة مكان تخزين ملفات Baseline Profile التي تم إنشاؤها.
  • mergeIntoMain: يتم تلقائيًا إنشاء "الملف الشخصي للمرجع" لكل تنويعة تصميم (صيغة المنتج ونوع التصميم). إذا أردت دمج جميع الملفات الشخصية في src/main، يمكنك إجراء ذلك من خلال تفعيل هذا الخيار.
  • filter: يمكنك فلترة الفئات أو الطرق التي تريد تضمينها أو استبعادها من "ملفات Baseline" التي تم إنشاؤها. يمكن أن يكون ذلك مفيدًا لمطوّري المكتبات الذين يريدون تضمين الرمز البرمجي من المكتبة فقط.

9- التحقّق من تحسينات أداء بدء التشغيل

بعد إنشاء "الملف الشخصي للمرجع" وإضافته إلى تطبيقك، تحقَّق من تأثيره المطلوب في أداء تطبيقك.

ينشئ معالج الوحدة النمطية الجديدة فئة قياس أداء باسم StartupBenchmarks. يحتوي هذا المقياس على مقياس أداء لقياس وقت بدء تشغيل التطبيق ومقارنته بالوقت الذي يستخدم فيه التطبيق "ملفات Baseline Profiles".

يظهر الصف على النحو التالي:

@RunWith(AndroidJUnit4::class)
@LargeTest
class StartupBenchmarks {

    @get:Rule
    val rule = MacrobenchmarkRule()

    @Test
    fun startupCompilationNone() =
        benchmark(CompilationMode.None())

    @Test
    fun startupCompilationBaselineProfiles() =
        benchmark(CompilationMode.Partial(BaselineProfileMode.Require))

    private fun benchmark(compilationMode: CompilationMode) {
        rule.measureRepeated(
            packageName = "com.example.baselineprofiles_codelab",
            metrics = listOf(StartupTimingMetric()),
            compilationMode = compilationMode,
            startupMode = StartupMode.COLD,
            iterations = 10,
            setupBlock = {
                pressHome()
            },
            measureBlock = {
                startActivityAndWait()

                // TODO Add interactions to wait for when your app is fully drawn.
                // The app is fully drawn when Activity.reportFullyDrawn is called.
                // For Jetpack Compose, you can use ReportDrawn, ReportDrawnWhen and ReportDrawnAfter
                // from the AndroidX Activity library.

                // Check the UiAutomator documentation for more information on how to
                // interact with the app.
                // https://d.android.com/training/testing/other-components/ui-automator
            }
        )
    }
}

يستخدم هذا التطبيق MacrobenchmarkRule يمكنه تشغيل مقاييس الأداء لتطبيقك وجمع مقاييس الأداء. نقطة الدخول لكتابة مقياس أداء هي الدالة measureRepeated من القاعدة.

يتطلّب ذلك عدة مَعلمات:

  • packageName: التطبيق الذي تريد قياس أدائه.
  • metrics: نوع المعلومات التي تريد قياسها أثناء قياس الأداء
  • iterations: عدد مرات تكرار مقياس الأداء
  • startupMode: كيف تريد أن يبدأ تطبيقك عند بدء قياس الأداء
  • setupBlock: التفاعلات التي يجب أن تحدث مع تطبيقك قبل القياس
  • measureBlock: التفاعلات مع تطبيقك التي تريد قياسها أثناء قياس الأداء.

يحتوي فئة الاختبار أيضًا على اختبارَين: startupCompilationeNone() وstartupCompilationBaselineProfiles()، اللذان يستدعيان الدالة benchmark() مع compilationMode مختلفة.

CompilationMode

تحدّد المَعلمة CompilationMode طريقة تجميع التطبيق مسبقًا في لغة الآلة. يتضمّن الخيارات التالية:

  • DEFAULT: يتم تجميع التطبيق مسبقًا بشكل جزئي باستخدام "ملفات تعريف خط الأساس" إذا كانت متاحة. يتم استخدام هذه السمة في حال عدم تطبيق أي مَعلمة compilationMode.
  • None(): تعيد ضبط حالة تجميع التطبيق ولا يتم تجميع التطبيق مسبقًا. تظل ميزة التجميع أثناء التنفيذ (JIT) مفعَّلة أثناء تنفيذ التطبيق.
  • Partial(): يتم تجميع التطبيق مسبقًا باستخدام "ملفات تعريف Baseline" أو عمليات الإعداد المسبق، أو كليهما.
  • Full(): يتم تجميع الرمز البرمجي للتطبيق بالكامل مسبقًا. هذا هو الخيار الوحيد المتاح على الإصدار 6 من نظام التشغيل Android (المستوى 23 من واجهة برمجة التطبيقات) والإصدارات الأقدم.

إذا أردت البدء في تحسين أداء تطبيقك، يمكنك اختيار وضع تجميع DEFAULT، لأنّ الأداء يكون مشابهًا للأداء عند تثبيت التطبيق من Google Play. إذا أردت مقارنة مزايا الأداء التي توفّرها "ملفات Baseline"، يمكنك إجراء ذلك من خلال مقارنة نتائج وضع التجميع None وPartial.

تعديل مقياس الأداء المعياري لانتظار المحتوى

تتم كتابة مقاييس الأداء بشكل مشابه لمولدات "ملفات Baseline Profile" من خلال كتابة التفاعلات مع تطبيقك. وبشكلٍ تلقائي، تنتظر مقاييس الأداء التي تم إنشاؤها عرض الإطار الأول فقط، تمامًا كما كان يحدث مع BaselineProfileGenerator، لذا ننصحك بتحسينها لانتظار المحتوى غير المتزامن.

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

// ...
measureBlock = {
   startActivityAndWait()

   // The app is fully drawn when Activity.reportFullyDrawn is called.
   // For Jetpack Compose, you can use ReportDrawn, ReportDrawnWhen and ReportDrawnAfter
   // from the AndroidX Activity library.
   waitForAsyncContent() // <------- Added to wait for async content.

   // Check the UiAutomator documentation for more information on how to
   // interact with the app.
   // https://d.android.com/training/testing/other-components/ui-automator
}

تشغيل مقاييس الأداء

يمكنك تنفيذ مقاييس الأداء بالطريقة نفسها التي تنفّذ بها الاختبارات التي تتطلّب أجهزة. يمكنك تشغيل وظيفة الاختبار أو الصف بأكمله باستخدام رمز المزراب بجانبه.

587b04d1a76d1e9d.png

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

94e0da86b6f399d5.png

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

بعد اكتمال مقاييس الأداء، يمكنك الاطّلاع على التوقيتات في الناتج في "استوديو Android" كما هو موضّح في لقطة الشاشة التالية:

282f90d5f6ff5196.png

من لقطة الشاشة، يمكنك ملاحظة أنّ وقت بدء تشغيل التطبيق يختلف لكل CompilationMode. يتم عرض قيم الوسيط في الجدول التالي:

timeToInitialDisplay [ms]

timeToFullDisplay [ms]

بدون

202.2

818.8

BaselineProfiles

193.7

637.9

التحسين

%4

28%

الفرق بين أوضاع التجميع في timeToFullDisplay هو 180 ملي ثانية، وهو تحسّن بنسبة% 28 تقريبًا من خلال استخدام "الملف الشخصي الأساسي" فقط. يكون أداء CompilationNone أسوأ لأنّ الجهاز عليه إجراء معظم عمليات التجميع أثناء التنفيذ (JIT) أثناء بدء تشغيل التطبيق. ويكون أداء CompilationBaselineProfiles أفضل لأنّ التجميع الجزئي باستخدام "ملفات Baseline Profile" يجمع مسبقًا الرمز الذي من المرجّح أن يستخدمه المستخدم ويترك الرمز غير المهم بدون تجميع مسبق حتى لا يتم تحميله على الفور.

10. (اختياري) التحقّق من تحسُّن أداء التمرير

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

@LargeTest
@RunWith(AndroidJUnit4::class)
class ScrollBenchmarks {

   @get:Rule
   val rule = MacrobenchmarkRule()

   @Test
   fun scrollCompilationNone() = scroll(CompilationMode.None())

   @Test
   fun scrollCompilationBaselineProfiles() = scroll(CompilationMode.Partial())

   private fun scroll(compilationMode: CompilationMode) {
       // TODO implement
   }
}

من داخل طريقة scroll، استخدِم الدالة measureRepeated مع المَعلمات المطلوبة. بالنسبة إلى المَعلمة metrics، استخدِم FrameTimingMetric، الذي يقيس المدة التي يستغرقها إنتاج لقطات واجهة المستخدم:

private fun scroll(compilationMode: CompilationMode) {
   rule.measureRepeated(
       packageName = "com.example.baselineprofiles_codelab",
       metrics = listOf(FrameTimingMetric()),
       compilationMode = compilationMode,
       startupMode = StartupMode.WARM,
       iterations = 10,
       setupBlock = {
           // TODO implement
       },
       measureBlock = {
           // TODO implement
       }
   )
}

في هذه المرة، عليك تقسيم التفاعلات بشكل أكبر بين setupBlock وmeasureBlock لقياس مدة عرض اللقطات أثناء التنسيق الأول وتصفّح المحتوى فقط. لذلك، ضَع الدوال التي تبدأ الشاشة التلقائية في setupBlock ودوال الإضافة التي تم إنشاؤها مسبقًا waitForAsyncContent() وscrollSnackListJourney() في measureBlock:

private fun scroll(compilationMode: CompilationMode) {
   rule.measureRepeated(
       packageName = "com.example.baselineprofiles_codelab",
       metrics = listOf(FrameTimingMetric()),
       compilationMode = compilationMode,
       startupMode = StartupMode.WARM,
       iterations = 10,
       setupBlock = {
           pressHome()
           startActivityAndWait()
       },
       measureBlock = {
           waitForAsyncContent()
           scrollSnackListJourney()
       }
   )
}

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

84aa99247226fc3a.png

تعرض السمة FrameTimingMetric مدة اللقطات بالمللي ثانية (frameDurationCpuMs) في النسبة المئوية الـ 50 والـ 90 والـ 95 والـ 99. في نظام التشغيل Android 12 (مستوى واجهة برمجة التطبيقات 31) والإصدارات الأحدث، تعرض أيضًا مقدار الوقت الذي تجاوزت فيه اللقطات الحدّ الأقصى (frameOverrunMs). ويمكن أن تكون القيمة سالبة، ما يعني أنّه كان هناك وقت إضافي متبقٍ لإنتاج اللقطة.

من النتائج، يمكنك ملاحظة أنّ CompilationBaselineProfiles لديه مدة عرض إطار أقصر بمقدار 2 ملي ثانية في المتوسط، وهو ما قد لا يلاحظه المستخدمون. ومع ذلك، تكون النتائج أكثر وضوحًا بالنسبة إلى النسب المئوية الأخرى. بالنسبة إلى مقياس P99، يبلغ الفرق 43.5 ملي ثانية، وهو ما يزيد عن 3 لقطات تم تخطّيها على جهاز يعمل بمعدّل 90 لقطة في الثانية. على سبيل المثال، بالنسبة إلى Pixel 6، يكون الحد الأقصى لوقت عرض اللقطة هو 1000 ملي ثانية / 90 لقطة في الثانية = ~11 ملي ثانية.

11. تهانينا

تهانينا، لقد أكملت بنجاح هذا الدرس التطبيقي حول الترميز وحسّنت أداء تطبيقك باستخدام "ملفات تعريف خط الأساس".

مراجع إضافية

يمكنك الاطّلاع على المراجع الإضافية التالية:

المستندات المرجعية