אם אתם יוצרים ספריות, אתם צריכים לוודא שמפתחי אפליקציות יוכלו לשלב את הספרייה באפליקציה שלהם בקלות, תוך שמירה על חוויית משתמש איכותית. צריך לוודא שהספרייה תואמת לאופטימיזציה של Android בלי להגדיר הגדרות נוספות, או לציין שהספרייה לא מתאימה לשימוש ב-Android.
התיעוד הזה מיועד למפתחים של ספריות שפורסמו, אבל הוא יכול להיות שימושי גם למפתחים של מודולים פנימיים של ספריות באפליקציה גדולה ומודולרית.
מפתחי אפליקציות שרוצים לדעת איך לבצע אופטימיזציה לאפליקציות ל-Android יכולים לקרוא את המאמר בנושא הפעלת אופטימיזציה של אפליקציות. במאמר בחירה מושכלת של ספריות מוסבר אילו ספריות מתאימות לשימוש.
שימוש ב-codegen במקום ב-reflection
במידת האפשר, מומלץ להשתמש ביצירת קוד (codegen) במקום בהשתקפות. גם Codegen וגם Reflection הן גישות נפוצות להימנעות מקוד boilerplate במהלך תכנות, אבל Codegen תואם יותר לאופטימיזציה של אפליקציות כמו R8:
- ב-codegen, הקוד מנותח ומשתנה במהלך תהליך ה-build. מכיוון שאין שינויים משמעותיים אחרי זמן ההידור, האופטימיזציה יודעת איזה קוד נדרש בסופו של דבר ואיזה קוד אפשר להסיר בבטחה.
- באמצעות רפלקציה, הקוד מנותח ועובר מניפולציה בזמן הריצה. הקוד לא באמת סופי עד שהוא מופעל, ולכן הכלי לא יודע איזה קוד אפשר להסיר בבטחה. העדכון כנראה יסיר קוד שנעשה בו שימוש באופן דינמי באמצעות רפלקציה במהלך זמן הריצה, מה שגורם לקריסת האפליקציה אצל המשתמשים.
בספריות מודרניות רבות נעשה שימוש ביצירת קוד במקום בהשתקפות. אפשר לעיין בKSP כדי לראות נקודת כניסה נפוצה שמשמשת את Room, Dagger2 ועוד.
מתי אפשר להשתמש בהשתקפות
אם אתם חייבים להשתמש בהשתקפות, אתם יכולים להשתקף רק באחת מהאפשרויות הבאות:
- סוגים ספציפיים של טירגוט (מיישמי ממשק ספציפיים או מחלקות משנה)
- קידוד באמצעות הערת זמן ריצה ספציפית
השימוש ברפלקציה באופן הזה מגביל את עלות זמן הריצה ומאפשר לכתוב כללי שמירה ממוקדים לצרכנים.
ההשתקפות הספציפית והממוקדת הזו היא דפוס שניתן לראות גם ב-Android framework (לדוגמה, כשמנפחים פעילויות, תצוגות ופריטים שניתנים לציור) וגם בספריות AndroidX (לדוגמה, כשמבצעים בנייה של WorkManager
ListenableWorkers
או RoomDatabases
). לעומת זאת, ההשתקפות הפתוחה של Gson לא מתאימה לשימוש באפליקציות ל-Android.
סוגים של כללי שמירה בספריות
יש שני סוגים שונים של כללי שמירה שאפשר להגדיר בספריות:
- בכללי שמירה לצרכנים צריך לציין כללים לשמירה של כל מה שמופיע בספרייה. אם ספרייה משתמשת ב-reflection או ב-JNI כדי לקרוא לקוד שלה, או לקוד שהוגדר על ידי אפליקציית לקוח, הכללים האלה צריכים לתאר איזה קוד צריך לשמור. ספריות צריכות לארוז כללים לשמירת נתונים של צרכנים, שמשתמשים באותו פורמט כמו כללים לשמירת נתונים של אפליקציות. הכללים האלה נכללים בארטיפקטים של ספריות (AAR או JAR) והמערכת משתמשת בהם באופן אוטומטי במהלך האופטימיזציה של אפליקציית Android כשהספרייה נמצאת בשימוש. הכללים האלה נשמרים בקובץ שמצוין במאפיין
consumerProguardFiles
בקובץbuild.gradle.kts
(אוbuild.gradle
). מידע נוסף זמין במאמר בנושא כתיבת כללי שמירה לצרכנים. - כללי השמירה של הספרייה מופעלים בזמן הבנייה של הספרייה. הם נדרשים רק אם מחליטים לבצע אופטימיזציה חלקית של הספרייה בזמן הבנייה.
הם צריכים למנוע את ההסרה של ה-API הציבורי של הספרייה, אחרת ה-API הציבורי לא יופיע בהפצה של הספרייה, כלומר מפתחי אפליקציות לא יוכלו להשתמש בספרייה. הכללים האלה מופיעים בקובץ שצוין במאפיין
proguardFiles
בקובץbuild.gradle.kts
(אוbuild.gradle
). מידע נוסף זמין במאמר בנושא אופטימיזציה של בניית ספריית AAR.
כתיבת כללי שמירה לצרכנים
בנוסף להנחיות הכלליות לגבי כללי שמירה, יש גם המלצות ספציפיות ליוצרי ספריות.
- אל תשתמשו בכללים גלובליים לא הולמים – אל תציבו הגדרות גלובליות כמו
-dontobfuscate
או-allowaccessmodification
בקובץ הכללים של הספרייה, כי הן משפיעות על כל האפליקציות שמשתמשות בספרייה. - אל תשתמשו ב-
-repackageclasses
בקובץ הכללים של שמירת נתונים לצרכן בספרייה שלכם. עם זאת, כדי לבצע אופטימיזציה של בניית הספרייה, אפשר להשתמש ב--repackageclasses
עם שם חבילה פנימי, כמו<your.library.package>.internal
, בקובץ כללי השמירה של הספרייה. הפעולה הזו יכולה לשפר את היעילות של הספרייה גם אם האפליקציות שמשתמשות בה לא מותאמות, אבל בדרך כלל היא לא נחוצה כי האפליקציות אמורות לבצע אופטימיזציה בעצמן. לפרטים נוספים על אופטימיזציה של ספריות, אפשר לעיין במאמר אופטימיזציה למפתחי ספריות. - צריך להצהיר על כל המאפיינים שדרושים לספרייה כדי לפעול בקובצי כללי השמירה של הספרייה, גם אם יש חפיפה עם המאפיינים שמוגדרים ב-
proguard-android-optimize.txt
. - אם אתם צריכים את המאפיינים הבאים בהפצה של הספרייה, אתם צריכים לשמור אותם בקובץ כללי השמירה של הספרייה, ולא בקובץ כללי השמירה של הצרכן של הספרייה:
AnnotationDefault
EnclosingMethod
Exceptions
InnerClasses
RuntimeInvisibleAnnotations
RuntimeInvisibleParameterAnnotations
RuntimeInvisibleTypeAnnotations
RuntimeVisibleAnnotations
RuntimeVisibleParameterAnnotations
RuntimeVisibleTypeAnnotations
Signature
- אם נעשה שימוש בהערות בזמן ריצה, מחברי הספריות צריכים להשאיר את מאפיין
RuntimeVisibleAnnotations
בכללי השמירה של הצרכן. - מחברי ספריות לא צריכים להשתמש באפשרויות הגלובליות הבאות בכללי השמירה של הצרכן:
-include
-basedirectory
-injars
-outjars
-libraryjars
-repackageclasses
-flattenpackagehierarchy
-allowaccessmodification
-overloadaggressively
-renamesourcefileattribute
-ignorewarnings
-addconfigurationdebugging
-printconfiguration
-printmapping
-printusage
-printseeds
-applymapping
-obfuscationdictionary
-classobfuscationdictionary
-packageobfuscationdictionary
ספריות AAR
כדי להוסיף כללי צריכה לספריית AAR, משתמשים באפשרות consumerProguardFiles
בסקריפט הבנייה של מודול הספרייה של Android. מידע נוסף זמין בהנחיות שלנו ליצירת מודולים של ספריות.
Kotlin
android { defaultConfig { consumerProguardFiles("consumer-proguard-rules.pro") } ... }
גרוב
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 הפלט.
כדי לוודא שהכללים של חבילות ה-JAR הסופיות נכונים, בודקים שהכללים נמצאים בספרייה META-INF/proguard
.
אופטימיזציה של בניית ספריית AAR (מתקדם)
בדרך כלל, אין צורך לבצע אופטימיזציה של בניית ספרייה ישירות, כי האפשרויות לאופטימיזציה בזמן בניית הספרייה מוגבלות מאוד. רק במהלך בניית האפליקציה, כשספרייה נכללת כחלק מהאפליקציה, R8 יכול לדעת איך כל המתודות של הספרייה נמצאות בשימוש ואילו פרמטרים מועברים. כמפתחים של ספריות, אתם צריכים לחשוב על כמה שלבים של אופטימיזציה ולשמור על ההתנהגות, גם בזמן בניית הספריות וגם בזמן בניית האפליקציות, לפני שאתם מבצעים אופטימיזציה של הספריות.
אם עדיין רוצים לבצע אופטימיזציה של הספרייה בזמן הבנייה, אפשר לעשות זאת באמצעות Android Gradle Plugin.
Kotlin
android { buildTypes { release { isMinifyEnabled = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) } configureEach { consumerProguardFiles("consumer-rules.pro") } } }
גרוב
android { buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } configureEach { consumerProguardFiles "consumer-rules.pro" } } }
שימו לב שההתנהגות של proguardFiles
שונה מאוד מזו של consumerProguardFiles
:
-
proguardFiles
משמשים בזמן הבנייה, לרוב יחד עם getDefaultProguardFile("proguard-android-optimize.txt")
, כדי להגדיר איזה חלק מהספרייה צריך לשמור במהלך בניית הספרייה. לפחות, זה ה-API הציבורי שלכם. - לעומת זאת,
consumerProguardFiles
נארזים בספרייה כדי להשפיע על האופטימיזציות שיתבצעו בהמשך, במהלך ה-build של אפליקציה שמשתמשת בספרייה שלכם.
לדוגמה, אם הספרייה שלכם משתמשת ברפלקציה כדי ליצור מחלקות פנימיות, יכול להיות שתצטרכו להגדיר את כללי השמירה גם ב-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).