אופטימיזציה של תמונות מפת סיביות

אם לא תפעלו בזהירות, עבודה עם תמונות עלולה לגרום לבעיות בביצועים. אפילו גרפיקה קטנה בפורמט דחוס כמו JPG או PNG יכולה להפוך למפת סיביות גדולה כשהיא מפוענחת לצורך הצגה.אם לא תשתמשו בגרפיקה בצורה יעילה, עלולות להיווצר בעיות בזיכרון שעלולות לפגוע בביצועים של האפליקציה ושל אפליקציות אחרות במכשיר. כדי להבטיח שהאפליקציה תפעל בצורה אופטימלית, מומלץ לפעול לפי השיטות המומלצות הבאות.

שימוש בספריות לטעינת תמונות

כדי לשפר את היעילות של האפליקציה, אפשר להשתמש בספריות לטעינת תמונות כמו Coil (לפרויקטים שמתמקדים ב-Kotlin) או Glide (לפרויקטים ב-Java). הספריות האלה מצמצמות את השימוש בזיכרון של האפליקציה באמצעות פעולות כמו שמירת תמונות במטמון, שינוי גודל של גרפיקה כשצריך ושימוש חוזר באובייקטים גרפיים.

שינוי הגודל של תמונות כשצריך

חשוב להשתמש בגודל התמונה המתאים לצרכים שלכם. לדוגמה, אסור לטעון תמונה גדולה לתמונה ממוזערת קטנה. במקום זאת, צריך להשתמש בשיטה כמו inSampleSize כדי לטעון גרסה שעברה דגימה מחדש של התמונה.

ספריות לטעינת תמונות כמו Coil ו-Glide מטפלות בדגימה מחדש כברירת מחדל באופן אוטומטי. אפשר להגדיר את אסטרטגיות הדגימה שלהם באמצעות ImageLoader (ל-Coil) או DownsampleStrategy (ל-Glide).

אספקת משאבים חלופיים לגדלים שונים של מסכים

אם אתם שולחים תמונות עם האפליקציה, כדאי לספק נכסים בגדלים שונים עבור רזולוציות שונות של מכשירים. השימוש בשיטה הזו יכול לעזור להקטין את גודל ההורדה של האפליקציה במכשירים ולשפר את הביצועים, כי תמונה ברזולוציה נמוכה תיטען במכשיר עם רזולוציה נמוכה. למידע נוסף על הוספת מפות סיביות חלופיות לגדלים שונים של מכשירים, אפשר לעיין במאמר בנושא מפות סיביות חלופיות.

לא להחיל מרווח פנימי ישירות

לפעמים צריך להוסיף שוליים לתמונה. לדוגמה, יכול להיות שתרצו להוסיף לתמונה גבול שקוף כדי ליצור אפקט של Letterbox. במקרים כאלה, לא מומלץ להוסיף את הריווח ישירות לתמונה, כי זה ישנה את המידות שלה. במקום זאת, משאירים את המימדים של התמונה כמו שהם ומשנים את המיקום שלה במסך באמצעות InsetDrawable. אפשר גם להוסיף ריווח פנימי לרכיב Composable או לרכיב View שמכיל את התמונה.

בחירת פורמט הפיקסלים הנכון

כדי לאזן בין הזיכרון לאיכות, צריך לבחור את פורמט הפיקסלים הנכון. משתמשים ב-RGB_565 כשלא צריך שקיפות. הפורמט הזה משתמש בחצי מהזיכרון של הפורמט ARGB_8888 שמוגדר כברירת מחדל.

ב-Glide אפשר להגדיר את זה באמצעות DecodeFormat. ב-Coil, אפשר להשתמש בנכס bitmapConfig.

שימוש בווקטורים כשאפשר

