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

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

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

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

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

ב-Google Play נאכפת הגבלה על גודל ההורדה הדחוס של 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]

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

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, צריך להפעיל את כיווץ הקוד. במהלך תהליך ה-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), ה-API של NDK‏ ImageDecoder הורחב כדי לפענח את כל הפריימים ונתוני התזמון של תמונות שמשתמשות בפורמטים של קובצי GIF מונפשים ו-WebP מונפשים.

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

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

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

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

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

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

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

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

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

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

לחלק מהתמונות לא נדרש משאב של תמונה סטטית. במקום זאת, המסגרת יכולה לצייר את התמונה באופן דינמי במהלך זמן הריצה. אובייקטים מסוג Drawable – או <shape> ב-XML – יכולים לתפוס כמות קטנה מאוד של מקום ב-APK. בנוסף, אובייקטים מסוג Drawable ב-XML יוצרים תמונות מונוכרום שתואמות להנחיות של 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/.
  • קובצי תמונות צריכים לכלול 256 צבעים או פחות כדי שכלי aapt יוכל לבצע אופטימיזציה שלהם.
  • הכלי aapt עשוי להגדיל את קובצי PNG שכבר דחוסים. כדי למנוע זאת, אפשר להשתמש בדגל isCrunchPngs כדי להשבית את התהליך הזה בקובצי PNG:
  • Kotlin

        buildTypes.all { isCrunchPngs = false }
        

    Groovy

        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 כדי ליצור אנימציות של פריים אחרי פריים, כי הפעולה הזו דורשת קובץ נפרד של מפת סיביות (bitmap) לכל פריים באנימציה. הפעולה הזו מגדילה משמעותית את הגודל של ה-APK.

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

צמצום הקוד המקורי והקוד ב-Java

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

הסרת קוד שנוצר שלא נדרש

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

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

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

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

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

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

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

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

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

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

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

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

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

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