ככותבי ספריות, אתם צריכים לוודא שמפתחי אפליקציות יוכלו לשלב את הספרייה שלכם באפליקציה שלהם בקלות, תוך שמירה על חוויית משתמש איכותית. חשוב לוודא שהספרייה תואמת לאופטימיזציה של Android בלי הגדרה נוספת, או לתעד שהספרייה עשויה להיות לא מתאימה לשימוש ב-Android.
המסמך הזה מיועד למפתחים של ספריות שפורסמו, אבל הוא יכול להיות שימושי גם למפתחים של מודולים של ספריות פנימיות באפליקציה גדולה וממודולרית.
מפתחים של אפליקציות שרוצים לקבל מידע על אופטימיזציה של אפליקציות ל-Android יכולים לעיין במאמר הפעלת אופטימיזציה של אפליקציות. במאמר בחירת ספריות בצורה מושכלת מוסבר אילו ספריות מתאימות לשימוש.
שימוש ב-codegen במקום ב-reflection
במידת האפשר, מומלץ להשתמש ביצירת קוד (codegen) במקום ברפלקציה. יצירת קוד (codegen) והשתקפות (reflection) הן שתי גישות נפוצות להימנעות מקוד סטנדרטי בזמן התכנות, אבל יצירת קוד תואמת יותר למטמיע אפליקציות כמו R8:
- באמצעות codegen, הקוד מנותח ומשתנה במהלך תהליך ה-build. מכיוון שאין שינויים משמעותיים לאחר זמן הידור, האופטימיזטור יודע איזה קוד נדרש בסופו של דבר ואיזה קוד אפשר להסיר בבטחה.
- באמצעות הרפלקציה, הקוד מנותח ומנוהל בזמן הריצה. מכיוון שהקוד לא הושלם עד שהוא מופעל, האופטימיזטור לא יודע איזה קוד אפשר להסיר בבטחה. סביר להניח שהיא תסיר קוד שנעשה בו שימוש באופן דינמי באמצעות השתקפות במהלך זמן הריצה, וגורם לקריסות של האפליקציה אצל המשתמשים.
בספריות מודרניות רבות נעשה שימוש ב-codegen במקום ב-reflection. KSP הוא נקודת כניסה נפוצה שמשמשת את Room, Dagger2 ועוד רבים.
מתי אפשר להשתמש בהשתקפות
אם אתם חייבים להשתמש בהשתקפות, כדאי להשתמש בה רק באחת מהאפשרויות הבאות:
- סוגי טירגוט ספציפיים (ממשקי משנה או מפעילי ממשק ספציפיים)
- קוד עם הערה ספציפית בסביבת זמן הריצה
שימוש ברפלקציה באופן הזה מגביל את עלות זמן הריצה ומאפשר לכתוב כללי שמירה ממוקדים של נתוני צרכנים.
צורת ההשתקפות הספציפית והממוקדת הזו היא דפוס שאפשר לראות גם במסגרת של Android (לדוגמה, כשמנפחים פעילויות, תצוגות ורכיבי drawable) וגם בספריות AndroidX (לדוגמה, כשיוצרים WorkManager ListenableWorkers או RoomDatabases). לעומת זאת, ההשתקפות הפתוחה של Gson לא מתאימה לשימוש באפליקציות ל-Android.
כתיבת כללים לשמירת נתוני הצרכן
בספריות צריך לארוז כללי שמירה של 'צרכן', שמשתמשים באותו פורמט של כללי שמירה של אפליקציות. הכללים האלה מקובצים בפריטי ספרייה (קבצי AAR או JAR) ומנוצלים באופן אוטומטי במהלך האופטימיזציה של אפליקציות ל-Android כשמשתמשים בספרייה.
ספריות AAR
כדי להוסיף כללי צרכן לספריית AAR, משתמשים באפשרות consumerProguardFiles
בסקריפט ה-build של מודול הספרייה של Android. למידע נוסף, קראו את ההנחיות שלנו ליצירת מודולים של ספריות.
Kotlin
android { defaultConfig { consumerProguardFiles("consumer-proguard-rules.pro") } ... }
Groovy
android { defaultConfig { consumerProguardFiles 'consumer-proguard-rules.pro' } ... }
ספריות JAR
כדי לארוז כללים עם ספריית Kotlin/Java שנשלחת כקובץ JAR, צריך להוסיף את קובץ הכללים לתיקייה META-INF/proguard/
של קובץ ה-JAR הסופי, עם כל שם קובץ.
לדוגמה, אם הקוד נמצא ב-<libraryroot>/src/main/kotlin
, צריך להציב קובץ כללים של צרכן ב-<libraryroot>/src/main/resources/META-INF/proguard/consumer-proguard-rules.pro
, והכללים יקובצו במיקום הנכון בקובץ ה-JAR של הפלט.
מוודאים שהכללים נמצאים בספרייה META-INF/proguard
כדי לוודא שהכללים נכללים בחבילת ה-JAR הסופית.
אופטימיזציה של גרסה של ספרייה עם המלצות שיושמו באופן אוטומטי (מתקדם)
באופן כללי, לא מומלץ לבצע אופטימיזציה של גרסה של ספרייה ישירות, כי אפשרויות האופטימיזציה בזמן היצירה של הספרייה מוגבלות מאוד. רק במהלך ה-build של האפליקציה, כשספרייה כלולה כחלק מהאפליקציה, מערכת R8 יכולה לדעת איך נעשה שימוש בכל השיטות של הספרייה ואילו פרמטרים מועברים. מפתחים של ספריות צריכים להביא בחשבון כמה שלבים של אופטימיזציה ולשמור על ההתנהגות, גם בספרייה וגם בזמן ה-build של האפליקציה, לפני שהם מבצעים אופטימיזציה לספרייה.
אם אתם עדיין רוצים לבצע אופטימיזציה לספרייה בזמן ה-build, תוכלו להשתמש בפלאגין של Android Gradle.
Kotlin
android { buildTypes { release { isMinifyEnabled = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) } configureEach { consumerProguardFiles("consumer-rules.pro") } } }
Groovy
android { buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } configureEach { consumerProguardFiles "consumer-rules.pro" } } }
חשוב לזכור שההתנהגות של proguardFiles
שונה מאוד מזו של consumerProguardFiles
:
proguardFiles
משמשים בזמן ה-build, לרוב יחד עםgetDefaultProguardFile("proguard-android-optimize.txt")
, כדי להגדיר איזה חלק מהספרייה צריך להישמר במהלך ה-build של הספרייה. לפחות זהו ממשק ה-API הציבורי שלכם.- לעומת זאת,
consumerProguardFiles
נארזים בספרייה כדי להשפיע על האופטימיזציות שיחולו מאוחר יותר, במהלך ה-build של האפליקציה שמשתמשת בספרייה.
לדוגמה, אם בספרייה שלכם נעשה שימוש בהשתקפות (reflection) כדי ליצור כיתות פנימיות, יכול להיות שתצטרכו להגדיר את כללי השמירה גם ב-proguardFiles
וגם ב-consumerProguardFiles
.
אם אתם משתמשים ב--repackageclasses
ב-build של הספרייה, צריך לארוז מחדש את הכיתות בחבילת משנה בתוך החבילה של הספרייה. לדוגמה, צריך להשתמש ב--repackageclasses 'com.example.mylibrary.internal'
במקום ב--repackageclasses 'internal'
.
תמיכה בגרסאות שונות של R8 (מתקדם)
אפשר להתאים אישית כללים כדי לטרגט גרסאות ספציפיות של R8. כך הספרייה תפעל בצורה אופטימלית בפרויקטים שמשתמשים בגרסאות R8 חדשות יותר, ועדיין תהיה אפשרות להשתמש בכללים הקיימים בפרויקטים עם גרסאות R8 ישנות יותר.
כדי לציין כללי R8 ממוקדים, צריך לכלול אותם בספרייה META-INF/com.android.tools
בתוך classes.jar
של קובץ AAR או בספרייה META-INF/com.android.tools
של קובץ JAR.
In an AAR library:
proguard.txt (legacy location, the file name must be "proguard.txt")
classes.jar
└── META-INF
└── com.android.tools (location of targeted R8 rules)
├── r8-from-<X>-upto-<Y>/<R8-rule-files>
└── ... (more directories with the same name format)
In a JAR library:
META-INF
├── proguard/<ProGuard-rule-files> (legacy location)
└── com.android.tools (location of targeted R8 rules)
├── r8-from-<X>-upto-<Y>/<R8-rule-files>
└── ... (more directories with the same name format)
בספרייה META-INF/com.android.tools
יכולות להיות כמה ספריות משנה עם שמות בפורמט r8-from-<X>-upto-<Y>
, כדי לציין לאילו גרסאות של R8 הכללים נכתבו. כל ספריית משנה יכולה להכיל קובץ אחד או יותר שמכיל את כללי R8, עם כל שמות הקבצים והסיומת.
הערה: החלקים -from-<X>
ו--upto-<Y>
הם אופציונליים, הגרסה <Y>
היא בלעדית, וטווחי הגרסאות הם בדרך כלל רציפים, אבל יכולים גם לחפוף.
לדוגמה, r8
, r8-upto-8.0.0
, r8-from-8.0.0-upto-8.2.0
ו-r8-from-8.2.0
הם שמות של ספריות שמייצגות קבוצה של כללי R8 ממוקדים.
כל הגרסאות של R8 יכולות להשתמש בכללים שבספרייה r8
. אפשר להשתמש בכללים שבספרייה r8-from-8.0.0-upto-8.2.0
ב-R8 מגרסה 8.0.0 ועד לגרסה 8.2.0 לא כולל.
הפלאגין של Android Gradle משתמש במידע הזה כדי לבחור את כל הכללים שאפשר להשתמש בהם בגרסה הנוכחית של R8. אם בספרייה לא צוינו כללים של R8 שמוגדרים לטירגוט, הפלאגין של Android Gradle יבחר את הכללים מהמיקומים הקודמים (proguard.txt
עבור קובץ AAR או META-INF/proguard/<ProGuard-rule-files>
עבור קובץ JAR).