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

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

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

يمكنك الموازنة بين الذاكرة والجودة من خلال اختيار تنسيق البكسل المناسب. استخدِم 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) {

}