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

برای فعال کردن بهینه‌سازی برنامه، باید از کتابخانه‌هایی استفاده کنید که با بهینه‌سازی Android سازگار باشند. اگر کتابخانه‌ای برای بهینه‌سازی Android پیکربندی نشده باشد—برای مثال، اگر از انعکاس بدون بسته‌بندی قوانین نگهداری مرتبط استفاده کند—ممکن است برای برنامه Android مناسب نباشد. این صفحه توضیح می‌دهد که چرا برخی کتابخانه‌ها برای بهینه‌سازی برنامه مناسب‌تر هستند و نکاتی کلی برای کمک به شما در انتخاب ارائه می‌دهد.

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

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

ترجیح دادن codegen به reflection

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

برای کسب اطلاعات بیشتر درباره codegen درمقابل 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 استفاده می‌کنند. وقتی از بازتاب استفاده می‌کنند، حداقل قوانین نگهداری را ارائه می‌دهند تا فقط کد موردنیاز را نگه دارند.
  • کتابخانه‌های سریال‌سازی اغلب از بازتاب برای اجتناب از کد متن استاندارد هنگام نمونه‌سازی یا سریال‌سازی اشیاء استفاده می‌کنند. به‌جای رویکردهای مبتنی بر بازتاب (مانند Gson برای JSON)، به‌دنبال کتابخانه‌هایی باشید که از codegen برای جلوگیری از این مشکلات استفاده می‌کنند، برای مثال بااستفاده از Kotlin Serialization یا Moshi با codegen.
  • درصورت امکان، از کتابخانه‌هایی که شامل قوانین نگهداری در سطح بسته هستند اجتناب کنید. قوانین نگهداری در سطح بسته می‌تواند به رفع خطاها کمک کند، اما قوانین نگهداری گسترده باید درنهایت اصلاح شوند تا فقط کدهای موردنیاز حفظ شوند. برای اطلاعات بیشتر، بهینه‌سازی‌های افزایشی را اتخاذ کنید.
  • قبل‌از انتشار برنامه‌ای که از کتابخانه طرف سوم استفاده می‌کند، از R8 Configuration Analyzer برای ممیزی قوانین نگهداری ارائه‌شده آن استفاده کنید. با مرور گزارش، می‌توانید بررسی کنید که آیا قوانین حفظ کتابخانه بیش‌ازحد گسترده است یا نه، و از انجام بهینه‌سازی‌های حیاتی R8 در پایگاه کدتان جلوگیری کنید. این بررسی تضمین می‌کند که کتابخانه‌هایی که انتخاب می‌کنید با اهداف عملکردی برنامه‌تان مطابقت داشته باشند و باعث ایجاد پیکربندی‌های غیرضروری و سنگین نشوند.
  • Libraries shouldn't require you to copy and paste keep rules from documentation into a file in your project, especially not package-wide keep rules. این قوانین در بلندمدت به بار نگهداری برای توسعه‌دهنده برنامه تبدیل می‌شوند و بهینه‌سازی و تغییر آن‌ها درطول زمان دشوار است.

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

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

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

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

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

// 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")
        }
    }
}

گردش کار توصیه‌شده برای قوانین گسترده کتابخانه

