כדי להפעיל אופטימיזציה של אפליקציות, צריך להשתמש בספריות שתואמות לאופטימיזציה של Android. אם ספרייה לא מוגדרת לאופטימיזציה ל-Android – למשל, אם היא משתמשת ברפלקציה בלי לאגד כללי שמירה משויכים – יכול להיות שהיא לא תתאים לאפליקציית Android. בדף הזה מוסבר למה חלק מהספריות מתאימות יותר לאופטימיזציה של אפליקציות, ומוצגים טיפים כלליים שיעזרו לכם לבחור.
העדפה של יצירת קוד על פני רפלקציה
בדרך כלל, כדאי לבחור ספריות שמשתמשות ביצירת קוד (codegen) במקום ברפלקציה. באמצעות codegen, האופטימיזציה יכולה לקבוע בקלות רבה יותר איזה קוד נמצא בשימוש בפועל בזמן הריצה ואיזה קוד אפשר להסיר. יכול להיות שיהיה לכם קשה להבין אם ספרייה מסוימת משתמשת ב-codegen או ב-reflection, אבל יש כמה סימנים שיכולים לעזור לכם – כדאי לעיין בטיפים.
מידע נוסף על codegen לעומת reflection זמין במאמר Optimization for library authors.
טיפים כלליים לבחירת ספריות
הטיפים האלה יעזרו לכם לוודא שהספריות שלכם תואמות לאופטימיזציה של האפליקציה.
בדיקה אם יש בעיות באופטימיזציה
כששוקלים להשתמש בספרייה חדשה, כדאי לעיין בכלי למעקב אחר בעיות של הספרייה ובדיונים באינטרנט כדי לבדוק אם יש בעיות שקשורות למיניפיקציה או להגדרת אופטימיזציה של האפליקציה. אם יש, כדאי לחפש חלופות לספרייה הזו. חשוב לזכור:
- ספריות AndroidX וספריות כמו Hilt פועלות היטב עם אופטימיזציה של אפליקציות, כי הן משתמשות ב-codegen במקום ב-reflection. כשהם משתמשים ברפלקציה, הם מספקים כללי שמירה מינימליים כדי לשמור רק את הקוד שנדרש.
- ספריות סריאליזציה משתמשות לעיתים קרובות ברפלקציה כדי להימנע מקוד סטנדרטי חוזר על עצמו כשיוצרים מופעים של אובייקטים או מבצעים סריאליזציה שלהם. במקום להשתמש בגישות שמבוססות על רפלקציה (כמו Gson ל-JSON), כדאי לחפש ספריות שמשתמשות ביצירת קוד כדי להימנע מהבעיות האלה. למשל, אפשר להשתמש ב-Kotlin Serialization.
- מומלץ להימנע מספריות שכוללות כללי שמירה שחלים על כל החבילה, אם אפשר. כללי שמירה ברמת החבילה יכולים לעזור לפתור שגיאות, אבל בסופו של דבר צריך לצמצם את כללי השמירה הרחבים כך שישמרו רק את הקוד שנדרש. מידע נוסף זמין במאמר אימוץ אופטימיזציות באופן הדרגתי.
- ספריות לא צריכות לחייב אתכם להעתיק ולהדביק כללי שמירה ממסמכים לקובץ בפרויקט, במיוחד לא כללי שמירה שחלים על כל החבילה. בטווח הארוך, הכללים האלה הופכים לנטל תחזוקה על מפתח האפליקציה, וקשה לבצע בהם אופטימיזציה ולשנות אותם עם הזמן.
הפעלת אופטימיזציה אחרי הוספה של ספרייה חדשה
כשמוסיפים ספרייה חדשה, צריך להפעיל את האופטימיזציה ולבדוק אם יש שגיאות. אם יש שגיאות, מחפשים חלופות לספרייה הזו או כותבים כללי שמירה. אם ספרייה לא תואמת לאופטימיזציה, צריך לשלוח דוח על באג בספרייה הזו.
הכללים מצטברים
חשוב לדעת שכללי השמירה הם מצטברים. כלומר, אי אפשר להסיר כללים מסוימים שכלולים בתלות של ספרייה, והם עשויים להשפיע על הקומפילציה של חלקים אחרים באפליקציה. לדוגמה, אם ספרייה כוללת כלל להשבתת אופטימיזציות של קוד, הכלל הזה משבית את האופטימיזציות של כל הפרויקט.
בדיקה של שימוש בהשתקפות (מתקדם)
יכול להיות שאפשר לדעת אם ספריה משתמשת בהשתקפות על ידי בדיקת הקוד שלה. אם הספרייה משתמשת בהשתקפות, צריך לוודא שהיא מספקת כללי שמירה משויכים. ספרייה כנראה משתמשת בהשתקפות אם היא מבצעת את הפעולות הבאות:
- היא משתמשת בכיתות או ב-methods מהחבילות
kotlin.reflectאוjava.lang.reflect - נעשה שימוש בפונקציות
Class.forNameאוclassLoader.getClass - קורא הערות בזמן ריצה, למשל אם הוא מאחסן ערך של הערה באמצעות
val value = myClass.getAnnotation()אוval value = myMethod.getAnnotation()ואז מבצע פעולה כלשהי עםvalue הפעלת שיטות באמצעות שם השיטה כמחרוזת, לדוגמה:
// Calls the private `processData` API with reflection myObject.javaClass.getMethod("processData", DataType::class.java) ?.invoke(myObject, data)
סינון כללי שמירה לא תקינים (מתקדם)
מומלץ להימנע מספריות עם כללי שמירה שמשאירים קוד שבאמת צריך להסיר. אבל אם אתם חייבים להשתמש בהם, אתם יכולים לסנן את הכללים כמו שמוצג בקוד הבא:
// If you're using AGP 8.4 and higher
buildTypes {
release {
optimization.keepRules {
it.ignoreFrom("com.somelibrary:somelibrary")
}
}
}
// If you're using AGP 7.3-8.3
buildTypes {
release {
optimization.keepRules {
it.ignoreExternalDependencies("com.somelibrary:somelibrary")
}
}
}
מקרה לדוגמה: למה Gson נכשל באופטימיזציות
Gson היא ספריית סריאליזציה שגורמת לעיתים קרובות לבעיות באופטימיזציה של אפליקציות, כי היא משתמשת הרבה ברפלקציה. בקטע הקוד הבא מוצג אופן השימוש ב-Gson, שיכול לגרום בקלות לקריסות בזמן הריצה. שימו לב: כשמשתמשים ב-Gson כדי לקבל רשימה של אובייקטים מסוג User, לא קוראים לקונסטרוקטור ולא מעבירים מפעל לפונקציה fromJson(). אם ספרייה יוצרת או משתמשת במחלקות שהוגדרו באפליקציה בלי להשתמש באחת מהאפשרויות הבאות, זה סימן לכך שהיא עשויה להשתמש בהשתקפות פתוחה:
- מחלקה של אפליקציה שמטמיעה ספרייה, או ממשק או מחלקה רגילים
- פלאגין ליצירת קוד כמו KSP
class User(val name: String)
class UserList(val users: List<User>)
// This code runs in debug mode, but crashes when optimizations are enabled
Gson().fromJson("""[{"name":"myname"}]""", User::class.java).toString()
כש-R8 מנתח את הקוד הזה ולא רואה את UserList או User מופעלים בשום מקום, הוא יכול לשנות את השם של השדות או להסיר בנאים שלא נראה שהם בשימוש, ולגרום לקריסת האפליקציה. אם אתם משתמשים בספריות אחרות בדרכים דומות, כדאי לוודא שהן לא יפריעו לאופטימיזציה של האפליקציה. אם הן מפריעות, אל תשתמשו בהן.
שימו לב שגם Room וגם Hilt יוצרים טיפוסים שמוגדרים באפליקציה, אבל משתמשים ב-codegen כדי להימנע מהצורך ברפלקציה.