R8 vs ProGuard در اندروید؛ تفاوت چیست و کدام را انتخاب کنیم؟

در سالهای اخیر، R8 به ابزار پیشفرض Android برای Code Shrinking و Obfuscation در Release Buildها تبدیل شده است
اگر در توسعه اپلیکیشنهای Android فعالیت میکنید، احتمالاً با نامهای R8 و ProGuard مواجه شدهاید. هر دو ابزار در زمینه کوچکسازی، بهینهسازی و Obfuscation کدهای Android استفاده میشوند، اما تفاوتهایی در نحوه عملکرد و جایگاه آنها در فرآیند Build وجود دارد.
در سالهای اخیر، R8 به ابزار پیشفرض Android برای Code Shrinking و Obfuscation در Release Buildها تبدیل شده است و در بسیاری از پروژههای جدید دیگر نیازی به استفاده مستقیم از ProGuard وجود ندارد.
در این مقاله از ناوین، R8 و ProGuard را بررسی میکنیم و میبینیم چه تفاوتی با یکدیگر دارند، چه زمانی باید از آنها استفاده کنیم و چگونه تنظیمات آنها را در یک پروژه Android مدیریت کنیم.
R8 چیست؟
R8 ابزاری است که در فرآیند Build اپلیکیشن Android برای کاهش حجم برنامه، حذف کدهای غیرضروری، بهینهسازی و Obfuscation استفاده میشود.
بهصورت ساده:
Source Code
↓
Compile
↓
R8
↓
Shrink
Optimize
Obfuscate
↓APK / AAB
R8 میتواند بخشهایی از کد را که در اپلیکیشن استفاده نمیشوند شناسایی و حذف کند.
همچنین نام کلاسها و متدها را میتواند تغییر دهد تا Reverse Engineering دشوارتر شود.
ProGuard چیست؟
ProGuard یکی از ابزارهای قدیمی و شناختهشده در اکوسیستم Java و Android برای:
- Shrinking
- Optimization
- Obfuscation
- حذف کدهای غیرضروری
است.
ProGuard قبل از ظهور R8 برای بسیاری از پروژههای Android مورد استفاده قرار میگرفت.
برای مثال ممکن است در پروژههای قدیمی فایلهایی مانند:
proguard-rules.pro
را مشاهده کنید.
نکته مهم این است که وجود نام proguard-rules.pro به این معنی نیست که حتماً ProGuard در حال اجرا است؛ پروژههای Android امروزی ممکن است همین فایل را برای Ruleهای R8 نیز استفاده کنند.
تفاوت اصلی R8 و ProGuard
تفاوت اصلی این است که R8 ابزار مدرنتری است که برای Build اپلیکیشنهای Android طراحی شده و میتواند عملیات Shrinking و Optimization را به شکل یکپارچهتر در فرآیند Build انجام دهد.
مقایسه کلی:
ویژگیR8ProGuard
Shrinking
✅
✅
Obfuscation
✅
✅
Optimization
✅
✅
مناسب برای پروژههای جدید Android
✅
معمولاً خیر
یکپارچه با Android Build
✅
قدیمیتر
Performance در Build
معمولاً بهتر
معمولاً کندتر
ابزار پیشفرض مدرن Android
✅
❌
چرا R8 جایگزین ProGuard شد؟
یکی از دلایل اصلی توسعه R8 این بود که فرآیند کوچکسازی و بهینهسازی کد برای Android به شکل بهتری با Build System و ساختار اپلیکیشنهای مدرن هماهنگ شود.
R8 در فرآیند Build میتواند مراحل مختلف را با یکدیگر ترکیب کند.
در یک نگاه:
ProGuard:
Bytecode
↓
ProGuard
↓Processed Bytecode
در مقابل:
R8:
Bytecode
↓
R8
↓
Shrink
Optimize
Obfuscate
↓DEX
این یک تصویر سادهشده از فرآیند است، اما برای درک تفاوت کلی مناسب است.
R8 چه کارهایی انجام میدهد؟
R8 را میتوان مجموعهای از قابلیتهای مهم برای Release Build در نظر گرفت.
1. Code Shrinking
کدهایی که در برنامه مورد استفاده قرار نمیگیرند میتوانند حذف شوند.
فرض کنید یک کلاس دارید:
class OldFeature { fun unusedFunction() { // ...
}}
اگر این کد هیچ مسیری در برنامه نداشته باشد که به آن نیاز داشته باشد، R8 میتواند آن را حذف کند.
نتیجه:
Unused Code
↓
Removed
↓Smaller App
2. Obfuscation
R8 میتواند نام کلاسها، متدها و فیلدهای مختلف را تغییر دهد.
مثلاً کد:
class PaymentManager { fun processPayment() { // ...
}}
ممکن است در خروجی Obfuscated به چیزی شبیه:
a.b()
تبدیل شود.
هدف این کار، سختتر کردن تحلیل و Reverse Engineering کد است.
البته Obfuscation به معنی غیرقابل نفوذ شدن اپلیکیشن نیست.
3. Optimization
R8 میتواند برخی قسمتهای Bytecode را بهینه کند.
هدف این است که کد خروجی تا حد امکان بهینهتر باشد.
بهعنوان مثال، برخی کدهای غیرضروری میتوانند حذف یا بعضی عملیات قابل بهینهسازی شوند.
R8 و کاهش حجم APK
یکی از مهمترین مزایای فعال کردن Code Shrinking، کاهش حجم خروجی برنامه است.
فرض کنید بدون Shrinking:
App Size = 50 MB
باشد.
پس از حذف کدها و Resourceهای غیرضروری ممکن است حجم خروجی کاهش پیدا کند.
البته مقدار کاهش حجم کاملاً به پروژه بستگی دارد و نمیتوان یک درصد ثابت برای همه اپلیکیشنها تعیین کرد.
R8 و امنیت اپلیکیشن
یکی از دلایلی که توسعهدهندگان R8 را فعال میکنند، سختتر کردن Reverse Engineering است.
بدون Obfuscation ممکن است ساختار کلاسها و نام متدها واضحتر باشند.
با Obfuscation:
PaymentService
UserRepositoryAuthenticationManager
میتوانند به نامهایی کوتاه و غیرمعنادار تبدیل شوند.
اما باید توجه داشت:
R8 یک ابزار امنیتی کامل نیست.
اگر اپلیکیشن اطلاعات حساس، API Key یا Secret داشته باشد، نباید صرفاً به Obfuscation اعتماد کرد.
R8 چگونه در پروژه Android فعال میشود؟
در پروژههای Android معمولاً Code Shrinking را برای Release Build فعال میکنیم.
در فایل Gradle مربوط به App میتوان تنظیماتی شبیه زیر داشت:
android { buildTypes { release { isMinifyEnabled = true isShrinkResources = true
}
}}
در برخی پروژهها نامگذاری یا ساختار فایل Gradle ممکن است با توجه به نسخه Android Gradle Plugin متفاوت باشد.
isMinifyEnabled برای فعال کردن فرآیند کوچکسازی و بهینهسازی کد استفاده میشود.
isShrinkResources نیز برای حذف Resourceهای غیرضروری کاربرد دارد.
proguard-rules.pro چیست؟
ممکن است در پروژه فایل زیر را ببینید:
proguard-rules.pro
این فایل برای تعریف Ruleهایی استفاده میشود که به ابزار Shrinker میگویند چه چیزهایی باید حفظ شوند یا چگونه پردازش شوند.
در پروژههای مدرن، این Ruleها معمولاً توسط R8 پردازش میشوند.
مثلاً:
-keep class com.example.models.** { *; }
یعنی کلاسهای مشخصشده را از حذف یا Obfuscation حفظ کنید.
چرا به Keep Rule نیاز داریم؟
R8 باید بتواند تشخیص دهد کدام کد واقعاً استفاده میشود.
اما همیشه تمام Dependencyها و Frameworkها به شکل مستقیم از یک کلاس استفاده نمیکنند.
مثلاً ممکن است یک Framework با استفاده از:
- Reflection
- Serialization
- Dependency Injection
- JNI
- Dynamic Loading
به کلاس خاصی دسترسی داشته باشد.
در این شرایط ممکن است R8 تصور کند کلاس استفاده نمیشود.
در نتیجه:
Runtime Dependency
↓
R8 doesn't detect it
↓
Class removed/renamed
↓Runtime Error
برای جلوگیری از این مشکل ممکن است به Keep Rule نیاز داشته باشیم.
یک مثال ساده از Keep Rule
فرض کنید کلاسهایی دارید که توسط Reflection استفاده میشوند:
data class User( val id: Int, val name: String)
ممکن است بسته به کتابخانه و نحوه استفاده، لازم باشد برخی کلاسها یا اعضا را از Shrinking یا Obfuscation مستثنی کنید.
برای نمونه:
-keep class com.example.model.** { *; }
اما نباید بدون دلیل از Ruleهای بسیار گسترده استفاده کنید.
آیا باید همه چیز را Keep کنیم؟
خیر.
یکی از اشتباهات رایج این است که توسعهدهنده برای برطرف کردن یک Crash، Rule بسیار گستردهای اضافه کند:
-keep class ** { *; }
این کار میتواند بخش بزرگی از مزایای R8 را از بین ببرد.
بهتر است Ruleها تا حد امکان دقیق باشند:
Bad:
Keep Everything
Better:Keep Required Classes
R8 و Third-Party Libraries
یکی از چالشهای رایج، استفاده از Libraryهای مختلف است.
مثلاً اپلیکیشن ممکن است از:
Retrofit
Gson
Room
FirebaseHilt
یا کتابخانههای دیگر استفاده کند.
بعضی Libraryها Ruleهای موردنیاز خود را همراه با Library ارائه میکنند.
به همین دلیل قبل از اضافه کردن Keep Rule دستی، ابتدا Documentation و Configuration خود Library را بررسی کنید.
R8 و Reflection
Reflection یکی از مواردی است که میتواند کار Shrinker را پیچیده کند.
فرض کنید کدی دارید که کلاس را با نام آن پیدا میکند:
Class.forName("com.example.User")
R8 ممکن است نتواند بهسادگی تشخیص دهد که کلاس User از طریق Reflection استفاده میشود.
در چنین شرایطی ممکن است به Rule مناسب نیاز داشته باشید.
R8 و Serialization
Serialization نیز میتواند نیازمند توجه ویژه باشد.
برای مثال اگر از JSON Mapping استفاده میکنید، باید مطمئن شوید که کلاسها و اطلاعات موردنیاز در Release Build همچنان قابل دسترسی هستند.
این موضوع به Library مورد استفاده بستگی دارد.
بنابراین همیشه قبل از اضافه کردن Ruleهای عمومی، Configuration رسمی کتابخانه را بررسی کنید.
R8 و Debug Build
در بیشتر پروژهها Shrinking و Obfuscation را برای Debug Build فعال نمیکنند.
چرا؟
چون Debugging کد Obfuscated بسیار دشوارتر است.
معمولاً:
Debug
↓
Readable Code
↓Easy Debugging
و:
Release
↓
R8
↓Shrinking + Obfuscation
استفاده میشود.
R8 و Release Build
Release Build جایی است که R8 اهمیت بیشتری پیدا میکند.
یک فرآیند معمول:
Developer Code
↓
Gradle Build
↓
R8
↓
Shrink
↓
Optimize
↓
Obfuscate
↓AAB
در نهایت فایل Release برای انتشار آماده میشود.
Mapping File چیست؟
هنگامی که Obfuscation فعال باشد، نامهای اصلی کلاسها و متدها تغییر میکنند.
برای مثال:
PaymentManager
↓a.b
اگر Crash در Production رخ دهد، بدون اطلاعات Mapping تحلیل Stack Trace بسیار دشوارتر خواهد بود.
R8 یک Mapping File تولید میکند که ارتباط بین نامهای اصلی و نامهای Obfuscated را مشخص میکند.
بهصورت مفهومی:
Original Obfuscated
PaymentManager → a
processPayment → bUserRepository → c
چرا باید Mapping File را نگه داریم؟
فرض کنید کاربر نسخه Release را نصب کرده و Crash رخ داده است.
Stack Trace ممکن است شامل نامهایی مانند:
a.b()c.d()
باشد.
با Mapping File میتوان این اطلاعات را به نامهای اصلی تبدیل کرد.
بنابراین برای Releaseهای واقعی باید Mapping Fileها را با دقت نگهداری و مدیریت کنید.
R8 و Resource Shrinking
R8 عمدتاً روی کد تمرکز دارد، اما Android Build System میتواند Resourceهای استفادهنشده را نیز حذف کند.
با فعال کردن:
isShrinkResources = true
ممکن است Resourceهایی مانند:
Unused Images
Unused XMLUnused Resources
حذف شوند.
این قابلیت میتواند در کاهش حجم نهایی App مفید باشد.
R8 و App Bundle
اگر اپلیکیشن را برای Google Play منتشر میکنید، معمولاً خروجی Android App Bundle یا AAB اهمیت زیادی دارد.
ساختار کلی:
Source
↓
Gradle
↓
R8
↓
AAB
↓
Google Play
↓Optimized APK
Google Play میتواند بر اساس دستگاه کاربر، APK مناسب را از App Bundle تولید و ارائه کند.
آیا R8 بهتر از ProGuard است؟
برای پروژههای مدرن Android، R8 معمولاً انتخاب مناسبتری است.
دلیل آن:
- یکپارچگی بهتر با Android Build
- مناسب بودن برای Android مدرن
- Shrinking
- Optimization
- Obfuscation
- Workflow مدرنتر
بنابراین اگر پروژه جدیدی ایجاد میکنید، معمولاً دلیلی برای انتخاب ProGuard بهعنوان ابزار اصلی به جای R8 ندارید.
آیا هنوز باید ProGuard را یاد بگیریم؟
تا حدی بله.
اگر با پروژههای قدیمی Android کار میکنید، احتمالاً با اصطلاحات و Ruleهای ProGuard مواجه خواهید شد.
همچنین Syntax بسیاری از Configuration Ruleها همچنان با نام ProGuard شناخته میشود و فایلهایی مانند:
proguard-rules.pro
در پروژههای R8 نیز استفاده میشوند.
بنابراین بهتر است تفاوت بین:
ProGuard Tool
و:
ProGuard Configuration Rules
را درک کنید.
مقایسه R8 و ProGuard از دید یک Android Developer
اگر بخواهیم خیلی ساده نگاه کنیم:
ProGuard
↓
Legacy / Older Tool
R8
↓Modern Android Shrinker
در پروژههای جدید:
Android App
↓
R8
↓Release Build
معمولاً مسیر اصلی است.
اشتباهات رایج در استفاده از R8
۱. اضافه کردن Keep Rule برای همه کلاسها
این کار میتواند Shrinking و Obfuscation را تا حد زیادی بیاثر کند.
۲. تست نکردن Release Build
ممکن است Debug Build کاملاً سالم باشد اما Release Build بعد از R8 Crash کند.
بنابراین Release Variant را حتماً تست کنید.
۳. حذف Mapping File
اگر Obfuscation فعال است، Mapping File برای تحلیل Crashهای Production اهمیت زیادی دارد.
۴. قرار دادن Secret در اپلیکیشن
R8 نباید بهعنوان راهی برای مخفی کردن API Secret یا Password استفاده شود.
۵. نادیده گرفتن Reflection
کتابخانههایی که از Reflection یا روشهای Dynamic استفاده میکنند ممکن است به Configuration خاص نیاز داشته باشند.
۶. استفاده از Ruleهای قدیمی بدون بررسی
گاهی پروژههای قدیمی تعداد زیادی Rule دارند که دیگر ضروری نیستند.
بهتر است Configurationها را دورهای بررسی کنید.
بهترین روش استفاده از R8
برای استفاده صحیح از R8 میتوانید این رویکرد را دنبال کنید:
۱. ابتدا R8 را برای Release فعال کنید
isMinifyEnabled = true
۲. اپلیکیشن را کامل تست کنید
بهخصوص:
- Login
- API
- Database
- Serialization
- Push Notification
- Deep Link
- Payment
- Reflection
۳. اگر Crash ایجاد شد، علت را پیدا کنید
فوراً Rule عمومی اضافه نکنید.
۴. Rule دقیق اضافه کنید
فقط کلاسها یا اعضایی را که واقعاً نیاز هستند Keep کنید.
۵. Mapping File را نگه دارید
برای Debugging نسخه Production ضروری است.
R8 برای چه پروژههایی مناسب است؟
تقریباً هر Android Application که قرار است بهصورت Release منتشر شود میتواند از مزایای R8 استفاده کند.
بهخصوص برای:
- اپلیکیشنهای تجاری
- اپلیکیشنهای بزرگ
- اپلیکیشنهای دارای Dependencyهای متعدد
- اپلیکیشنهای منتشرشده در Store
- پروژههایی که حجم APK/AAB اهمیت دارد
R8 در یک نگاه
میتوان عملکرد R8 را اینگونه خلاصه کرد:
R8
│
┌─────────┼─────────┐
↓ ↓ ↓
Shrink Optimize Obfuscate
│ │ │
└─────────┼─────────┘
↓ Release Build
یعنی R8 میتواند:
کدهای غیرضروری را حذف کند + کد را بهینه کند + نامها را Obfuscate کند.
نتیجهگیری
R8 و ProGuard هر دو در زمینه کوچکسازی و Obfuscation کد Android شناخته شدهاند، اما در پروژههای مدرن Android، R8 ابزار اصلی و مدرن برای این فرآیند است.
R8 میتواند در Release Build برای کاهش حجم برنامه، حذف کدهای غیرضروری، بهینهسازی و Obfuscation مورد استفاده قرار گیرد.
در عین حال، استفاده از R8 نیازمند دقت است. اگر اپلیکیشن از Reflection، Serialization، Dependency Injection یا برخی روشهای Dynamic استفاده کند، ممکن است لازم باشد Ruleهای مخصوصی تعریف شود.
بهترین رویکرد این است که R8 را برای Release فعال کنید، نسخه Release را بهطور کامل تست کنید، Keep Ruleها را تا حد امکان محدود نگه دارید و Mapping File هر Release را بهصورت امن ذخیره کنید.
در نهایت باید به خاطر داشته باشیم که R8 یک ابزار امنیتی کامل نیست؛ بلکه یکی از لایههای محافظتی و بهینهسازی اپلیکیشن است. اطلاعات حساس، Secretها و منطقهای امنیتی مهم نباید صرفاً با Obfuscation محافظت شوند.
سوالات متداول
R8 چیست؟
R8 ابزار مدرن Android برای Code Shrinking، Optimization و Obfuscation است که در فرآیند Build اپلیکیشن استفاده میشود.
آیا R8 همان ProGuard است؟
خیر. R8 ابزار متفاوتی است، اما از بسیاری از Syntaxها و Ruleهای سازگار با ProGuard استفاده میکند. به همین دلیل فایلهایی مانند proguard-rules.pro در پروژههای R8 نیز رایج هستند.
آیا باید R8 را در Debug فعال کنیم؟
معمولاً خیر. فعال بودن Obfuscation در Debug میتواند فرآیند Debugging را دشوار کند. معمولاً R8 برای Release Build استفاده میشود.
آیا R8 امنیت اپلیکیشن را تضمین میکند؟
خیر. R8 میتواند Reverse Engineering را دشوارتر کند، اما نمیتواند بهتنهایی امنیت کامل اپلیکیشن را تضمین کند.
چرا بعد از فعال کردن R8 اپلیکیشن Crash میکند؟
یکی از دلایل رایج، حذف یا تغییر نام کلاسی است که در Runtime از طریق Reflection، Serialization یا روشهای Dynamic مورد استفاده قرار میگیرد. در چنین شرایطی ممکن است به Keep Rule مناسب نیاز داشته باشید.
فایل Mapping در R8 چه کاربردی دارد؟
Mapping File ارتباط بین نامهای اصلی و نامهای Obfuscated را نگه میدارد و برای خواندن و تحلیل Stack Traceهای Obfuscated و Crashهای Release بسیار مهم است.
آیا ProGuard هنوز در Android استفاده میشود؟
ProGuard همچنان بهعنوان یک ابزار قدیمی شناخته میشود و Configuration Syntax آن در اکوسیستم Android اهمیت دارد، اما برای پروژههای مدرن معمولاً R8 انتخاب اصلی است.
آیا R8 حجم اپلیکیشن را کاهش میدهد؟
بله، R8 میتواند کدهای استفادهنشده را حذف کند و همراه با Resource Shrinking میتواند به کاهش حجم خروجی Release کمک کند؛ مقدار کاهش به ساختار و Dependencyهای هر پروژه بستگی دارد.
نظرات و بازخورد
هنوز نظری تأیید نشده است. اولین نفر باشید.



