کتابخانه ها را هوشمندانه انتخاب کنید، کتابخانه ها را هوشمندانه انتخاب کنید

To enable app optimization, you must use libraries that are compatible with Android optimization. If a library isn't configured for Android optimization—for example, if it uses reflection without bundling associated keep rules—it might not be a good fit for an Android app. This page explains why some libraries are better suited for app optimization and provides general tips to help you choose.

نکات کلی هنگام انتخاب کتابخانه‌ها

از این نکات برای اطمینان از سازگاری کتابخانه‌هایتان با بهینه‌سازی برنامه استفاده کنید.

کدگن را به رفلکشن ترجیح دهید

کتابخانه‌هایی را انتخاب کنید که به جای reflection از code generation ( codegen ) استفاده می‌کنند. با codegen، بهینه‌ساز می‌تواند راحت‌تر تشخیص دهد که چه کدی واقعاً در زمان اجرا استفاده می‌شود و چه کدی را می‌توان حذف کرد. تشخیص اینکه یک کتابخانه از codegen یا reflection استفاده می‌کند، می‌تواند دشوار باشد، اما نشانه‌هایی وجود دارد - برای کمک به نکات مراجعه کنید.

برای اطلاعات بیشتر در مورد codegen در مقابل reflection، به Optimization for library authors مراجعه کنید.

بررسی استفاده از بازتاب (پیشرفته)

شما می‌توانید با بررسی کد یک کتابخانه متوجه شوید که آیا از 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)
    

بررسی مشکلات بهینه‌سازی

هنگام بررسی یک کتابخانه جدید، ردیاب مشکلات کتابخانه و بحث‌های آنلاین را بررسی کنید تا بررسی کنید که آیا مشکلاتی مربوط به کوچک‌سازی یا پیکربندی بهینه‌سازی برنامه وجود دارد یا خیر. در صورت وجود، باید سعی کنید به دنبال جایگزین‌هایی برای آن کتابخانه باشید. موارد زیر را در نظر داشته باشید:

  • کتابخانه‌های AndroidX و کتابخانه‌هایی مانند Hilt به خوبی با بهینه‌سازی برنامه کار می‌کنند، زیرا آنها بیشتر از codegen به جای reflection استفاده می‌کنند. وقتی از reflection استفاده می‌کنند، قوانین keep حداقلی را ارائه می‌دهند تا فقط کد مورد نیاز را نگه دارند.
  • کتابخانه‌های سریال‌سازی اغلب از reflection برای جلوگیری از کد تکراری هنگام نمونه‌سازی یا سریال‌سازی اشیاء استفاده می‌کنند. به جای رویکردهای مبتنی بر reflection (مانند Gson برای JSON)، به دنبال کتابخانه‌هایی باشید که از codegen برای جلوگیری از این مشکلات استفاده می‌کنند، به عنوان مثال با استفاده از Kotlin Serialization یا Moshi با codegen .
  • در صورت امکان، از کتابخانه‌هایی که شامل قوانین keep در سطح بسته هستند، اجتناب کنید. قوانین keep در سطح بسته می‌توانند به رفع خطاها کمک کنند، اما قوانین keep گسترده در نهایت باید اصلاح شوند تا فقط کد مورد نیاز را نگه دارند. برای اطلاعات بیشتر، به Adopt optimizations incrementally مراجعه کنید.
  • کتابخانه‌ها نباید شما را ملزم به کپی و پیست کردن قوانین keep از مستندات در یک فایل در پروژه‌تان کنند، به خصوص قوانین keep در کل پکیج. این قوانین در درازمدت به یک بار نگهداری برای توسعه‌دهنده برنامه تبدیل می‌شوند و بهینه‌سازی و تغییر آنها در طول زمان دشوار است.

فعال کردن بهینه‌سازی پس از افزودن کتابخانه جدید

وقتی کتابخانه جدیدی اضافه می‌کنید، بهینه‌سازی را بعداً فعال کنید و بررسی کنید که آیا خطایی وجود دارد یا خیر. اگر خطایی وجود دارد، به دنبال جایگزین‌هایی برای آن کتابخانه باشید یا قوانین keep را بنویسید. اگر کتابخانه‌ای با بهینه‌سازی سازگار نیست، یک اشکال (bug) را با آن کتابخانه ثبت کنید.

فیلتر کردن قوانین بد برای نگه داشتن (پیشرفته)

قوانین Keep افزودنی هستند. این بدان معناست که قوانین خاصی که یک وابستگی کتابخانه‌ای شامل آن‌ها می‌شود، قابل حذف نیستند و ممکن است بر کامپایل سایر بخش‌های برنامه شما تأثیر بگذارند. برای مثال، اگر یک کتابخانه شامل قانونی برای غیرفعال کردن بهینه‌سازی کد باشد، آن قانون بهینه‌سازی‌ها را برای کل پروژه شما غیرفعال می‌کند.

شما باید از کتابخانه‌هایی که دارای قوانین 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 روی Gson، به قوانین مصرف‌کننده Gson مراجعه کنید. وقتی R8 این کد را تجزیه و تحلیل می‌کند و UserList یا User نمونه‌سازی‌شده را در هیچ کجا نمی‌بیند، می‌تواند فیلدها را تغییر نام دهد یا سازنده‌هایی را که به نظر نمی‌رسد استفاده شوند حذف کند و باعث خرابی برنامه شما شود. اگر از کتابخانه‌های دیگری به روش‌های مشابه استفاده می‌کنید، باید بررسی کنید که آنها در بهینه‌سازی برنامه اختلال ایجاد نمی‌کنند و اگر این کار را می‌کنند، از آنها اجتناب کنید.

برای تعریف کلاس‌ها به شیوه‌ای سازگار با قوانین مصرف‌کننده‌ی Gson، از قطعه کد زیر به عنوان مرجع استفاده کنید:

class User(@com.google.gson.annotations.SerializedName("name") val name: String)
class UserList(@com.google.gson.annotations.SerializedName("users") val users: List<User>)

توجه داشته باشید که Room ، Hilt و Moshi با codegen انواع تعریف‌شده توسط برنامه را می‌سازند، اما از codegen برای جلوگیری از نیاز به reflection استفاده می‌کنند.