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

يمكن أن يؤدي التعامل مع الصور إلى حدوث مشاكل في الأداء بسرعة إذا لم تتوخَّ الحذر. حتى الرسومات الصغيرة بتنسيق مضغوط، مثل 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) أو حدِّد نسبة العرض إلى الارتفاع. يتيح ذلك لمحرك التنسيق حساب هدف البكسل الدقيق مسبقًا، والذي يمكن أن تستخدمه أداة تحميل الصور بعد ذلك لطلب الأصل الذي تم تغيير حجمه بشكلٍ صحيح وفك ترميزه.

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

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

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

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

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

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

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

استخدام الرسومات المتجهة كلما أمكن ذلك

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

إلغاء تخصيص الصور النقطية وإعادة استخدامها كلما أمكن ذلك

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

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

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

نصائح أخرى

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

عدم تضمين صور كبيرة في ملف AAB أو APK

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

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

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

عند استخدام 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) {

}