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

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

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

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

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

ב-Google Play יש הגבלה על גודל ההורדה הדחוס של אפליקציות שפורסמו באמצעות קובצי App Bundle, והיא עומדת על 200MB. אפשר להשתמש ב-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]

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

Kotlin

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, צריך להפעיל כיווץ קוד. במהלך תהליך הבנייה, R8 מסיר קוד שלא נמצא בשימוש. לאחר מכן, הפלאגין של Android Gradle מסיר את המשאבים שלא נמצאים בשימוש.

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

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

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

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

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

פענוח של תמונות מונפשות מובנות

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

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

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

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

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

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

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

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

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

מידע נוסף על דחיסות המסך זמין במאמר בנושא גדלים ודחיסויות של מסכים.

שימוש באובייקטים שניתן לצייר

חלק מהתמונות לא דורשות משאב תמונה סטטית. במקום זאת, אפשר להגדיר את המסגרת כך שתצייר את התמונה באופן דינמי בזמן הריצה. אובייקטים של 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

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

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

  • הכלי aapt לא מכווץ קובצי PNG שנמצאים בתיקייה asset/.
  • כדי שהכלי aapt יוכל לבצע אופטימיזציה של קובצי תמונות, צריך להשתמש ב-256 צבעים או פחות.
  • יכול להיות שהכלי aapt יגדיל קובצי PNG שכבר דחוסים. כדי למנוע את זה, אפשר להשתמש בדגל isCrunchPngs כדי להשבית את התהליך הזה עבור קובצי PNG:
  • Kotlin

        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, אפשר לעיין במאמר Drawables.

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

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

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

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

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

הסרה של קוד מיותר שנוצר

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

הימנעות משימוש בספירות

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

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

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

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

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

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

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

שמירה על כמה חבילות APK רזות

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

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

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

מידע נוסף זמין במאמרים יצירת כמה קובצי APK ותמיכה בכמה קובצי APK.