1. قبل البدء
يعرض هذا الدرس التطبيقي حول الترميز كيفية إنشاء "الملفات الشخصية المرجعية" لتحسين أداء تطبيقك وكيفية التحقّق من مزايا الأداء لاستخدام "الملفات الشخصية المرجعية".
المتطلبات
- الإصدار Hedgehog من "استوديو Android" (2023.1.1) أو إصدار أحدث
- الإصدار 8.0 من المكوّن الإضافي لنظام Gradle المتوافق مع Android أو إصدار أحدث
- فهم أساسي لميزة "مقاييس الأداء على مستوى النظام" في Jetpack
- جهاز Android فعلي يعمل بالإصدار 7 (المستوى 24 من واجهة برمجة التطبيقات) أو الإصدارات الأحدث
الإجراءات التي ستنفذّها
- إعداد المشروع لاستخدام أدوات إنشاء الملفات الشخصية الأساسية
- أنشئ ملفّات تعريف مرجعية لتحسين أداء بدء تشغيل التطبيق والتمرير.
- تأكّد من تحسينات الأداء باستخدام مكتبة Jetpack Macrobenchmark.
المُعطيات
- الملفات الشخصية الأساسية وكيفية تحسينها لأداء التطبيق
- كيفية إنشاء الملفات الشخصية للمرجع
- المكاسب على صعيد الأداء للملفات الشخصية المرجعية
2. الإعداد
للبدء، استنسِخ مستودع جيت هب من سطر الأوامر باستخدام الأمر التالي:
$ git clone https://github.com/android/codelab-android-performance.git
بدلاً من ذلك، يمكنك تنزيل ملفي zip:
فتح "المشروع" في "استوديو Android"
- في نافذة "مرحبًا بك في استوديو Android"، انقر على فتح مشروع حالي.
- اختَر المجلد
[Download Location]/codelab-android-performance/baseline-profiles
. تأكَّد من اختيار الدليلbaseline-profiles
. - عندما يستورد "استوديو Android" المشروع، تأكَّد من إمكانية تشغيل وحدة
app
لإنشاء نموذج التطبيق الذي تعمل معه لاحقًا.
نموذج التطبيق
يمكنك استخدام تطبيق JetSnack sample في هذا الدرس التطبيقي حول الترميز. وهو تطبيق افتراضي لطلب الوجبات الخفيفة يستخدم Jetpack Compose.
لقياس أداء التطبيق، عليك فهم بنية واجهة المستخدم وسلوك التطبيق، حتى تتمكّن من الوصول إلى عناصر واجهة المستخدم من خلال مقاييس الأداء. افتح التطبيق وتعرَّف على الشاشات الأساسية من خلال طلب وجبات خفيفة. لا تحتاج إلى معرفة تفاصيل كيفية هندسة التطبيق.
3- ما هي الملفات الشخصية الأساسية؟
تعمل ملفات الأداء الأساسية على تحسين سرعة تنفيذ الرموز البرمجية بنسبة% 30 تقريبًا عن التشغيل الأول من خلال تجنُّب تفسير خطوات التجميع أثناء التنفيذ (JIT) لمسارات الرموز البرمجية المضمّنة. من خلال إضافة ملف شخصي أساسي في تطبيق أو مكتبة، يمكن لميزة وقت تشغيل Android (ART) تحسين مسارات الرموز البرمجية المضمَّنة من خلال طريقة تجميع AOT، ما يؤدي إلى تحسين الأداء لكل مستخدم جديد مع كل تحديث للتطبيق. يتيح هذا التحسين المستنِد إلى الملف الشخصي (PGO) للتطبيقات تحسين بدء التشغيل وتقليل الارتباك في التفاعل وتحسين الأداء العام أثناء التشغيل للمستخدمين النهائيين من أول عملية إطلاق.
باستخدام "الملف الشخصي الأساسي"، تصبح جميع تفاعلات المستخدمين أكثر سلاسة منذ المرة الأولى التي يتم فيها تشغيلها، مثل بدء تشغيل التطبيق أو التنقّل بين الشاشات أو الانتقال في المحتوى. تؤدي زيادة سرعة التطبيق وسرعة استجابته إلى زيادة عدد المستخدمين النشطين يوميًا وارتفاع متوسط معدّل معاودة الزيارة.
تساعد "ملفات الأداء الأساسية" في توجيه التحسين إلى ما بعد بدء تشغيل التطبيق من خلال توفير تفاعلات المستخدمين الشائعة التي تحسّن وقت تشغيل التطبيق من أول عملية تشغيل. لا تعتمد تجميعات AOT الإرشادية على أجهزة المستخدمين ويمكن إجراؤها مرة واحدة لكل إصدار على جهاز تطوير بدلاً من جهاز جوّال. من خلال طرح إصدارات تتضمّن ملفًا أساسيًا، تصبح تحسينات التطبيقات متاحة بشكل أسرع بكثير من الاعتماد على ملفات Cloud Profile وحدها.
في حال عدم استخدام ملف تعريف أساسي، يتم تجميع جميع رموز التطبيق أثناء التنفيذ (JIT) في الذاكرة بعد تفسيرها أو في ملف odex في الخلفية عندما يكون الجهاز في وضع السكون. وقد يحصل المستخدمون بعد ذلك على تجربة غير مثالية عند تشغيل تطبيق بعد تثبيته أو تحديثه للمرة الأولى قبل تحسين المسارات الجديدة.
4. إعداد وحدة إنشاء الملف الشخصي الأساسي
يمكنك إنشاء ملفات تعريف مرجعية باستخدام فئة اختبار قياس حالة التطبيق تتطلّب إضافة وحدة Gradle جديدة إلى مشروعك. إنّ أسهل طريقة لإضافتها إلى مشروعك هي باستخدام معالج وحدة "استوديو Android" الذي يأتي مع الإصدار Hedgehog من "استوديو Android" أو الإصدارات الأحدث.
افتح نافذة معالج الوحدة الجديدة من خلال النقر بزر الماوس الأيمن على مشروعك أو وحدتك في لوحة المشروع واختيار جديد > وحدة.
من النافذة التي تم فتحها، اختَر أداة إنشاء الملف الشخصي الأساسي من لوحة "النماذج".
بالإضافة إلى المعلَمات المعتادة، مثل اسم الوحدة أو اسم الحزمة أو اللغة أو لغة ضبط الإصدار، هناك إدخالان غير معتادَين للوحدة الجديدة، وهما التطبيق المستهدَف واستخدام الجهاز المُدار من خلال نظام Gradle.
التطبيق المستهدَف هو وحدة التطبيق المستخدَمة لإنشاء الملفات الشخصية الأساسية. إذا كان لديك أكثر من وحدة تطبيق واحدة في مشروعك، اختَر الوحدة التي تريد تشغيل أدوات إنشاء النماذج لها.
يحدِّد مربّع الاختيار استخدام الجهاز المُدار من Gradle الوحدة لتشغيل أدوات إنشاء الملفات الشخصية الأساسية على أدوات محاكاة Android المُدارة تلقائيًا. يمكنك الاطّلاع على المزيد من المعلومات عن الأجهزة المُدارة من خلال Gradle في مقالة توسيع نطاق اختباراتك باستخدام الأجهزة المُدارة من Gradle. في حال إزالة العلامة من المربّع، تستخدم المولدات أي جهاز متصل.
بعد تحديد جميع التفاصيل عن الوحدة الجديدة، انقر على إنهاء لمتابعة إنشاء الوحدة.
التغييرات التي أجراها معالج الوحدات
يجري معالج الوحدة العديد من التغييرات على مشروعك.
وستضيف وحدة Gradle باسم baselineprofile
أو الاسم الذي تختاره في المعالج.
تستخدم هذه الوحدة المكوّن الإضافي com.android.test
الذي يخبر نظام Gradle بعدم تضمينه في تطبيقك، وبالتالي يمكن أن تحتوي فقط على رمز اختبار أو مقاييس أداء. ويتم أيضًا استخدام المكوّن الإضافي androidx.baselineprofile
، الذي يسمح تلقائيًا بإنشاء الملفات الشخصية الأساسية.
يُجري المعالج أيضًا تغييرات على وحدة التطبيق المستهدَف التي تختارها. على وجه التحديد، يطبّق المكوّن الإضافي 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
تنفيذ ما يلي:
- التحقّق محليًا من مكاسب الأداء للملفات الشخصية الأساسية التي تم إنشاؤها.
- استخدام الملفات الأساسية على Android 7 (المستوى 24 لواجهة برمجة التطبيقات) وAndroid 8 (المستوى 26 لواجهة برمجة التطبيقات)، اللذان لا يتيحان استخدام الملفات في السحابة الإلكترونية
- استخدام الملفات الشخصية الأساسية على الأجهزة التي لا تتضمّن "خدمات Google Play"
تسمح التبعية baselineProfile(project(":baselineprofile"))
لـ Gradle بمعرفة الوحدة التي يجب أن يأخذ منها الملفات الشخصية الأساسية التي تم إنشاؤها.
الآن بعد أن تم إعداد المشروع، اكتب فئة لإنشاء الملفات الشخصية الأساسية.
5- كتابة أداة إنشاء الملف الشخصي للمرجع
وعادةً ما يتم إنشاء الملفات الشخصية الأساسية لتجارب المستخدِمين النموذجية في تطبيقك.
ينشئ معالج الوحدات فئة اختبار 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 الأخيرة
في دالة profileBlock
lambda، يمكنك تحديد التفاعلات التي تغطي تجارب المستخدمين المعتادة في تطبيقك. وتعمل المكتبة على تشغيل دالة profileBlock
عدة مرات، وجمع الفئات والدوالّ التي تمّ استدعاؤها، وإنشاء الملف الشخصي الأساسي على الجهاز باستخدام الرمز الذي سيتم تحسينه.
تحتوي فئة المُنشئ التي تم إنشاؤها تلقائيًا على تفاعلات لبدء Activity
التلقائي وتنتظر إلى أن يتم عرض الإطار الأول من تطبيقك باستخدام طريقة startActivityAndWait()
.
توسيع نطاق المولّد من خلال رحلات مخصّصة
يمكنك ملاحظة أنّ الفئة التي تم إنشاؤها تتضمّن أيضًا بعض TODO
لكتابة المزيد من التفاعلات لتحسين الرحلات المتقدّمة في تطبيقك. وننصحك بذلك حتى تتمكّن من تحسين الأداء بعد بدء التطبيق.
في نموذج التطبيق، يمكنك تحديد هذه المسارات من خلال إجراء ما يلي:
- ابدأ التطبيق. وقد تمّت تغطية ذلك جزئيًا من خلال الفئة التي تمّ إنشاؤها.
- انتظِر إلى أن يتم تحميل المحتوى بشكل غير متزامن.
- انتقِل إلى قائمة الوجبات الخفيفة.
- الانتقال إلى تفاصيل الوجبة الخفيفة
غيِّر المُنشئ ليحتوي على الوظائف الموضّحة التي تغطي التجارب المعتادة في المقتطف التالي:
// ...
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
) مع التفاعلات التالية:
- ابحث عن قائمة الخلاصات الخفيفة.
- انتظِر إلى أن تظهر بعض العناصر في القائمة على الشاشة.
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
)، يمكنك اتّباع التفاعلات التالية:
- ابحث عن عنصر واجهة مستخدم قائمة الوجبات الخفيفة.
- اضبط هوامش الإيماءات لمنع تنشيط ميزة التنقّل في النظام.
- انتقِل للأسفل في القائمة وانتظِر إلى أن تستقر واجهة المستخدم.
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
) التفاعلات التالية:
- ابحث عن قائمة الوجبات الخفيفة وجميع عناصر الوجبات الخفيفة التي يمكنك استخدامها.
- اختَر عنصرًا من القائمة.
- انقر على العنصر وانتظر حتى يتم تحميل شاشة التفاصيل. يمكنك الاستفادة من حقيقة أنّ قائمة الوجبات الخفيفة لن تظهر على الشاشة بعد الآن.
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)
}
بعد تحديد جميع التفاعلات اللازمة ليكون "أداة إنشاء الملف الشخصي الأساسي" جاهزًا للتشغيل، عليك تحديد الجهاز الذي يتم تشغيله عليه.
6- تحضير جهاز لتشغيل المولّد عليه
لإنشاء الملفات الشخصية الأساسية، ننصحك باستخدام جهاز محاكاة مثل جهاز Gradle المُدار أو جهاز يعمل بالإصدار 13 من نظام التشغيل Android (المستوى 33 لواجهة برمجة التطبيقات) أو إصدار أحدث.
لإعادة تنفيذ العملية وإنشاء الملفات الشخصية الأساسية بشكل آلي، يمكنك استخدام "الأجهزة المُدارة من Gradle". تتيح لك أجهزة Gradle المُدارة إجراء اختبارات على محاكي Android بدون الحاجة إلى تشغيله يدويًا وإيقافه. يمكنك معرفة المزيد من المعلومات عن الأجهزة المُدارة من خلال Gradle في مقالة توسيع نطاق اختباراتك باستخدام الأجهزة المُدارة من Gradle.
لتحديد جهاز مُدار من خلال Gradle، أضِف تعريفه إلى ملف build.gradle.kts
لوحدة :baselineprofile
كما هو موضّح في المقتطف التالي:
android {
// ...
testOptions.managedDevices.devices {
create<ManagedVirtualDevice>("pixel6Api31") {
device = "Pixel 6"
apiLevel = 31
systemImageSource = "aosp"
}
}
}
في هذه الحالة، نستخدم الإصدار 11 من نظام التشغيل Android (المستوى 31 لواجهة برمجة التطبيقات)، ويمكن لصورة نظام aosp
الوصول إلى إذن الوصول إلى الجذر.
بعد ذلك، عليك ضبط المكوّن الإضافي Gradle Plugin للملف الشخصي الأساسي لاستخدام "الجهاز المُدار من Gradle" المحدّد. لإجراء ذلك، أضِف اسم الجهاز إلى السمة managedDevices
وأوقِف useConnectedDevices
كما هو موضّح في المقتطف التالي:
android {
// ...
}
baselineProfile {
managedDevices += "pixel6Api31"
useConnectedDevices = false
}
dependencies {
// ...
}
بعد ذلك، أنشئ الملف الشخصي الأساسي.
7- إنشاء الملف الشخصي للمرجع
بعد أن يصبح الجهاز جاهزًا، يمكنك إنشاء "الملف الشخصي الأساسي". ينشئ المكوّن الإضافي Gradle لملف السجلّ الأساسي مهام Gradle لتشغيل عملية تشغيل فئة اختبار المولد بالكامل وتطبيق ملفات السجلّ الأساسية التي تم إنشاؤها في تطبيقك.
أنشأ معالج الوحدات الجديد إعدادات تشغيل ليتم تشغيل مهمة Gradle بسرعة مع جميع المعلمات اللازمة لتشغيلها بدون الحاجة إلى التبديل بين الوحدة الطرفية و"استوديو Android"
ولتشغيله، حدِّد موقع إعدادات تشغيل "Generate Baseline Profile
" وانقر على الزر "تشغيل" .
تبدأ المهمة صورة المحاكي المحدّدة سابقًا. يمكنك تنفيذ التفاعلات من فئة الاختبار BaselineProfileGenerator
عدة مرات، وبعد ذلك يمكنك إيقاف المحاكي وتقديم الإخراج إلى Android Studio.
بعد انتهاء عملية إنشاء الرمز البرمجي بنجاح، يضع المكوّن الإضافي Gradle تلقائيًا baseline-prof.txt
الذي تم إنشاؤه في تطبيقك المستهدَف (وحدة :app
) في مجلد src/release/generated/baselineProfile/
.
(اختياري) تشغيل أداة إنشاء التقارير من سطر الأوامر
بدلاً من ذلك، يمكنك تشغيل أداة الإنشاء من سطر الأوامر. يمكنك الاستفادة من المهمّة التي أنشأها الجهاز المُدار من 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 Profiles Gradle خيارات لتخصيص كيفية إنشاء الملفات الشخصية لتلبية احتياجاتك المحدّدة. يمكنك تغيير السلوك باستخدام كتلة الإعداد baselineProfile { }
في نصوص البناء.
تؤثر مجموعة الإعدادات ضمن وحدة :baselineprofile
في كيفية تشغيل منشئي النماذج مع إمكانية إضافة managedDevices
وتحديد ما إذا كان سيتم استخدام useConnectedDevices
أو الأجهزة المُدارة من Gradle.
تحدِّد مجموعة الإعدادات ضمن الوحدة المستهدَفة :app
مكان حفظ الملفات الشخصية أو كيفية إنشائها. يمكنك تغيير المَعلمات التالية:
automaticGenerationDuringBuild
: يمكنك إنشاء الملف الشخصي الأساسي عند إنشاء إصدار الإصدار العلني إذا كان مفعّلاً. ويُعدّ ذلك مفيدًا عند إنشاء الإصدارات باستخدام عملية التكامل المستمر قبل شحن تطبيقك.-
saveInSrc
: لتحديد ما إذا كان سيتم تخزين الملفات الشخصية الأساسية التي تم إنشاؤها في المجلدsrc/
. بدلاً من ذلك، يمكنك الوصول إلى الملف من مجلد الإصدار:baselineprofile
. baselineProfileOutputDir
: لتحديد مكان تخزين الملفات الشخصية للمرجع التي تم إنشاؤهاmergeIntoMain
: بشكل تلقائي، يتم إنشاء الملفات الشخصية الأساسية لكل صيغة إصدار (نكهة المنتج ونوع الإصدار). إذا أردت دمج جميع الملفات الشخصية فيsrc/main
، يمكنك إجراء ذلك من خلال تفعيل هذه العلامة.filter
: يمكنك فلترة الفئات أو الطرق التي تريد تضمينها أو استبعادها من الملفات الشخصية الأساسية التي تم إنشاؤها. يمكن أن يكون ذلك مفيدًا لمطوّري المكتبات الذين يريدون تضمين الرمز البرمجي من المكتبة فقط.
9. التحقق من تحسينات أداء بدء التشغيل
بعد إنشاء الملف الشخصي الأساسي وإضافته إلى تطبيقك، تأكَّد من أنّه يحقّق التأثير المطلوب في أداء تطبيقك.
ينشئ معالج الوحدة الجديد فئة قياس أداء تُسمى StartupBenchmarks
. يحتوي على مقياس أداء لقياس وقت بدء تشغيل التطبيق ومقارنته بالوقت الذي يستخدم فيه التطبيق "الملفات الشخصية الأساسية".
يبدو الصف على النحو التالي:
@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()
: يجمع التطبيق مسبقًا مع الملفات الشخصية الأساسية أو عمليات التشغيل التحضيرية أو كليهما.Full()
: تجمع مسبقًا رمز التطبيق بالكامل. هذا هو الخيار الوحيد المتاح على الإصدار 6 من نظام التشغيل Android (المستوى 23 من واجهة برمجة التطبيقات) والإصدارات الأقدم.
إذا كنت تريد بدء تحسين أداء تطبيقك، يمكنك اختيار DEFAULT
وضع الترجمة والربط، لأنّ الأداء مشابه لما يحدث عند تثبيت التطبيق من Google Play. إذا كنت تريد مقارنة مزايا الأداء التي تقدّمها "الملفات الشخصية الأساسية"، يمكنك إجراء ذلك من خلال مقارنة نتائج وضعَي التجميع None
وPartial
.
تعديل المقياس المعياري للانتظار إلى أن يظهر المحتوى
يتم إنشاء مقاييس الأداء بالطريقة نفسها التي يتم بها إنشاء أدوات إنشاء الملفات الشخصية الأساسية، وذلك من خلال كتابة التفاعلات مع تطبيقك. لا تنتظر مقاييس الأداء التي تم إنشاؤها تلقائيًا سوى عرض اللقطة الأولى، تمامًا كما كان يفعل 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
}
تشغيل مقاييس الأداء
يمكنك تنفيذ مقاييس الأداء بالطريقة نفسها التي تُجري بها الاختبارات المدمَجة. يمكنك تشغيل الدالة الاختبارية أو الصف بأكمله مع رمز الفاصل بجانبه.
تأكَّد من اختيار جهاز فعلي، لأنّ تشغيل معايير الأداء على محاكي Android يتعذّر أثناء التشغيل مع تحذير بأنّ معيار الأداء يمكن أن يقدّم نتائج غير صحيحة. على الرغم من أنّه يمكنك تشغيله تقنيًا على محاكي، إلا أنّك تقيس أداء الجهاز المضيف. وإذا كان الجهاز يواجه حملًا ثقيلًا، سيؤدي ذلك إلى تباطؤ أداء معايير الأداء.
بعد تشغيل مقياس الأداء، تتم إعادة إنشاء تطبيقك ثم تشغيل مقاييس الأداء. تبدأ مقاييس الأداء تطبيقك وتوقفه وتعيد تثبيته عدة مرات استنادًا إلى iterations
التي تحدِّدها.
بعد اكتمال مقاييس الأداء، يمكنك الاطّلاع على التوقيتات في نتائج "استوديو Android" كما هو موضّح في لقطة الشاشة التالية:
من لقطة الشاشة، يمكنك ملاحظة أنّ وقت بدء تشغيل التطبيق يختلف لكل CompilationMode
. تظهر قيم المتوسط في الجدول التالي:
timeToInitialDisplay [ms] | timeToFullDisplay [ملّي ثانية] | |
لا ينطبق | 202.2 | 818.8 |
BaselineProfiles | 193.7 | 637.9 |
التحسين | %4 | 28% |
الفرق بين أوضاع التجميع في timeToFullDisplay
هو 180 ملّي ثانية، ما يشكّل تحسُّنًا بنسبة% 28 تقريبًا عند توفّر ملف شخصي أساسي. يحقّق CompilationNone
أداءً أسوأ، لأنّ الجهاز يُجري معظم عمليات التجميع أثناء التنفيذ (JIT) أثناء بدء تشغيل التطبيق. يحقّق CompilationBaselineProfiles
أداءً أفضل لأنّ التجميع الجزئي باستخدام ميزة AOT في "ملفات الأداء الأساسية" يُجمِّع الرمز الذي يُرجَّح أن يستخدمه المستخدم ويترك الرمز غير المهم غير مجمَّع مسبقًا حتى لا يكون عليه تحميله على الفور.
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()
}
)
}
بعد أن يصبح مقياس الأداء جاهزًا، يمكنك تشغيله كما في السابق للحصول على النتائج كما هو موضّح في لقطة الشاشة التالية:
يعرض FrameTimingMetric
مدة اللقطات بالمللي ثانية (frameDurationCpuMs
) في الشريحة المئوية الخامسة والخمسين والتاسعة والخمسين والتاسعة والخمسين والتاسعة والتسعين. في نظام التشغيل Android 12 (المستوى 31 من واجهة برمجة التطبيقات) والإصدارات الأحدث، يعرض هذا المقياس أيضًا الوقت الذي تتجاوز فيه اللقطات الحدّ الأقصى (frameOverrunMs
). ويمكن أن تكون القيمة سالبة، ما يعني أنّه تبقى وقت إضافي لعرض اللقطة.
من النتائج، يمكنك ملاحظة أنّ CompilationBaselineProfiles
تبلغ مدة اللقطة في المتوسط 2 ملي ثانية، وهو ما قد لا يلاحظه المستخدمون. ومع ذلك، تكون النتائج أكثر وضوحًا بالنسبة إلى الشرائح المئوية الأخرى. بالنسبة إلى P99، يكون الفرق 43.5 ملي ثانية، أي أكثر من 3 لقطات تم تخطّيها على جهاز يعمل بمعدّل 90 لقطة في الثانية. على سبيل المثال، بالنسبة إلى هاتف Pixel 6، يكون الحد الأقصى لوقت عرض اللقطة هو 1000 ملي ثانية / 90 لقطة في الثانية = 11 ملي ثانية تقريبًا.
11. تهانينا
مبروك، لقد أكملت بنجاح هذا الدرس التطبيقي ونجحت في تحسين أداء تطبيقك باستخدام "الملفات الشخصية الأساسية".
مراجع إضافية
يمكنك الاطّلاع على المراجع الإضافية التالية:
- فحص أداء التطبيق باستخدام Macrobenchmark: درس تطبيقي حول الترميز يتناول بشكل مفصّل قياس الأداء
- عيّنات الأداء: مستودع يحتوي على Macrobenchmark وعيّنات أداء أخرى.
- التطبيق النموذجي ضمن "نظام التشغيل Android": تطبيق واقعي يستخدم أدوات قياس الأداء والملفات الشخصية الأساسية لتحسين الأداء.