تخطَّ إلى المحتوى
ARAB CODEمجتمع المطورين العرب

أداء تطبيقات الجوال واختبارها: قياس قبل التحسين وأتمتة قبل النشر

درسنُشر ٢٤ أكتوبر ٢٠٢٦ · 0 رد · 1 مشاهدة#أداء#اختبارات#android#flutter
قبل ساعتين

مقدمة

المستخدم يلاحظ البطء قبل أي شيء آخر: إقلاع يتجاوز ثانيتين، أو تمرير متقطع، أو تطبيق يستهلك البطارية. والحل ليس «تحسيناً» عشوائياً؛ بل قياس يحدد المشكلة، ثم إصلاح، ثم قياس مرة أخرى، مع اختبارات تمنع عودة الأخطاء.

أهداف التعلم

  • معرفة المؤشرات المهمة: زمن الإقلاع، الإطارات المتقطعة (Jank)، الذاكرة، حجم التطبيق.
  • استخدام أدوات القياس في Android Studio وFlutter DevTools.
  • التعرف على أشهر أسباب البطء وعلاجها.
  • بناء هرم اختبارات: وحدات، تكامل، واجهة.

1. ماذا نقيس؟

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

قِس دائماً على إصدار release وجهاز حقيقي متوسط، لا على المحاكي في وضع debug.

2. الأدوات

  • Android Studio Profiler: المعالج، الذاكرة، الشبكة، الطاقة.
  • Macrobenchmark لقياس الإقلاع والتمرير آلياً، وBaseline Profiles لتسريع الإقلاع بتجهيز الكود الأكثر استخداماً مسبقاً.
  • Flutter DevTools: عرض الأداء (Performance)، وطبقات إعادة الرسم، وتتبع الذاكرة. شغّل بـ flutter run --profile.

3. أشهر أسباب البطء

عمل ثقيل على الخيط الرئيسي:

// خطأ: قراءة قاعدة بيانات أو ملف على الخيط الرئيسي
val topics = dao.allTopicsBlocking()

// صحيح
viewModelScope.launch {
    val topics = withContext(Dispatchers.IO) { dao.allTopics() }
}

في Flutter، المهام الحسابية الثقيلة (تحليل JSON ضخم مثلاً) تُنقل إلى Isolate:

final topics = await Isolate.run(() => parseTopics(hugeJsonString));

قوائم غير كسولة: استخدم LazyColumn في Compose وListView.builder في Flutter بدل بناء كل العناصر دفعة واحدة، وأعط كل عنصر مفتاحاً ثابتاً.

صور كبيرة: صورة 4000×3000 لعرضها في 120×120 تستهلك عشرات الميغابايت. اطلب من الخادم حجماً مناسباً، أو استخدم مكتبة تحميل صور تعيد التحجيم (Coil في Android، وcacheWidth في Flutter).

إعادة رسم زائدة: في Flutter استخدم const للعناصر الثابتة، وقسّم الشاشة إلى عناصر صغيرة حتى لا يُعاد رسم كل شيء عند تغيير جزء صغير.

4. هرم الاختبارات

         ▲   اختبارات واجهة شاملة (قليلة، بطيئة)
        ▲▲▲  اختبارات تكامل / عناصر (متوسطة)
      ▲▲▲▲▲▲ اختبارات وحدات (كثيرة، سريعة)

مثال اختبار وحدة في Flutter:

test('Topic.fromJson يعطي صفر ردود عند غياب الحقل', () {
  final t = Topic.fromJson({'id': '1', 'title': 'سؤال'});
  expect(t.replyCount, 0);
});

ومثال اختبار عنصر:

testWidgets('يعرض رسالة الخطأ وزر إعادة المحاولة', (tester) async {
  await tester.pumpWidget(const MaterialApp(home: ErrorView(message: 'تعذر التحميل')));
  expect(find.text('تعذر التحميل'), findsOneWidget);
  expect(find.text('إعادة المحاولة'), findsOneWidget);
});

شغّل الاختبارات تلقائياً في CI مع كل طلب دمج، حتى لا يصل خطأ مكتشف سابقاً إلى المستخدمين.

تمرين عملي

  1. شغّل تطبيقك في وضع profile وسجّل زمن الإقلاع وعدد الإطارات المتقطعة أثناء تمرير قائمة طويلة.
  2. أصلح سبباً واحداً (عمل على الخيط الرئيسي أو صورة كبيرة) وأعد القياس. دوّن الأرقام قبل وبعد.
  3. أضف ثلاثة اختبارات وحدات وواحداً للعناصر، وشغلها في GitHub Actions.

أخطاء شائعة

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

الخلاصة

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

مصادر مقترحة

  • Android Developers: «App performance guide» و«Baseline Profiles».
  • توثيق Flutter: «Performance best practices» و«Testing Flutter apps».
  • أداة Firebase Performance Monitoring لقياس الأداء لدى المستخدمين الحقيقيين.

الرد على الموضوع

سجّل الدخول للمشاركة في النقاش.

نقاشات مشابهة