بهینه سازی برای نویسندگان کتابخانه

به عنوان یک نویسنده کتابخانه، باید مطمئن شوید که توسعه دهندگان برنامه می توانند به راحتی کتابخانه شما را در برنامه خود بگنجانند و در عین حال تجربه کاربر نهایی با کیفیت بالا را حفظ کنند. باید مطمئن شوید که کتابخانه شما بدون راه‌اندازی اضافی با بهینه‌سازی Android سازگار است یا سندی مبنی بر اینکه کتابخانه ممکن است برای استفاده در Android نامناسب باشد.

این مستندات برای توسعه دهندگان کتابخانه های منتشر شده هدف قرار می گیرد، اما ممکن است برای توسعه دهندگان ماژول های کتابخانه داخلی در یک برنامه بزرگ و مدولار شده نیز مفید باشد.

اگر توسعه‌دهنده برنامه هستید و می‌خواهید درباره بهینه‌سازی برنامه Android خود بیاموزید، به فعال کردن بهینه‌سازی برنامه مراجعه کنید. برای اطلاع از اینکه کدام کتابخانه ها برای استفاده مناسب هستند، به انتخاب عاقلانه کتابخانه ها مراجعه کنید.

از کدژن بر روی بازتاب استفاده کنید

در صورت امکان، از تولید کد ( کدژن ) بر روی بازتاب استفاده کنید. کدژن و انعکاس هر دو رویکردهای رایج برای جلوگیری از کد دیگ بخار در هنگام برنامه نویسی هستند، اما کدژن با بهینه ساز برنامه مانند R8 سازگارتر است:

  • با کدژن، کد در طول فرآیند ساخت، تجزیه و تحلیل و اصلاح می شود. از آنجایی که پس از زمان کامپایل هیچ تغییر عمده‌ای وجود ندارد، بهینه‌ساز می‌داند که در نهایت به چه کدی نیاز است و چه چیزی را می‌توان با خیال راحت حذف کرد.
  • با بازتاب، کد در زمان اجرا تجزیه و تحلیل و دستکاری می شود. از آنجایی که کد تا زمانی که اجرا نشود واقعاً نهایی نمی شود، بهینه ساز نمی داند چه کدی را می توان با خیال راحت حذف کرد. احتمالاً کدهایی را که به صورت پویا از طریق بازتاب در زمان اجرا استفاده می‌شوند، حذف می‌کند، که باعث خرابی برنامه برای کاربران می‌شود.

بسیاری از کتابخانه های مدرن به جای بازتاب از کدژن استفاده می کنند. KSP را برای یک ورودی مشترک که توسط Room ، Dagger2 و بسیاری دیگر استفاده می‌شود، ببینید.

وقتی انعکاس مشکلی نداره

اگر باید از بازتاب استفاده کنید، فقط باید به یکی از موارد زیر فکر کنید:

  • انواع هدفمند خاص (پیاده‌کننده‌های رابط یا زیر کلاس‌های خاص)
  • کد با استفاده از یک حاشیه نویسی زمان اجرا خاص

استفاده از بازتاب در این روش هزینه زمان اجرا را محدود می کند و نوشتن قوانین حفظ مشتری هدفمند را امکان پذیر می کند.

این شکل خاص و هدفمند انعکاس الگویی است که می‌توانید هم در چارچوب Android (مثلاً هنگام افزایش فعالیت‌ها، نماها و نقشه‌ها) و هم در کتابخانه‌های AndroidX (به عنوان مثال هنگام ساخت WorkManager ListenableWorkers یا RoomDatabases ) مشاهده کنید. در مقابل، بازتاب باز Gson برای استفاده در برنامه‌های Android مناسب نیست .

انواع قوانین نگهداری در کتابخانه ها

دو نوع متمایز از قوانین نگهداری وجود دارد که می توانید در کتابخانه ها داشته باشید:

  • قوانین نگهداری مصرف کننده باید قوانینی را مشخص کند که هر آنچه را که کتابخانه به آن فکر می کند حفظ کند. اگر کتابخانه ای از بازتاب یا 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 استفاده کنید. برای اطلاعات بیشتر، به راهنمای ما در مورد ایجاد ماژول های کتابخانه مراجعه کنید.

کاتلین

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 خروجی شما قرار می گیرند.

با بررسی اینکه قوانین در دایرکتوری META-INF/proguard قرار دارند، بررسی کنید که قوانین بسته‌بندی نهایی JAR به درستی انجام می‌شود.

بهینه سازی ساخت کتابخانه AAR (پیشرفته)

به طور کلی، شما نباید نیازی به بهینه سازی ساخت کتابخانه به طور مستقیم داشته باشید زیرا بهینه سازی های ممکن در زمان ساخت کتابخانه بسیار محدود است. تنها در طول ساخت برنامه، زمانی که یک کتابخانه به عنوان بخشی از یک برنامه کاربردی گنجانده می شود، R8 می تواند بداند که چگونه از تمام روش های کتابخانه استفاده می شود، و کدام پارامترها ارسال می شوند. به‌عنوان یک توسعه‌دهنده کتابخانه، قبل از بهینه‌سازی کتابخانه، باید در مورد مراحل متعدد بهینه‌سازی و حفظ رفتار، هم در زمان ساخت کتابخانه و هم در زمان ساخت برنامه، استدلال کنید.

اگر همچنان می‌خواهید کتابخانه خود را در زمان ساخت بهینه کنید، این افزونه توسط Android Gradle پشتیبانی می‌شود.

کاتلین

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 در مقابل در کتابخانه بسته بندی می شود تا بر روی بهینه سازی هایی که بعداً در طول ساخت برنامه ای که کتابخانه شما را مصرف می کند تأثیر بگذارد.

برای مثال، اگر کتابخانه شما از بازتاب برای ساخت کلاس‌های داخلی استفاده می‌کند، ممکن است لازم باشد قوانین keep را هم در proguardFiles و هم consumerProguardFiles تعریف کنید.

اگر از -repackageclasses در ساخت کتابخانه خود استفاده می کنید، کلاس ها را به یک بسته فرعی در داخل بسته کتابخانه خود بسته بندی کنید. برای مثال، از -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).