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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

איך נמנעים משליפה של ספריות מקוריות

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