اگر تحلیل‌گر پیکربندی R8 نشان دهد که کتابخانه‌ای قوانین نگهداری بسیار گسترده‌ای را بسته‌بندی می‌کند، آن قوانین را فقط در ساخت‌های محلی فیلتر کنید، نه در ساخت‌های تولید. برای آزمایش اینکه این قوانین چقدر بر برنامه‌تان تأثیر می‌گذارد، این مراحل را دنبال کنید:

  1. به‌روزرسانی کتابخانه: مطمئن شوید از جدیدترین نسخه کتابخانه‌ای که «تحلیل‌گر پیکربندی R8» پرچم‌گذاری کرده است استفاده می‌کنید، زیرا نسخه‌های جدیدتر ممکن است قوانین نگهداری مصرف‌کننده محدودتری را ارائه دهند. برای پیدا کردن جدیدترین نسخه، اسناد کتابخانه یا مخزن Maven آن را بررسی کنید. «استودیو Android» همچنین نسخه‌های جدیدتر بسیاری از وابستگی‌ها را پرچم‌گذاری می‌کند. پس‌از به‌روزرسانی، R8 Configuration Analyzer را دوباره اجرا کنید تا بررسی کنید آیا قوانین نگهداری گسترده باقی مانده‌اند یا نه.
  2. بهبود احتمالی را به‌صورت محلی بررسی کنید: قوانین مصرف‌کننده بسته‌بندی‌شده کتابخانه را در فایل قوانین نگهداری محلی خود کپی کنید، قوانین بسته‌بندی‌شده را همان‌طور که در فیلتر کردن قوانین نگهداری بد نشان داده شده است فیلتر کنید و قوانین کپی‌شده را محدود کنید. «تحلیل‌گر پیکربندی» R8 را قبل و بعداز محدود کردن قوانین اجرا کنید تا امتیازهای کوچک‌سازی، بهینه‌سازی، و مبهم‌سازی برنامه‌تان را مقایسه کنید.
  3. یافته‌ها را گزارش کنید: گزارش اشکالی را با نگهدارنده کتابخانه حاوی داده‌های تجربی از گزارش «تحلیل‌گر پیکربندی R8» خود (برای مثال، نشان دادن تعداد مشخصی از کلاس‌ها یا روش‌های بدون ارجاع که توسط قانون نویسه عام آن‌ها حفظ شده است) ثبت کنید.

مطالعه موردی: چرا Gson با بهینه‌سازی‌ها خراب می‌شود

Gson کتابخانه سریال‌سازی است که اغلب به‌دلیل استفاده زیاد از بازتاب، مشکلاتی در بهینه‌سازی برنامه ایجاد می‌کند. تکه‌کد زیر نشان می‌دهد که Gson معمولاً چگونه استفاده می‌شود، که می‌تواند باعث ازکارافتادن در زمان اجرا شود. توجه داشته باشید که وقتی از Gson برای دریافت فهرستی از اشیاء User استفاده می‌کنید، سازنده را فراخوانی نمی‌کنید یا کارخانه‌ای را به تابع fromJson() ارسال نمی‌کنید. ساختن یا مصرف کردن کلاس‌های تعریف‌شده توسط برنامه بدون هیچ‌یک از موارد زیر نشانه‌ای است از اینکه کتابخانه ممکن است از بازتاب نامحدود استفاده کند:

  • کلاس برنامه که کتابخانه، یا میانای استاندارد، یا کلاس را پیاده‌سازی می‌کند
  • افزایه تولید کد مثل نکته کلیدی فروش
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 در جایی نمونه‌سازی شده باشد، می‌تواند نام فیلدها را تغییر دهد یا سازنده‌هایی را که به‌نظر می‌رسد استفاده نمی‌شوند بردارد و باعث ازکارافتادن برنامه‌تان شود. اگر از کتابخانه‌های دیگری به روش‌های مشابه استفاده می‌کنید، باید بررسی کنید که با بهینه‌سازی برنامه تداخل نداشته باشند و اگر تداخل دارند، از آن‌ها استفاده نکنید.

استفاده از @SerializedName با نسخه‌های بعدی Gson

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

import com.google.gson.annotations.SerializedName

class User(@SerializedName("name") val name: String)
class UserList(@SerializedName("users") val users: List<User>)

بااستفاده از حاشیه‌نویسی @SerializedName، به R8 اجازه می‌دهید کلاس‌های مدل شما را با قوانین نگهداری موجود در Gson نسخه ۲.۱۱.۰ و بالاتر مطابقت دهد. R8 به‌طور خودکار فیلدهای حاشیه‌نویسی‌شده و سازنده‌های ضروری را حفظ می‌کند، و به شما امکان می‌دهد بدون نیاز به حفظ پیکربندی‌های دستی ProGuard در پروژه‌تان، به قوانین بسته‌بندی‌شده کتابخانه تکیه کنید.

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