צמצום גודל האפליקציה

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

העלאת אפליקציה באמצעות קובצי Android App Bundle

מעלים את האפליקציה כקובץ Android App Bundle כדי שמירת גודל האפליקציה כשמפרסמים ב-Google Play. Android App Bundle הוא פורמט להעלאה שכולל את כל המשאבים והקוד המהדר של האפליקציה, אבל העברת היצירה והחתימה של ה-APK מתבצעת ב-Google Play.

לאחר מכן, מודל הצגת האפליקציות של Google Play משתמש ב-App Bundle כדי ליצור ולהציג חבילות APK שעברו אופטימיזציה. לתצורת המכשיר של כל משתמש, כדי שיוריד רק את הקוד והמשאבים שדרושים לו להריץ את האפליקציה. אתם לא צריכים לפתח, לחתום ולנהל חבילות APK מרובות כדי לתמוך במכשירים שונים, והמשתמשים מקבלים הורדות קטנות ומשופרות.

Google Play אוכפת הורדה דחוסה הגבלת גודל של 200MB לאפליקציות שפורסמו באמצעות קובצי App Bundle. גדלים גדולים יותר אפשריות באמצעות Play Feature Delivery ו-Play Asset Delivery, אבל הגדלה של גודל האפליקציה תשפיע לרעה על הצלחת ההתקנה ותגדיל את מספר ההסרות, לכן אנחנו ממליצים להחיל שמתוארות בדף הזה כדי להקטין את גודל ההורדה של האפליקציה ככל האפשר.

הסבר על המבנה של קובץ ה-APK

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

חבילת APK מכילה את הספריות הבאות:

  • META-INF/: מכיל את קובצי החתימה CERT.SF ו-CERT.RSA, וגם את קובץ המניפסט MANIFEST.MF.
  • assets/: מכיל את נכסי האפליקציה, שהאפליקציה יכולה לאחזר באמצעות אובייקט AssetManager.
  • res/: מכיל משאבים שלא מקובצים ב-resources.arsc.
  • lib/: מכיל את הקוד המהדר שספציפי לשכבת התוכנה של מעבד. הספרייה הזו מכילה ספריית משנה לכל סוג פלטפורמה, כמו armeabi, armeabi-v7a, arm64-v8a, x86 x86_64 ו-mips.

חבילת APK מכילה גם את הקבצים הבאים. חובה להזין רק AndroidManifest.xml:

  • resources.arsc: מכיל משאבים שעברו הידור. הקובץ הזה מכיל את תוכן ה-XML מכל ההגדרות של התיקייה res/values/. כלי האריזה מחלץ את תוכן ה-XML הזה, מקמפל אותו לפורמט בינארי ומאחסן את התוכן בארכיון. התוכן הזה כולל שפה מחרוזות וסגנונות, וכן נתיבים לתוכן שלא נכלל ישירות resources.arsc, למשל קובצי פריסה ותמונות.
  • classes.dex: מכיל את הכיתות שעברן הידור בפורמט קובץ DEX שידוע למכונה הווירטואלית Dalvik או ART.
  • AndroidManifest.xml: מכיל את קובץ המניפסט המרכזי של Android. בקובץ הזה מפורטים השם, הגרסה, זכויות הגישה וקבצי הספרייה של האפליקציה. הקובץ בפורמט ה-XML הבינארי של Android.

הקטנה של מספר המשאבים והגודל שלהם

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

הסרת משאבים שלא בשימוש

הכלי lint – מנתח קוד סטטי שכלול ב-Android Studio – מזהה משאבים בתיקייה res/ שלא מופיעים בקוד. כשהכלי lint מזהה משאב שייתכן שלא נמצא בשימוש בחשבון שלך המערכת מדפיסה הודעה כמו בדוגמה הבאה:

res/layout/preferences.xml: Warning: The resource R.layout.preferences appears
    to be unused [UnusedResources]

ספריות שאתם מוסיפים לקוד עשויות לכלול משאבים שלא בשימוש. Gradle יכול להסיר משאבים בשמכם באופן אוטומטי אם תפעילו את shrinkResources בקובץ build.gradle.kts של האפליקציה.