אם התמונות מורכבות מצורות גיאומטריות, פרטי גרפי וקטורי יהיו קטנים בהרבה ממפת סיביות, וניתן לשנות את הגודל שלהם בצורה חלקה בכל צפיפות תצוגה. כשמתאים, אפשר להשתמש באלמנטים כמו ShapeDrawable כדי לייצג גרפיקה.

כדאי לשחרר מפות סיביות ולעשות בהן שימוש חוזר כשניתן

קבצים גרפיים גדולים יכולים לתפוס הרבה זיכרון. כדי לצמצם את ההשפעה שלהם, מומלץ לשחרר או לעשות שימוש חוזר באובייקטים הגרפיים בכל הזדמנות.

אם אתם משתמשים בספרייה לטעינת תמונות, הקפידו לשחרר מפות סיביות למאגר המנוהל של הספרייה כשאתם כבר לא צריכים אותן. הספרייה יכולה לעשות שימוש חוזר באובייקטים כשצריך, והיא שומרת מאגר זיכרון זמין לצרכים עתידיים.

אם אתם מנהלים את הגרפיקה באופן ידני, אתם צריכים לשחרר מפות סיביות כשאתם מסיימים להשתמש בהן על ידי קריאה ל-Bitmap.recycle וביטול ההפניה Bitmap באופן מיידי, במקום להסתמך על garbage collection.

טיפים וטריקים נוספים

בקטע הזה מפורטות כמה דרכים נוספות לשיפור הביצועים של האפליקציה כשמטפלים בגרפיקה.

לא מומלץ לארוז תמונות גדולות עם קובץ ה-AAB או ה-APK

אחת הסיבות העיקריות לגודל גדול של קובץ ההורדה של האפליקציה היא גרפיקה שנארזת בתוך קובץ ה-AAB או ה-APK. כדאי להשתמש בכלי APK analyzer כדי לוודא שאתם לא אורזים קובצי תמונות גדולים מהנדרש. אפשר להקטין את הגודל שלהם או לשקול להציב את התמונות בשרת ולהוריד אותן רק כשצריך.

איתור מפות סיביות מיותרות

אם יש לכם כמה עותקים של אותה תמונה, הזיכרון מתבזבז. אתם יכולים להשתמש בכלי לניתוח ביצועים (profiler) ב-Android Studio כדי לזהות גרפיקה מיותרת. כדי לתעד תמונת מצב של הזיכרון, משתמשים בכלי לניתוח תמונת מצב של הזיכרון ומסננים את התוצאות על ידי בחירה בהגדרה duplicate bitmaps (עותקים של מפות סיביות).

כשמשתמשים ב-ImageBitmap, מתקשרים אל prepareToDraw לפני שמציירים

כשמשתמשים ב-ImageBitmap, כדי להתחיל את תהליך ההעלאה של הטקסטורה ל-GPU, צריך לקרוא ל-ImageBitmap#prepareToDraw() לפני שמציירים אותה בפועל. כך המעבד הגרפי יכול להכין את המרקם ולשפר את הביצועים של הצגת רכיב ויזואלי על המסך. רוב הספריות לטעינת תמונות כבר מבצעות את האופטימיזציה הזו, אבל אם אתם עובדים עם המחלקה ImageBitmap בעצמכם, כדאי לזכור את זה.

מומלץ להעביר Int DrawableRes או כתובת URL כפרמטרים לרכיב הניתן להרכבה במקום Painter

בגלל המורכבות של העבודה עם תמונות (לדוגמה, כתיבת פונקציית שוויון עבור Bitmaps תהיה יקרה מבחינת חישובים), ה-API של Painter לא מסומן במפורש כ-API יציב באמצעות ההערה @Stable. שיעורים לא יציבים עלולים להוביל להרכבות מחדש מיותרות כי הקומפיילר לא יכול להסיק בקלות אם הנתונים השתנו.

לכן, מומלץ להעביר כתובת URL או מזהה של משאב drawable כפרמטרים לפונקציה הניתנת להרכבה, במקום להעביר Painter כפרמטר.

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

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

}