השתמש בזיכרון בצורה אופטימלית

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

צריכת זיכרון גבוהה עלולה להוביל לבעיות בהתנהגות של האפליקציות והמערכת, כולל:

  • האפליקציה עצמה עשויה להיות איטית או עם השהיה, או במקרה הגרוע ביותר, להיסגר.
  • שירותי מערכת שגלויים למשתמשים (שליטה בעוצמת הקול, לוח הבקרה של הגדרות התמונה, Voice Assistant וכו') מגיבים באיטיות רבה או לא פועלים בכלל.
  • רכיבי המערכת יכולים להיפסק, ואז הם מופעלים מחדש, מה שגורם לעליות חדות בלחימה על משאבים ומשפיע ישירות על האפליקציה שבחזית.
  • המעבר למרכז האפליקציות עשוי להתעכב משמעותית, והאפליקציה שבחזית עשויה להיראות לא מגיבה עד שהמעבר יסתיים.
  • המערכת עשויה להיכנס למצב של direct reclaim, שבו היא משהה באופן זמני את ביצוע השרשור בזמן ההמתנה להקצאת זיכרון. זה יכול לקרות לכל חוט, כמו החוט הראשי או חוטים שקשורים לקודק, ויכול לגרום לבעיות כמו ירידה במסגרות של אודיו וווידאו ובעיות בממשק המשתמש.

שיקולים לגבי זיכרון במכשירי טלוויזיה

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

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

הסבר על מכשירי טלוויזיה

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

במכשירי טלוויזיה, כדאי להביא בחשבון את המאפיינים הבאים:

  • זיכרון המכשיר: נפח הזיכרון (RAM) שמותקן במכשיר.
  • רזולוציית ממשק המשתמש של המכשיר: הרזולוציה שבה המכשיר משתמש כדי ליצור את מערכת ההפעלה ואת ממשק המשתמש של האפליקציות. בדרך כלל היא נמוכה יותר מרזולוציית הווידאו של המכשיר.
  • רזולוציית הווידאו: הרזולוציה המקסימלית שבה המכשיר יכול להפעיל סרטונים.

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

סיכום של מכשירי טלוויזיה

זיכרון המכשיר רזולוציית הווידאו של המכשיר רזולוציית ממשק המשתמש של המכשיר isLowRAMDevice()
‎1 GB 1080p 720p כן
1.5 GB 2160p 1080p כן
‎≥1.5GB 1080p 720p או 1080p לא*
‎≥2GB 2160p 1080p לא*

מכשירי טלוויזיה עם זיכרון RAM נמוך

המכשירים האלה נמצאים במצב של מחסור בזיכרון, והם ידווחו על ActivityManager.isLowRAMDevice() כ-True. אפליקציות שפועלות במכשירי טלוויזיה עם נפח זיכרון RAM נמוך צריכות ליישם אמצעי בקרה נוספים על הזיכרון.

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

  • מכשירים עם 1GB: 1GB של RAM, רזולוציית ממשק משתמש של 720p/HD‏ (1280x720), רזולוציית וידאו של 1080p/FullHD‏ (1920x1080)
  • מכשירים עם 1.5GB: 1.5GB של RAM, רזולוציית ממשק משתמש של 1080p/FullHD‏ (1920x1080), רזולוציית וידאו של 2160p/UltraHD/4K‏ (3840x2160)
  • מצבים אחרים שבהם יצרן הציוד המקורי הגדיר את הדגל ActivityManager.isLowRAMDevice() עקב מגבלות זיכרון נוספות.

מכשירי טלוויזיה רגילים

במכשירים האלה אין לחץ זיכרון משמעותי כזה. אנחנו מתייחסים למכשירים האלה בתור מכשירי טלוויזיה עם המאפיינים הבאים:

  • זיכרון RAM בנפח 1.5GB ומעלה, ממשק משתמש ברזולוציה של 720p או 1080p ורזולוציית וידאו של 1080p
  • זיכרון RAM בנפח 2GB ומעלה, ממשק משתמש ברזולוציה של 1080p ורזולוציית וידאו של 1080p או 2160p

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

יעדי זיכרון במכשירי טלוויזיה עם נפח זיכרון RAM נמוך

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

כלי לניתוח ביצועי הזיכרון

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

  • אנונימי + החלפה (swap): מורכב מ-Java + זיכרון הקצאה מקומי + זיכרון סטאק ב-Android Studio.
  • גרפיקה: הדיווח מתבצע ישירות בכלי לניתוחי ביצועים. בדרך כלל מורכב מרקמות גרפיות.
  • קובץ: הדיווח מתבצע בקטגוריות 'קוד' ו'אחר' ב-Android Studio.

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

סוג הזיכרון מטרה יעדי שימוש (1GB)
אנונימי + החלפה (Java + Native + Stack) משמש להקצאות, למאגרי מדיה, למשתנים ולמשימות אחרות שצורכות הרבה זיכרון. ‎< 160 MB
גרפיקה משמש את ה-GPU לצורך טקסטורות ומאגרים קשורים לתצוגה 30-40 MB
קובץ משמש לדפי קוד ולקבצים בזיכרון. 60-80 MB

הזיכרון הכולל המקסימלי (Anon+Swap + Graphics + File) לא יכול לחרוג מהערכים הבאים:

  • 280MB של שימוש כולל בזיכרון (Anon+Swap + Graphics + File) במכשירים עם זיכרון RAM בנפח 1GB.

מומלץ מאוד לא לחרוג מהערכים הבאים:

  • שימוש בזיכרון בנפח 200MB ב-(Anon+Swap + Graphics).

זיכרון של קובץ

כהנחיה כללית לגבי זיכרון שמבוסס על קבצים, חשוב לזכור:

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

עם זאת, כשעובדים עם זיכרון של קובץ באופן כללי:

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

המלצות ספציפיות לתוכניות טלוויזיה

בקטע הזה מפורטות המלצות ספציפיות לאופטימיזציה של השימוש בזיכרון במכשירי טלוויזיה.

זיכרון ה-GPU

שימוש בפורמטים וברזולוציות מתאימים של תמונות.

  • לא טוענים תמונות ברזולוציה גבוהה יותר מהרזולוציה של ממשק המשתמש של המכשיר. לדוגמה, תמונות באיכות 1080p צריכות להיות מוקטנות ל-720p במכשיר עם ממשק משתמש באיכות 720p.
  • שימוש בתמונות בפורמט bitmap שמגובות בחומרה כשהדבר אפשרי.
    • בספריות כמו Glide, מפעילים את התכונה Downsampler.ALLOW_HARDWARE_CONFIG, שהיא מושבתת כברירת מחדל. הפעלת האפשרות הזו מונעת כפילויות של קובצי bitmap, שבמקרה אחר היו נמצאים גם בזיכרון הגרפיקה וגם בזיכרון האנונימי.
  • הימנעו מעיבוד ביניים ומעיבוד מחדש
    • אפשר לזהות אותם באמצעות Android GPU Inspector:
    • בקטע 'טקסטורות', מחפשים תמונות שמהוות שלבים בתהליך היצירה של הגרסה הסופית, ולא רק את הרכיבים שמרכיבים אותן. בדרך כלל מדובר בגרסה ביניים.
    • באפליקציות ל-Android SDK, לרוב אפשר להסיר אותן באמצעות דגל הפריסה forceHasOverlappedRendering:false כדי להשבית את העיבודים הביניים של הפריסה הזו.
    • מקור מידע מצוין בנושא הוא המאמר הימנעות מעיבוד שכבות חופפות.
  • כדאי להימנע משימוש בתמונות placeholder כשהדבר אפשרי, ולהשתמש ב-@android:color/ או ב-@color לצורך טקסטורות placeholder.
  • הימנעו משימוש בשילוב של כמה תמונות במכשיר אם אפשר לבצע את השילוב אופליין. עדיף לטעון תמונות נפרדות במקום ליצור קומפוזיציה של תמונות שהורדתם
  • מומלץ לעיין במדריך טיפול בתמונות בפורמט bitmap כדי להבין טוב יותר איך עובדים עם תמונות בפורמט bitmap.

Anon+Swap memory

הערך Anon+Swap מורכב מהקצאות של Native + Java + Stack בפרופיל הזיכרון של Android Studio. אפשר להשתמש ב-ActivityManager.isLowMemoryDevice() כדי לבדוק אם המכשיר מוגבל בזיכרון, ולהתאים את עצמכם למצב הזה בהתאם להנחיות האלה.

  • מדיה:
    • מציינים גודל משתנה למאגרי מדיה בהתאם ל-RAM של המכשיר ולרזולוציית ההפעלה של הסרטון. הזמן הזה צריך לכלול דקה אחת של הפעלת הסרטון:
      1. 40-60MB ל-1GB‏ / 1080p
      2. 60-80MB ל-1.5GB / 1080p
      3. 80-100MB עבור 1.5GB / 2160p
      4. 100-120MB עבור 2GB / 2160p
    • פינוי הקצאות של זיכרון מדיה כשמשנים פרק כדי למנוע עלייה בסך הזיכרון של Anonymous.
    • משחררים ומפסיקים את משאבי המדיה באופן מיידי כשהאפליקציה מופסקת: משתמשים בקריאות חזרה של מחזור החיים של הפעילות כדי לטפל במשאבי האודיו והווידאו. אם האפליקציה שלכם היא לא אפליקציית אודיו, עצרו את ההפעלה כשהאירוע onStop() מתרחש בפעילויות שלכם, שמרו את כל העבודה שאתם מבצעים והגדירו את המשאבים כך שיתפנו. כדי לתזמן עבודה שעשויה להידרש לכם מאוחר יותר. עיינו בקטע משימות והתראות.
    • חשוב לשים לב לזיכרון של המאגר כשמחפשים סרטון: לרוב, מפתחים מקצים 15 עד 60 שניות נוספות של תוכן עתידי כדי שהסרטון יהיה מוכן למשתמש, אבל הפעולה הזו יוצרת עומס נוסף על הזיכרון. באופן כללי, לא כדאי לאגור יותר מ-5 שניות של נתונים לטעינה מראש עד שהמשתמש בחר את המיקום החדש בסרטון. אם אתם צריכים לאחסן מראש זמן נוסף במטמון בזמן דילוג, חשוב לוודא:
      • הקצאת מאגר הביניים לחיפוש מראש ושימוש חוזר בו.
      • גודל המאגר הזמני לא יכול להיות גדול מ-15-25MB (בהתאם לזיכרון של המכשיר).
  • הקצאות:
    • מומלץ להשתמש בהנחיות לגבי זיכרון גרפי כדי לוודא שלא יוצרים תמונות כפולות בזיכרון אנונימי
      • תמונות הן בדרך כלל הגורם הגדול ביותר לשימוש בזיכרון, ולכן כפילויות שלהן יכולות להפעיל לחץ רב על המכשיר. זה נכון במיוחד במהלך ניווט נרחב בתצוגות של תמונות בחלונית.
    • משחררים הקצאות על ידי ביטול ההפניות שלהן כשעוברים בין מסכים: חשוב לוודא שאין הפניות לתמונות בפורמט bitmap ולאובייקטים שנשארו מאחור.
  • ספריות:
    • הקצאות זיכרון בפרופיל מספריות כשמוסיפים חדשות, כי הן עשויות לטעון גם ספריות נוספות, שעשויות גם הן לבצע הקצאות וליצור קישורים.
  • Networking:
    • אל תבצעו קריאות חסימה לרשת במהלך הפעלת האפליקציה. קריאות כאלה מאטות את זמן ההפעלה של האפליקציה ויוצרות עומס זיכרון נוסף בזמן ההפעלה, שבו הזיכרון מוגבל במיוחד על ידי עומס האפליקציה. כדאי להציג קודם מסך טעינה או מסך פתיחה, ולשלוח בקשות לרשת אחרי שהממשק המשתמש מוכן.

קישורים

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

קישורים אופייניים ושיטות מומלצות:

  • Play Integrity API: משמש לבדיקת תקינות המכשיר
    • בדיקה של תקינות המכשיר אחרי מסך הטעינה ולפני הפעלת המדיה
    • מפרסמים את ההפניות ל-PlayIntegrity‏ StandardIntegrityManager לפני שמפעילים את התוכן.
  • Play Billing Library: משמשת לניהול מינויים ורכישות באמצעות Google Play‏
    • מפעילים את הספרייה אחרי מסך הטעינה ומטפלים בכל עבודות החיוב לפני שמפעילים מדיה.
    • משתמשים ב-BillingClient.endConnection() כשמסיימים להשתמש בספרייה ותמיד לפני שמפעילים סרטון או מדיה.
    • משתמשים ב-BillingClient.isReady() וב-BillingClient.getConnectionState() כדי לבדוק אם השירות התנתק, למקרה שצריך לבצע שוב פעולות חיוב. לאחר מכן, מריצים שוב את הפקודה BillingClient.endConnection().
  • GMS FontsProvider
    • מומלץ להשתמש בגופנים עצמאיים במכשירים עם נפח זיכרון RAM נמוך, במקום להשתמש בספק גופנים. הורדת הגופנים היא פעולה יקרה, ו-FontsProvider יקשור שירותים כדי לבצע אותה.
  • ספריית Google Assistant: לפעמים היא משמשת לחיפוש ולחיפוש בתוך האפליקציה. אם אפשר, כדאי להחליף את הספרייה הזו.
    • באפליקציות מסוג leanback: משתמשים ב-Gboard להמרת טקסט לדיבור או בספרייה androidx.leanback.
    • באפליקציות לכתיבת הודעות:
      • משתמשים בתכונה 'טקסט לדיבור' ב-Gboard כדי להטמיע חיפוש קולי.
    • מטמיעים את התכונה הסרטון הבא כדי לאפשר למשתמשים לגלות את תוכן המדיה באפליקציה.

שירותים שפועלים בחזית

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

ב-Android TV וב-Google TV, מותר לשירותים שפועלים בחזית להמשיך לפעול רק אחרי שהמשתמש יוצא מהאפליקציה:

משימות והתראות

WorkManager הוא ממשק ה-API המתקדם ביותר ל-Android לתזמון משימות חוזרות ברקע. ‏WorkManager ישתמש ב-JobScheduler החדש כשהוא יהיה זמין (SDK 23 ואילך) וב-AlarmManager הישן כשהוא לא יהיה זמין. כדי לבצע משימות מתוזמנות בטלוויזיה, מומלץ לפעול לפי ההמלצות הבאות:

  • אין להשתמש בממשקי ה-API של AlarmManager בגרסאות SDK 23 ואילך, במיוחד ב-AlarmManager.set(), ב-AlarmManager.setExact() ובשיטות דומות, כי הם לא מאפשרים למערכת לקבוע את הזמן המתאים להרצת המשימות (לדוגמה, כשהמכשיר לא פעיל).
  • במכשירים עם נפח זיכרון RAM נמוך, מומלץ להימנע מהרצת משימות אלא אם יש צורך חיוני בכך. במקרה הצורך, אפשר להשתמש ב-WorkManager WorkRequest רק לעדכון ההמלצות אחרי ההפעלה, ולנסות לעשות זאת בזמן שהאפליקציה עדיין פתוחה.
  • מגדירים את WorkManager Constraints כדי לאפשר למערכת להריץ את המשימות בזמן המתאים:

Kotlin

Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresStorageNotLow(true)
.setRequiresDeviceIdle(true)
.build()

Java

Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresStorageNotLow(true)
.setRequiresDeviceIdle(true)
.build()
  • אם אתם צריכים להריץ משימות באופן קבוע (לדוגמה, כדי לעדכן את הסרטון הבא לצפייה על סמך פעילות הצפייה של משתמש בתוכן באפליקציה במכשיר אחר), כדאי לצמצם את השימוש בזיכרון ולשמור על צריכת הזיכרון של המשימה מתחת ל-30MB.

הנחיות כלליות נוספות

ההנחיות הבאות מספקות מידע כללי על פיתוח אפליקציות ל-Android:

  • צמצום ההקצאות של אובייקטים, אופטימיזציה של שימוש חוזר באובייקטים וביטול ההקצאה של אובייקטים שלא בשימוש באופן מיידי.
    • לא לשמור הפניות לאובייקטים, במיוחד לא לביטים.
    • מומלץ להימנע משימוש ב-System.gc() ובקריאות ישירות לשחרור זיכרון, כי הן מפריעות לתהליך הטיפול בזיכרון של המערכת: לדוגמה, במכשירים שמשתמשים ב-zRAM, קריאה מאולצת ל-gc() עלולה להגדיל באופן זמני את השימוש בזיכרון בגלל דחיסת הזיכרון והפעלת דחיסה חוזרת שלו.
    • כדי לעשות שימוש חוזר בתצוגות ולא ליצור מחדש רכיבי רשימה, אפשר להשתמש ב-LazyList, כמו שמוצג בדפדפן קטלוגים ב-Compose או ב-RecyclerView בכלי ה-UI של Leanback (שעבר עכשיו ללא שימוש).
    • שומרים במטמון מקומי רכיבים שנקראו מספקי תוכן חיצוניים, ושסביר להניח שלא ישתנו, ומגדירים מרווחי זמן לעדכון כדי למנוע הקצאת זיכרון חיצוני נוסף.
  • בודקים אם יש דליפות זיכרון אפשריות.
    • חשוב לשים לב למקרים אופייניים של דליפת זיכרון, כמו הפניות בתוך שרשורים אנונימיים, הקצאה מחדש של מאגרי וידאו שלא משתחררים אף פעם ומצבים דומים אחרים.
    • שימוש בdump של ערימה כדי לנפות באגים של דליפות זיכרון.
  • יוצרים פרופילים בסיסיים כדי לצמצם את כמות הידור ה-JIT שנדרשת כשמריצים את האפליקציה בהפעלה ראשונית.

סיכום הכלים

  • אפשר להשתמש בכלי Android Studio memory profiler כדי לבדוק את צריכת הזיכרון במהלך השימוש.
    • משתמשים ב-heapdump כדי לבדוק הקצאות ספציפיות של אובייקטים ותמונות מפה בינארית.
    • משתמשים בכלי לניתוחי זיכרון כדי לבדוק הקצאות שאינן של Java או Kotlin.
  • משתמשים ב-Android GPU Inspector כדי לבדוק את הקצאות הגרפיקה.