برای فعال کردن بهینهسازی برنامه، باید از کتابخانههایی استفاده کنید که با بهینهسازی اندروید سازگار باشند. اگر یک کتابخانه برای بهینهسازی اندروید پیکربندی نشده باشد - برای مثال، اگر از reflection بدون bunding مرتبط با keep rules استفاده کند - ممکن است برای یک برنامه اندروید مناسب نباشد. این صفحه توضیح میدهد که چرا برخی از کتابخانهها برای بهینهسازی برنامه مناسبتر هستند و نکات کلی برای کمک به شما در انتخاب ارائه میدهد.
کدگن را به رفلکشن ترجیح دهید
به طور کلی، شما باید کتابخانههایی را انتخاب کنید که به جای reflection از code generation ( codegen ) استفاده میکنند. با codegen، بهینهساز میتواند راحتتر تشخیص دهد که چه کدی واقعاً در زمان اجرا استفاده میشود و چه کدی را میتوان حذف کرد. تشخیص اینکه یک کتابخانه از codegen یا reflection استفاده میکند، میتواند دشوار باشد، اما نشانههایی وجود دارد - برای کمک به نکات مراجعه کنید.
برای اطلاعات بیشتر در مورد codegen در مقابل reflection، به Optimization for library authors مراجعه کنید.
نکات کلی هنگام انتخاب کتابخانهها
از این نکات برای اطمینان از سازگاری کتابخانههایتان با بهینهسازی برنامه استفاده کنید.
بررسی مشکلات بهینهسازی
هنگام بررسی یک کتابخانه جدید، ردیاب مشکلات کتابخانه و بحثهای آنلاین را بررسی کنید تا بررسی کنید که آیا مشکلاتی مربوط به کوچکسازی یا پیکربندی بهینهسازی برنامه وجود دارد یا خیر. در صورت وجود، باید سعی کنید به دنبال جایگزینهایی برای آن کتابخانه باشید. موارد زیر را در نظر داشته باشید:
- کتابخانههای AndroidX و کتابخانههایی مانند Hilt به خوبی با بهینهسازی برنامه کار میکنند زیرا به جای reflection از codegen استفاده میکنند. وقتی از reflection استفاده میکنند، قوانین keep حداقلی را ارائه میدهند تا فقط کد مورد نیاز را نگه دارند.
- کتابخانههای سریالسازی اغلب از reflection برای جلوگیری از کد تکراری هنگام نمونهسازی یا سریالسازی اشیاء استفاده میکنند. به جای رویکردهای مبتنی بر reflection (مانند Gson برای JSON)، به دنبال کتابخانههایی باشید که از codegen برای جلوگیری از این مشکلات استفاده میکنند، به عنوان مثال با استفاده از Kotlin Serialization .
- در صورت امکان باید از کتابخانههایی که شامل قوانین keep در سطح بسته هستند، اجتناب شود. قوانین keep در سطح بسته میتوانند به رفع خطاها کمک کنند، اما قوانین keep گسترده در نهایت باید اصلاح شوند تا فقط کد مورد نیاز را نگه دارند. برای اطلاعات بیشتر، به Adopt optimizations incrementally مراجعه کنید.
- کتابخانهها نباید شما را ملزم به کپی و پیست کردن قوانین keep از مستندات در یک فایل در پروژهتان کنند، به خصوص قوانین keep در کل پکیج. این قوانین در درازمدت به یک بار نگهداری برای توسعهدهنده برنامه تبدیل میشوند و بهینهسازی و تغییر آنها در طول زمان دشوار است.
فعال کردن بهینهسازی پس از افزودن کتابخانه جدید
وقتی کتابخانه جدیدی اضافه میکنید، بهینهسازی را بعداً فعال کنید و بررسی کنید که آیا خطایی وجود دارد یا خیر. اگر خطایی وجود دارد، به دنبال جایگزینهایی برای آن کتابخانه باشید یا قوانین keep را بنویسید. اگر کتابخانهای با بهینهسازی سازگار نیست، یک اشکال (bug) را با آن کتابخانه ثبت کنید.
قوانین افزودنی هستند
توجه داشته باشید که قوانین keep افزایشی هستند. این بدان معناست که قوانین خاصی که یک وابستگی کتابخانهای شامل آنها میشود، قابل حذف نیستند و ممکن است بر کامپایل سایر بخشهای برنامه شما تأثیر بگذارند. برای مثال، اگر یک کتابخانه شامل قانونی برای غیرفعال کردن بهینهسازی کد باشد، آن قانون بهینهسازیها را برای کل پروژه شما غیرفعال میکند.
بررسی استفاده از بازتاب (پیشرفته)
شما میتوانید با بررسی کد یک کتابخانه متوجه شوید که آیا از reflection استفاده میکند یا خیر. اگر کتابخانه از reflection استفاده میکند، بررسی کنید که آیا قوانین keep مرتبط را ارائه میدهد یا خیر. اگر کتابخانهای موارد زیر را انجام دهد، احتمالاً از reflection استفاده میکند:
- از کلاسها یا متدهای پکیجهای
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)
فیلتر کردن قوانین بد برای نگه داشتن (پیشرفته)
شما باید از کتابخانههایی که دارای قوانین keep هستند و کدهایی را که واقعاً باید حذف شوند، نگه میدارند، اجتناب کنید. اما اگر مجبور به استفاده از آنها هستید، میتوانید قوانین را همانطور که در کد زیر نشان داده شده است، فیلتر کنید:
// 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 یک کتابخانه سریالسازی است که اغلب به دلیل استفاده زیاد از reflection، باعث ایجاد مشکلاتی در بهینهسازی برنامه میشود. قطعه کد زیر نحوه استفاده معمول از Gson را نشان میدهد که میتواند به راحتی باعث خرابی در زمان اجرا شود. توجه داشته باشید که وقتی از Gson برای دریافت لیستی از اشیاء User استفاده میکنید، سازنده را فراخوانی نمیکنید یا یک factory به تابع fromJson() ارسال نمیکنید. ساخت یا استفاده از کلاسهای تعریفشده توسط app بدون هیچ یک از موارد زیر، نشانهای از این است که یک کتابخانه ممکن است از open-ended reflection استفاده کند:
- کلاس برنامه که یک کتابخانه یا رابط یا کلاس استاندارد را پیادهسازی میکند
- افزونه تولید کد مانند 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 استفاده میکنند.