android {
    // Other settings.

    buildTypes {
        getByName("release") {
            minifyEnabled = true
            shrinkResources = true
            proguardFiles(getDefaultProguardFile('proguard-android.txt'), "proguard-rules.pro")
        }
    }
}
android {
    // Other settings.

    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

כדי להשתמש ב-shrinkResources, צריך להפעיל כיווץ קוד. במהלך ה-build, מקישים קודם על R8 מסירה קוד שלא בשימוש. אחר כך, הפלאגין של Android Gradle מסיר את המשאבים שלא נמצאים בשימוש.

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

ב-Android Gradle Plugin 7.0 ואילך, אפשר להצהיר על ההגדרות שהאפליקציה תומכת בהן. Gradle מעבירה את המידע הזה למערכת ה-build באמצעות resourceConfigurations טעם חדש והאפשרות defaultConfig. לאחר מכן מערכת ה-build מונעת משאבים תצורות אחרות שאינן נתמכות ב-APK, וכתוצאה מכך הקטנת את גודל ה-APK. לקבלת מידע נוסף על התכונה הזאת: הסרת חלופה שלא בשימוש משאבים.

צמצום השימוש במשאבים מספריות

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

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

פענוח תמונה מונפשת רגילה

ב-Android 12 (רמת API 31), ה-NDK ממשק ה-API של ImageDecoder מורחב לפענוח קוד כל נתוני הפריימים והתזמון מתמונות שנעשה בהן שימוש בפורמט GIF מונפש וקובץ WebP מונפש.

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

למידע נוסף על ה-API של ImageDecoder, אפשר לעיין במסמכי העזרה של API reference ובדוגמה ב-GitHub.

תמיכה בדחיסות ספציפיות בלבד

מערכת Android תומכת בדחיסות מסך שונות, למשל:

  • ldpi
  • mdpi
  • tvdpi
  • hdpi
  • xhdpi
  • xxhdpi
  • xxxhdpi

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

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

אם האפליקציה שלך צריכה רק תמונות מותאמות, תוכל לחסוך עוד יותר מקום אם תשתמש בווריאציה אחת של תמונה באוסף drawable-nodpi/. מומלץ לכלול לפחות xxhdpi של קבוצת התמונות באפליקציה שלך.

למידע נוסף על דחיסות המסך, גדלים ודחיסויות של מסכים.

שימוש באובייקטים שניתנים להזזה

לחלק מהתמונות לא נדרש משאב של תמונה סטטית. תוכנת ה-framework יכולה לשרטט באופן דינמי את התמונה בזמן ריצה. אובייקטים מסוג Drawable – או <shape> ב-XML – יכולים לתפוס כמות קטנה מאוד של מקום ב-APK. בנוסף, XML Drawable אובייקטים מייצרים תמונות מונוכרמטיות שתואמות להנחיות של Material Design.

שימוש חוזר במשאבים

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

ב-Android יש כמה כלי עזר לשינוי הצבע של נכס, באמצעות המאפיינים android:tint ו-tintMode.

אפשר גם להשמיט משאבים שהם רק מקבילים של משאב אחר ברוטציה. קטע הקוד הבא הוא דוגמה להפיכת 'לייק' ל'דיסלייק' על ידי סיבוב באמצע התמונה וסיבוב שלה ב-180 מעלות:

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/ic_thumb_up"
    android:pivotX="50%"
    android:pivotY="50%"
    android:fromDegrees="180" />

עיבוד מקוד

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

קובצי PNG מסוג Crunch

הכלי aapt יכול לבצע אופטימיזציה של משאבי התמונות שמוצבים בres/drawable/ עם דחיסת נתונים ללא אובדן מידע במהלך תהליך ה-build. לדוגמה, הכלי aapt יכול להמיר קובץ PNG בצבעים אמיתיים שלא דורשים יותר מ-256 צבעים לקובץ PNG של 8 ביט עם לוח צבעים. אם עושים זאת, מתקבלת תמונה באיכות זהה אבל טביעת הרגל הפחמנית קטנה יותר של הזיכרון.

ל-aapt יש את המגבלות הבאות:

  • הכלי aapt לא מכווץ קובצי PNG שכלולים בasset/ .
  • קובצי תמונות צריכים לכלול 256 צבעים או פחות כדי שכלי aapt יוכל לבצע אופטימיזציה שלהם.
  • ייתכן שהכלי aapt יביא להגדלה של קבצי PNG שכבר דחוסים. כדי למנוע במקרה הזה אפשר להשתמש בדגל isCrunchPngs כדי להשבית את התהליך הזה בקובצי PNG:
  • KotlinGroovy
        buildTypes.all { isCrunchPngs = false }
        
        buildTypes.all { isCrunchPngs = false }
        

דחיסת קובצי PNG ו-JPEG

אפשר להקטין את הגודל של קובצי PNG בלי לגרוע מאיכות התמונה באמצעות כלים כמו pngcrush, pngquant, או zopflipng. כל הכלים האלה יכולים להקטין את קובץ ה-PNG תוך שמירה על איכות התמונה.

הכלי pngcrush יעיל במיוחד. הכלי הזה מבצע איטרציה על בסיס מסנני PNG zlib (Deflate) באמצעות כל שילוב של מסננים ופרמטרים כדי לדחוס את התמונה. לאחר מכן הוא בוחר את התצורה שיוצרת את הפלט הדחוס הקטן ביותר.

כדי לדחוס קובצי JPEG, אפשר להשתמש בכלים כמו packJPG ו-guetzli.

שימוש בפורמט הקובץ WebP

במקום להשתמש בקובצי PNG או JPEG, אפשר להשתמש גם פורמט הקובץ WebP של התמונות. פורמט WebP מספק שקיפות ודחיסת נתונים מסוג lossy, כמו JPG ו-PNG, והוא יכול לספק דחיסת נתונים טובה יותר מאשר JPEG או PNG.

אפשר להמיר תמונות קיימות בפורמט BMP,‏ JPG,‏ PNG או GIF סטטי לפורמט WebP באמצעות Android Studio. מידע נוסף זמין במאמר יצירת תמונות WebP.

שימוש בגרפיקה וקטורית

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

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

למידע נוסף על עבודה עם אובייקטים מסוג VectorDrawable: נכסים דיגיטליים.

שימוש בגרפיקה וקטורית לתמונות מונפשות

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

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

הפחתת הקוד המקורי וקוד Java

אפשר להשתמש בשיטות הבאות כדי להקטין את הגודל של Java ושל Native Codebase אפליקציה.

מסירים קוד מיותר שנוצר

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

נמנעים מספירות

enum אחד יכול להוסיף בין 1.0 ל-1.4KB לקובץ classes.dex של האפליקציה. הוספות כאלה יכולות לצבור במהירות במערכות מורכבות או בספריות משותפות. אם אפשר, כדאי להשתמש בהערה @IntDef ובצמצום קוד כדי להסיר את המניינים ולהמיר אותם למספרים שלמים. המרה מהסוג הזה שומרת על כל היתרונות של בטיחות הסוגים של enums.

הקטנת הגודל של הקבצים הבינאריים המקוריים

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

הסרת סמלי ניפוי באגים

כדאי להשתמש בסמלים לניפוי באגים אם האפליקציה נמצאת בפיתוח ועדיין מחייבת שימוש בניפוי באגים. כדאי להשתמש הכלי arm-eabi-strip שמסופק ב-Android NDK להסרת ניפוי באגים מיותר מספריות מקוריות. לאחר מכן תוכלו לקמפל את גרסה ה-build של המוצר.

הימנעות מחילוץ ספריות מקוריות

כשיוצרים את גרסת ההפצה של האפליקציה, צריך לארוז קובצי .so לא דחוסים APK לפי הגדרה useLegacyPackaging אל false בקובץ build.gradle.kts של האפליקציה שלך. השבתת הסימון הזה מונעת PackageManager מ- העתקת .so קבצים מה-APK למערכת הקבצים במהלך ההתקנה. השיטה הזו מקטינה את הגודל של עדכוני האפליקציה.

ניהול מספר חבילות APK רזות

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

אם לא מפרסמים את האפליקציה ב-Google Play, אפשר לפלח את האפליקציה לכמה חבילות APK, שונה בהתאם לגורמים כמו גודל המסך או תמיכה בטקסטורה של GPU.

כשמשתמש מוריד את האפליקציה שלך, המכשיר שלו מקבל את ה-APK הנכון, בהתאם תכונות והגדרות. כך, מכשירים לא יקבלו נכסים עבור תכונות שהמכשירים לא מקבלים שיש. לדוגמה, אם למשתמש יש מכשיר hdpi, הוא לא צריך xxxhdpi שאולי תוכלו לכלול במכשירים עם מסכים עם צפיפות גבוהה יותר.

אפשר לקרוא מידע נוסף במאמר יצירת גרסאות מרובות חבילות APK ותמיכה ב-APK מרובים.