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

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

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

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

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

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

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

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

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

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

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

כך אפשר לסווג סוגים שונים של מכשירים ולהגדיר איך הזיכרון צריך לשמש אותם.

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

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

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

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

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

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

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

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

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

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

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

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

כלי לניתוח פרופיל הזיכרון

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

  • Anonymous + Swap: מורכב מ-Java + Native + Stack allocation memory ב-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.
  • שימוש במפות סיביות שמגובות בחומרה כשזה אפשרי.
    • בספריות כמו Glide, מפעילים את התכונה Downsampler.ALLOW_HARDWARE_CONFIG שמושבתת כברירת מחדל. הפעלת ההגדרה הזו מונעת שכפול של מפות סיביות, שאחרת היו נמצאות גם בזיכרון ה-GPU וגם בזיכרון אנונימי.
  • הימנעות מרינדורים ביניים ומרינדורים חוזרים
    • אפשר לזהות אותם באמצעות Android GPU Inspector:
    • בקטע 'טקסטורות' מחפשים תמונות שהן שלבים לקראת הרינדור הסופי, ולא רק של הרכיבים שמרכיבים אותו. בדרך כלל מדובר ב'רינדור ביניים'.
    • באפליקציות Android SDK, אפשר בדרך כלל להסיר את הרינדורים האלה באמצעות דגל הפריסה forceHasOverlappedRendering:false כדי להשבית רינדורים ביניים לפריסה הזו.
    • מומלץ לעיין במאמר הימנעות מחפיפה בעיבודים.
  • כדאי להימנע מטעינת תמונות placeholder כשזה אפשרי, ולהשתמש ב-@android:color/ או ב-@color לטקסטורות placeholder.
  • מומלץ להימנע מצירוף של כמה תמונות במכשיר אם אפשר לבצע את הצירוף במצב אופליין. עדיפות לטעינת תמונות עצמאיות במקום ליצור קומפוזיציה של תמונות מתמונות שהורדו
  • כדי לטפל טוב יותר במפות סיביות, כדאי לפעול לפי המדריך בנושא טיפול במפות סיביות.

זיכרון Anon+Swap

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

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

Bindings

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

התאמות אופייניות ושיטות מומלצות:

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

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

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

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

משימות ושעונים מעוררים

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

  • לא מומלץ להשתמש בממשקי ה-API‏ AlarmManager ב-SDK בגרסה 23 ומעלה, במיוחד ב-API‏ 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 כמו בדוגמה של דפדפן קטלוג בכלי הכתיבה או ב-RecyclerView בערכת הכלים של ממשק המשתמש Leanback, שהוצא משימוש, כדי לעשות שימוש חוזר בתצוגות ולא ליצור מחדש רכיבי רשימה.
    • שמירת רכיבים במטמון באופן מקומי שנקראים מספקי תוכן חיצוניים שלא צפויים להשתנות, והגדרת מרווחי עדכון שמונעים הקצאה של זיכרון חיצוני נוסף.
  • בודקים אם יש דליפות זיכרון אפשריות.
    • שימו לב למקרים אופייניים של דליפת זיכרון, כמו הפניות בתוך שרשורים אנונימיים, הקצאה מחדש של מאגרי וידאו שלא משוחררים לעולם ומצבים דומים אחרים.
    • משתמשים בתמונת מצב של הזיכרון כדי לנפות באגים של דליפות זיכרון.
  • יוצרים פרופילים של Baseline כדי לצמצם את כמות הקומפילציה בזמן אמת שנדרשת כשמפעילים את האפליקציה בהפעלה במצב התחלתי.

הסבר על שחזור זיכרון ישיר

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

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

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

סיכום הכלים