שימוש בשיטות המומלצות

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

דוגמאות טובות לכללי שמירה

כללי שמירה מוגדרים היטב הם ספציפיים ככל האפשר ועומדים בדפוסים הבאים:

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

    -keepclassmembers class com.example.MyClass {
      void someSpecificMethod();
    }
    
    -keepclassmembers ** extends com.example.MyBaseClass {
      void someSpecificMethod();
    }
    
    -keepclassmembers @com.example.MyAnnotation class ** {
      void someSpecificMethod();
    }
    
  • בכל הזדמנות, כדאי להשתמש בהערות בקוד המקור ואז לטרגט את ההערות האלה ישירות בכללי השמירה. כך נוצר קישור ברור ומפורש בין הקוד לבין הכללים ששומרים עליו, וההגדרה הופכת לחזקה יותר, קלה יותר להבנה ופחות רגישה לשיבושים כשמתבצעים שינויים בקוד.

    לדוגמה, בקטע הקוד הבא אפשר לראות איך שומרים את המחלקה MyClass, וגם מחלקות אחרות שמוגדרות להן הערות באמצעות @com.example.DisplayComponent:

    // In the source code
    @com.example.DisplayComponent
    class MyClass { /* ... */ }
    
    // In the keep rules
    -keep @com.example.DisplayComponent class * {*;}
    

    מומלץ לתת להערות שמות שיספקו הקשר משמעותי לגבי הסיבה לשמירת חלקי הקוד. לדוגמה, אפשר להשתמש ב-@DisplayComponent באפליקציה שבה רכיבי התצוגה דורשים לשמור חלקים מסוימים.

  • ככל שאפשר, צריך להצהיר על מפרט הגורמים ולהפנות רק לחלקים של המחלקה שצריך לשמור כדי שהאפליקציה תפעל. מומלץ לא ליישם כלל על מחלקה שלמה על ידי הגדרה של היקף הגורמים האופציונלי כ-{ *; }, אלא אם יש בכך צורך מובהק.

    -keepclassmembers com.example.MyClass {
      void someSpecificMethod();
      void @com.example.MyAnnotation *;
    }
    
  • אם אתם משתמשים באפשרות הגלובלית repackageclasses, אל תציינו את שם החבילה האופציונלי. ציון של השם הזה יהפוך את קובצי ה-DEX לקטנים יותר, כי הקידומת של החבילה מושמטת בשמות המחלקה שנארזים מחדש.

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

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

-keepclassmembers class com.example.pkg.** { *; }

דברים שכדאי להימנע מהם

יש הרבה אפשרויות לכללי השמירה, אבל כדי ליהנות משיפורים מדידים וקבועים בביצועים, אנחנו ממליצים לא להשתמש באפשרויות הבאות:

  • אל תשתמשו בכללי שמירה שחלים על כל החבילה, כמו -keep class com.example.pkg.** { *; } לטווח ארוך. אפשר להשתמש בהם באופן זמני כדי לעקוף בעיות בהגדרת R8. מידע נוסף זמין במאמר בנושא הגבלת היקף האופטימיזציה. באופן כללי, צריך להיזהר עם תווים כלליים – חשוב לוודא ששומרים רק את הקוד שצריך.
  • במידת האפשר, כדאי להימנע מספריות שמציעות להעתיק ולהדביק כללי שמירה כשמשתמשים בהן, במיוחד כללי שמירה שחלים על כל החבילה. בספריות שמיועדות לפעול בצורה טובה ב-Android, מומלץ להימנע ככל האפשר משימוש ב-reflection, ולהטמיע כללי שמירה על נתונים של צרכנים כשהדבר נדרש.
  • מומלץ להימנע משימוש באופרטור ההיפוך ! בכללי השמירה, כי זה יכול לגרום לכלל לפעול בטעות על כמעט כל מחלקה באפליקציה.

אם אתם לא מצליחים לפעול לפי הכללים האלה, יכול להיות שאתם משתמשים ב-reflection פתוח בהיקף מאוד גדול. כדאי להימנע מ-reflection או מהספרייה באמצעות reflection (ראו את המקרה לדוגמה של Gson).