تحسين الصور النقطية

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

استخدام مكتبات تحميل الصور

يمكنك تحسين كفاءة تطبيقك باستخدام مكتبات تحميل الصور، مثل Coil (للمشاريع التي تستخدم Kotlin بشكل أساسي) أو Glide (لمشاريع Java). تساهم هذه المكتبات في تقليل استخدام الذاكرة في تطبيقك من خلال تنفيذ إجراءات، مثل التخزين المؤقت للصور وتقليل دقة الرسومات عند الحاجة وإعادة استخدام عناصر الرسومات.

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

احرص على استخدام حجم الصورة المناسب لاحتياجاتك. يجب تجنُّب تحميل صورة كبيرة وعالية الدقة في حاوية صغيرة (مثل صورة مصغّرة). بدلاً من ذلك، استخدِم تقليل الدقة لتصغير حجم الصورة قبل فك ترميزها في الذاكرة.

تقليل عدد العيّنات من جهة العميل

تتولّى مكتبات تحميل الصور، مثل Coil وGlide، مهمة تقليل الدقة تلقائيًا. يمكنك ضبط استراتيجيات تقليل عدد العيّنات باستخدام ImageLoader (لـ Coil) أو DownsampleStrategy (لـ Glide). إذا كنت تدير الصور النقطية يدويًا، يمكنك استخدام inSampleSize لفك ترميز نسخة أصغر. لإجراء ذلك بأمان، عليك أولاً ضبط inJustDecodeBounds على true لقراءة أبعاد الصورة بدون تخصيص الذاكرة، ثم احتساب حجم العيّنة، وضبط inSampleSize على تلك القيمة، وضبط inJustDecodeBounds على false، ثم فك ترميز الصورة.

تفضيل تغيير الحجم من جهة الخادم

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

يمكنك ضبط المكتبات لإضافة حجم العرض المستهدَف بشكل ديناميكي إلى عنوان URL الخاص بالصورة. على سبيل المثال، تتيح Coil ذلك باستخدام أدوات اعتراض مخصّصة، وتتيح Glide ذلك باستخدام أدوات تحميل نماذج مخصّصة (مثل BaseGlideUrlLoader).

تجنُّب أحجام التصميم غير المقيدة

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

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

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

توفير موارد بديلة لأحجام الشاشات المختلفة

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

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

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

اختيار تنسيق البكسل المناسب

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

في Glide، يمكنك ضبط هذا الإعداد باستخدام DecodeFormat. في Coil، يمكنك استخدام السمة bitmapConfig.

استخدام المتجهات حيثما أمكن

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

إصدار وإعادة استخدام الصور النقطية عند الإمكان

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

إذا كنت تستخدم مكتبة لتحميل الصور، احرص على تحرير خرائط البتات إلى مجموعة المكتبة المُدارة عندما لا تعود بحاجة إليها. يمكن للمكتبة إعادة استخدام العناصر عند الحاجة، كما تحتفظ بمخزن مؤقت للذاكرة متاح للاستخدام في المستقبل.

إذا كنت تدير الرسومات يدويًا، عليك تحرير صور نقطية عند الانتهاء منها من خلال استدعاء Bitmap.recycle وإلغاء مرجع Bitmap فورًا، بدلاً من الاعتماد على عملية جمع البيانات المُهمَلة.

نصائح أخرى

يسرد هذا القسم بعض الطرق الأخرى لتحسين أداء تطبيقك عند التعامل مع الرسومات.

لا تضمِّن صورًا كبيرة في ملف AAB أو APK

من أهم أسباب زيادة حجم تنزيل التطبيق هي الرسومات المضمّنة في حِزمة AAB أو ملف APK. استخدِم أداة محلّل حِزم APK للتأكّد من أنّك لا تضمِّن ملفات صور أكبر من الحجم المطلوب. يمكنك تقليل أحجام الصور أو وضعها على خادم وتنزيلها عند الحاجة فقط.

العثور على الصور النقطية المكرّرة

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

عند استخدام ImageBitmap، اتّصِل بـ prepareToDraw قبل الرسم

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

يُفضَّل تمرير Int DrawableRes أو عنوان URL كمعلَمات إلى الدالة المركّبة بدلاً من Painter

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

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

// Prefer this:
@Composable
fun MyImage(url: String) {

}
// Over this:
@Composable
fun MyImage(painter: Painter) {

}