در دنیای رقابتی توسعه اپلیکیشن های موبایل، حجم فایل نصبی (APK یا AAB) و امنیت کدهای منبع دو فاکتور حیاتی در موفقیت تجاری محصول هستند. بر اساس آمارهای رسمی گوگل، به ازای هر ۶ مگابایت افزایش حجم برنامه، نرخ نصب موفق در گوگل پلی حدود ۱ درصد کاهش می یابد.
علاوه بر حجم، کدهای کامپایل شده زبان های کاتلین و جاوا بدون محافظت به راحتی با ابزار های دیکامپایلر به سورس کد اولیه تبدیل می شوند. این موضوع منطق تجاری برنامه، کلیدهای ارتباطی و الگوریتم های محرمانه را در معرض دید هکرها قرار می دهد.
برای حل این دو چالش بنیادین، شرکت گوگل کامپایلر پیشرفته R8 را به عنوان ابزار پیش فرض فرآیند ساخت (Build Process) معرفی کرده است.
کامپایلر R۸ وظیفه کاهش حجم کدها (Shrinking)، بهینه سازی دستورات (Optimization)، مبهم سازی نام کلاس ها و توابع (Obfuscation) و تولید فایل اجرایی دالویک (Dexing) را بر عهده دارد.
گزارش های بنچمارک مهندسی نشان می دهد که فعال سازی دقیق کامپایلر R۸ به همراه قوانین استاندارد ProGuard حجم فایل نصبی را بین ۳۵ تا ۶۵ درصد کاهش می دهد.
همچنین زمان اجرای اولیه اپلیکیشن (Cold Startup Time) را تا ۲۲ درصد بهبود بخشیده و نفوذناپذیری کد در برابر مهندسی معکوس را به شدت ارتقا می بخشد.
در این راهنمای جامع و مرجع تخصصی، معماری داخلی کامپایلر R۸، تفاوت آن با ProGuard سنتی، سینتکس نگارش قوانین Keep، رفع خطاهای رایج زمان اجرا و استراتژی های تست بیلد نهایی در اندروید را به طور کامل تحلیل و پیاده سازی می کنیم.
۱. چرا کاهش حجم و مبهم سازی کدها در انتشار اپلیکیشن اندروید حیاتی است؟
هنگامی که شما کتابخانه های متعددی مانند Retrofit، OkHttp، Room، Coil و ماژول های فریمورک کامپوز را به پروژه اضافه می کنید، هزاران کلاس و متد آماده وارد پروژه می شوند.
در عمل ممکن است اپلیکیشن شما تنها به ۱۰ درصد از قابلیت های آن کتابخانه ها نیاز داشته باشد. بدون استفاده از ابزار های پاکسازی کد، تمام متدهای دست نخورده درون فایل نهایی قرار می گیرند.
این موضوع نه تنها حجم پکیج نصبی را متورم می کند، بلکه پروژه را به سقف ۶۴ هزار متد اجرایی (۶۴K DEX Method Reference Limit) نزدیک می سازد که نیازمند فعال سازی Multidex خواهد بود.
از سوی دیگر، مسأله امنیت و حفظ مالکیت فکری مطرح است. فایل های APK بدون مبهم سازی شبیه به کتابی باز هستند که هر پژوهشگر امنیتی یا رقیب تجاری می تواند معماری، الگو های شبکه و آسیب پذیری های منطقی آن را در ابزار JADX مشاهده کند.
برای تسلط بر چرخه حیات و ساختار کامپوننت های نرم افزاری در اکوسیستم گوگل، مطالعه راهنمای کامل آموزش برنامه نویسی اندروید به عنوان پیش نیاز توصیه می گردد.
۲. تفاوت بنیادین میان ProGuard سنتی و کامپایلر مدرن R۸ در چرخه ساخت
در نسخه های قدیمی ابزار های اندروید استودیو، خط لوله ساخت (Build Pipeline) از چند مرحله مجزا و سنگین تشکیل شده بود. ابتدا کدهای جاوا و کاتلین به بایت کد جاوایی (Java Bytecode با فرمت .class) تبدیل می شدند.
سپس ابزار ProGuard کدهای کلاس را خوانده، بهینه سازی و مبهم سازی را انجام می داد و یک خروجی بایت کد جاوایی دیگر تولید می کرد. در پایان، کامپایلر D8 یا DX آن بایت کدها را به فرمت اجرایی اندروید یعنی classes.dex تبدیل می نمود.
این ساختار چندمرحله ای باعث مصرف بالای حافظه رم و افزایش شدید زمان بیلد می شد. علاوه بر این، پروگارد درک عمیقی از ساختار خاص ماشین مجازی دالویک (ART/DVM) نداشت و بسیاری از بهینه سازی های ویژه رجیسترهای دالویک از دست می رفت.
شرکت گوگل کامپایلر R8 را توسعه داد تا تمام این مراحل را در یک گذر پردازشی واحد (Single-pass Direct Dexing) ادغام کند. R۸ مستقیماً سورس بایت کد را دریافت کرده، هرس کدهای زائد و مبهم سازی را انجام می دهد و مستقیماً فایل DEX بهینه را صادر می نماید.
جهت درک شیوه های سازمان دهی ماژول های مستقل و جداسازی وابستگی ها، مطالعه راهنمای معماری تمیز در مهندسی نرم افزار پیشنهاد می شود.
۳. جدول مقایسه جامع: کامپایلر R۸ در برابر ابزار سنتی ProGuard
شناخت تفاوت های فنی میان این دو ابزار نشان می دهد چرا گوگل ابزار پروگارد را کاملاً بازنشسته کرده و R۸ را استاندارد صنعتی رسمی قرار داده است:
| معیار ارزیابی فنی | کامپایلر پیشرفته R۸ | ابزار سنتی ProGuard |
|---|---|---|
| معماری خط لوله کامپایل | تک مرحله ای با خروجی مستقیم DEX | چند مرحله ای (Java Bytecode به Dex با ابزار D۸) |
| سرعت فرآیند بیلد (Build Time) | تا ۳۰ الی ۴۰ درصد سریع تر | کند به دلیل تبادل فایل های میانی متعدد |
| درصد کاهش حجم نهایی APK | کاهش بالاتر (به دلیل شناخت ساختار رجیسترهای دالویک) | کاهش خوب اما محدودتر از R۸ |
| پشتیبانی از کاتلین و کامپوز | پشتیبانی محلی از ساختارهای کاتلین (مانند اینلاین فانکشن ها) | نیازمند پیکربندی دستی و قواعد پیچیده تر |
| سازگاری با قوانین موجود | پذیرش کامل فایل های کانفیگ proguard-rules.pro |
فرمت مرجع قواعد |
| حالت پیشرفته بهینه سازی | دارای دو مود Compatibility و Full Mode | حالت های بهینه سازی استاندارد |
نکته قابل توجه این است که کامپایلر R۸ با تمام سینتکس ها و قواعد موجود در فایل های پروگارد قبلی شما کاملاً سازگار است؛ بنابراین برای مهاجرت نیازی به بازنویسی قوانین پایه نخواهید داشت.
۴. فعال سازی R۸ در فایل Gradle با کاتلین DSL و تنظیم قوانین بیلد ریلیز
برای فعال کردن کامپایلر R۸ در پروژه اندروید، کافی است ویژگی های مربوطه را در بلاک buildTypes فایل build.gradle.kts ماژول اپلیکیشن پیکربندی نمایید:
android {
buildTypes {
release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
signingConfig = signingConfigs.getByName("release")
}
debug {
isMinifyEnabled = false
isShrinkResources = false
}
}
}
دستور isMinifyEnabled = true هسته اصلی کامپایلر R۸ را برای کاهش حجم و مبهم سازی کدها فعال می کند. بدون این دستور، R۸ تنها به عنوان یک مبدل ساده به فرمت DEX عمل خواهد کرد.
دستور isShrinkResources = true حذف کننده منابع بلااستفاده (مانند تصاویر، فایل های صوتی و XML های دست نخورده) را فعال می کند. این ویژگی الزماً نیازمند فعال بودن isMinifyEnabled است.
همچنین استفاده از فایل پیش فرض proguard-android-optimize.txt به جای نسخه معمولی، قوانین تهاجمی تر و مدرن تری از بهینه سازی را بر روی بایت کد اعمال می نماید.
۵. کالبدشکافی چهار فاز اصلی کامپایلر R۸: از هرس درخت تا تبدیل به DEX
کامپایلر R۸ برای آماده سازی بسته پروداکشن، کدهای شما را از چهار مرحله پالایش عبور می دهد. درک عمیق این مراحل برای خطایابی حیاتی است:
الف) حذف کدهای مرده (Tree Shaking)
در این فاز، کامپایلر از نقاط ورودی برنامه (مانند اکتیویتی ها، سرویس ها و برودکست رسیورهای اعلام شده در مانیفست) شروع می کند. سپس تمام گراف ارجاعات متدها را پیمایش می نماید. هر کلاسی که هیچ مسیری به آن نرسد، بی رحمانه از فایل نهایی حذف می شود.
ب) بهینه سازی بایت کد (Optimization)
در این فاز، R۸ تغییراتی در ساختار کد ایجاد می کند تا کارایی پردازنده افزایش یابد. متدهای تک خطی درون خطی (Inline) می شوند، کلاس های تک ارث بری با یکدیگر ادغام می گردند و شاخه های شرطی غیرقابل دسترس (Dead Branches) به طور کامل حذف می شوند.
ج) مبهم سازی کدها (Obfuscation)
نام طولانی کلاس ها، متدها و متغیرها به اسامی تک حرفی و کوتاه مانند a، b و c تغییر پیدا می کند. این کار دو مزیت دارد: خوانایی کد دیکامپایل شده را برای انسان ناممکن می سازد و حجم جدول رشته های فایل DEX را کاهش می دهد.
د) تبدیل مستقیم به بایت کد دالویک (Dexing)
در مرحله پایانی، تمام ساختارهای بهینه شده به طور مستقیم به فایل های classes.dex نوشته می شوند تا آماده بسته بندی در قالب AAB یا APK گردند.
۶. آموزش جامع سینتکس قوانین ProGuard: تفاوت keep، keepclassmembers و keepclasseswithmembers
مهم ترین مهارت یک مهندس اندروید در کار با R۸، دانستن نحوه نگارش قواعد نگه داری (Keep Rules) است. کامپایلر نمی داند چه کدهایی قرار است از طریق انعکاس (Reflection) یا توسط کتابخانه های خارجی فراخوانی شوند:
# نگه داری کامل کلاس و تمام اعضای آن
-keep class com.example.models.UserProfile { *; }
# نگه داری نام کلاس اما مجاز بودن مبهم سازی متدها
-keep class com.example.analytics.Tracker
# نگه داری فیلدهای خاص در صورتی که خود کلاس حفظ شود
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
!static !transient <fields>;
}
# نگه داری متدهایی که انوتیشن خاصی دارند
-keepclasseswithmembers class * {
@androidx.annotation.Keep <methods>;
}
قاعده -keep به کامپایلر دستور می دهد که نه کلاس را حذف کند و نه نام آن را تغییر دهد.
قاعده -keepclassmembers تنها اعضای درون کلاس (فیلدها یا متدها) را حفظ می کند، در حالی که نام خود کلاس ممکن است مبهم سازی شود.
قاعده -keepclasseswithmembers بسیار هوشمند است؛ تنها در صورتی کلاس و اعضایش را حفظ می کند که تمام شرایط مشخص شده (مثلاً داشتن فیلد یا متد خاص) برقرار باشد.
۷. پیکربندی قوانین برای مدل های داده و کتابخانه های سریالایزیشن (JSON)
رایج ترین باگی که برنامه نویسان پس از خروجی ریلیز با آن مواجه می شوند، کرش اپلیکیشن در زمان دریافت پاسخ های شبکه از سرور است. کتابخانه هایی مانند Gson، Moshi و Retrofit بر پایه انعکاس کار می کنند:
import com.google.gson.annotations.SerializedName
import androidx.annotation.Keep
@Keep
data class UserDto(
@SerializedName("user_id")
val id: String,
@SerializedName("display_name")
val name: String,
@SerializedName("email_address")
val email: String
)
اگر R۸ نام فیلدهای این کلاس را به a و b تغییر دهد، کتابخانه جیسون نمی تواند کلیدهای دریافتی از سرور را با فیلدهای کلاس مطابقت دهد و مقادیر null می شوند.
برای حل ساختاری این چالش بدون نیاز به گذاشتن انوتیشن روی تک تک کلاس ها، قاعده زیر را در فایل proguard-rules.pro درج می کنیم:
# حفظ نام تمام مدل های لایه دیتا در برابر مبهم سازی
-keepclassmembers class com.example.app.data.models.** {
<fields>;
}
# نگه داری انوتیشن های کتابخانه های سریالایزیشن
-keepattributes *Annotation*,Signature,InnerClasses,EnclosingMethod
همچنین جهت ذخیره سازی اصولی و بدون خطای این مدل ها در حافظه محلی دیسک، راهنمای جامع آموزش دیتابیس Room در اندروید را به عنوان مرجع مطالعه فرمایید.
۸. قواعد ضروری ProGuard در فریمورک Jetpack Compose و ناوبری
فریمورک مدرن کامپوز از پلاگین کامپایلر کاتلین برای بازنویسی کدهای رابط کاربری استفاده می کند. گرچه کتابخانه های کامپوز قوانین ProGuard پیش فرض خود را دارند، اما در نسخه های جدید باید به این نکات توجه شود:
# حفظ توابع Composable که به صورت پویا یا در تست ها فراخوانی می شوند
-keepclassmembers class * {
@androidx.compose.runtime.Composable *;
}
# جلوگیری از حذف ساختارهای وضعیت در زمان بازترکیب
-keep class androidx.compose.runtime.** { *; }
همچنین در صورتی که از ناوبری کامپوز بر پایه کلاس های سریالایزبل بهره می برید، باید مدل های مسیر و آرگومان های ناوبری را در برابر مبهم سازی حفظ کنید تا سریالایز کردن مسیرها در کاتلین با خطای زمان اجرا روبرو نشود.
برای درک شیوه اتصال کدهای اعلانی و ساختارهای وضعیت در محیط های تجاری، مقاله مرجع آموزش جامع Jetpack Compose را بررسی فرمایید.
۹. بهینه سازی کورتین های کاتلین و فلوها با کامپایلر R۸
کدهای همزمانی در کاتلین بر پایه ماشین وضعیت (State Machine) و توابع معلق (Suspend Functions) کامپایل می شوند. این ساختارها تعداد زیادی کلاس ناشناس و اینترفیس های میانی ایجاد می کنند:
# نگه داری متادیتا و تایپ های کورتین های کاتلین
-keepattributes EnclosingMethod,InnerClasses
-dontwarn kotlinx.coroutines.**
# جلوگیری از حذف کلاس های بازتابی سرویس لوکیتور کورتین
-keepclassmembers class kotlinx.coroutines.android.AndroidDispatcherFactory {
public <init>();
}
کامپایلر R۸ در بهینه سازی کورتین ها بسیار کارآمد است؛ متدهای تعلیق شده ساده را درون خطی می کند و اشیاء حالت اضافی را از بین می برد تا مصرف رم در زمان اجرای تسک های همزمان کاهش یابد.
برای آشنایی با ساختارهای پیشرفته مدیریت همروندی در کاتلین، مقاله اختصاصی مدیریت همزمانی در کاتلین با کورتین ها را در ساختار پروژه های خود اعمال نمایید.
۱۰. بهینه سازی منابع با Resource Shrinking و فیلتر کردن وکتورها
کدهای برنامه تنها بخشی از حجم پکیج را تشکیل می دهند؛ در بسیاری از پروژه ها تصاویر، فونت ها و فایل های صوتی بخش اعظم حجم را می بلعند.
با فعال کردن isShrinkResources = true، ابزار بسته بندی اندروید منابعی که در کدهای منبع به آنها ارجاعی وجود ندارد را شناسایی می کند.
منابع شناسایی شده به جای حذف فیزیکی (که ممکن است ساختار جدول منابع resources.arsc را بشکند)، با فایل های ساختگی ۱ بایتی جایگزین می شوند. این ترفند هوشمندانه بدون هیچ خطایی، حجم اشغال شده را صفر می کند.
اگر در کدهای خود نام منابع را به صورت رشته پویا فراخوانی می کنید (مانند resources.getIdentifier)، باید در فایلی به نام res/raw/keep.xml آن منابع را به عنوان منابع استثنا تعریف کنید:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@drawable/badge_*,@string/error_*"
tools:discard="@layout/unused_dialog_layout"
tools:shrinkMode="strict" />
تنظیم shrinkMode="strict" به ابزار دستور می دهد که محافظه کاری را کنار بگذارد و تنها منابعی را نگه دارد که دقیقاً در کدها ذکر شده اند.
۱۱. بهینه سازی در پروژه های چندماژولی با فایل consumer-rules.pro
در پروژه های مقیاس پذیر سازمانی که از معماری ماژولار و تفکیک لایه های دیتا، دامین و فیچر بهره می برند، متمرکز کردن تمام قوانین در ماژول اپلیکیشن خطایی راهبردی است.
گوگل برای ماژول های کتابخانه ای (Android Library Modules) مکانیزم consumer-rules.pro را تعبیه کرده است:
// build.gradle.kts در ماژول کتابخانه ای (:core:network یا :feature:auth)
android {
defaultConfig {
consumerProguardFiles("consumer-rules.pro")
}
}
قوانینی که در فایل consumer-rules.pro یک ماژول نوشته می شوند، به صورت خودکار همراه با فایل پکیج AAR به ماژول نهایی برنامه منتقل می گردند.
برای مثال، اگر در ماژول شبکه اینترفیس های Retrofit یا ماژول های تزریق وابستگی Hilt تعریف شده باشند، قوانین نگه داری متدهای آن ها مستقیماً درون همان ماژول قرار می گیرد. این رویکرد ساختاریافته مانع از سردرگمی توسعه دهندگان در پروژه های بزرگ با ده ها ماژول می شود و نگه داری بلندمدت را آسان می سازد.
با این رویکرد، ماژول برنامه اصلی نیازی به آگاهی از جزییات پیاده سازی داخلی کتابخانه های زیرین ندارد و اصل استقلال لایه ها در معماری تمیز به طور کامل رعایت می گردد. این الگو زمان کامپایل پروژه را نیز بهبود می بخشد.
۱۲. رمزگشایی و مانیتورینگ کرش های پروداکشن با فایل mapping.txt و ReTrace
هنگامی که کدها مبهم سازی می شوند، استک تریس (Stack Trace) خطاهای برنامه در کنسول گوگل پلی یا Firebase Crashlytics به صورت نامفهوم نمایش داده می شود:
java.lang.NullPointerException: Attempt to invoke virtual method 'void b.a.c.a()' on a null object reference
at com.example.app.ui.MainActivity.onCreate(MainActivity.java:42)
at a.b.d.run(Unknown Source:12)
برای تبدیل این اسامی رمزی به نام توابع و خطوط واقعی سورس کد، کامپایلر R۸ فایلی به نام mapping.txt تولید می کند. این فایل جدول تناظر میان نام های اصلی و مبهم شده را درون خود دارد.
محل ذخیره سازی این فایل پس از اتمام فرآیند بیلد ریلیز در مسیر زیر قرار دارد:
app/build/outputs/mapping/release/mapping.txt
شما می توانید این فایل را در کنسول گوگل پلی بارگذاری کنید تا خطاهای کاربران به طور خودکار به زبان اصلی ترجمه شوند. همچنین با ابزار خط فرمانی retrace در پوشه cmdline-tools اس دی کی اندروید می توانید خطاها را به صورت آفلاین رمزگشایی نمایید.
۱۳. حالت پیشرفته R۸ Full Mode: مزایا، ریسک ها و فعال سازی در gradle.properties
به صورت پیش فرض، کامپایلر R۸ در حالت سازگاری (Compatibility Mode) اجرا می شود تا خطاهای احتمالی ناشی از حذف قوانین پیش فرض کاهش یابد.
اما اگر به دنبال نهایت کاهش حجم هستید، می توانید حالت R8 Full Mode را فعال نمایید.
برای فعال کردن این قابلیت، دستور زیر را در فایل gradle.properties ریشه پروژه قرار دهید:
# فعال سازی حالت نهایت بهینه سازی کامپایلر R8
android.enableR8.fullMode=true
در حالت Full Mode، کامپایلر فرضیات سخت گیرانه تری در مورد رفتار انوتیشن ها و وراثت کلاس ها اتخاذ می کند. برای مثال، کلاس هایی که صرفاً پیاده سازی کننده یک اینترفیس هستند و رفتار متفاوتی ندارند با کلاس والد ادغام می شوند.
فعال سازی این حالت معمولاً ۵ الی ۱۰ درصد کاهش حجم مضاعف به ارمغان می آورد، اما نیازمند تست دقیق کلیه سناریوهای اپلیکیشن پیش از انتشار در مارکت ها است.
۱۴. کالبدشکافی ساختار پکیج با ابزار APK Analyzer در اندروید استودیو
پس از تولید فایل ریلیز، حتماً فایل APK یا AAB را با ابزار داخلی اندروید استودیو به نام APK Analyzer بررسی کنید. کافی است فایل را به داخل پنجره ادیتور درگ کنید.
این ابزار نموداری دقیق از سهم فایل های classes.dex، پوشه res، فونت ها و کتابخانه های نیتیو (C++ .so) را نمایش می دهد. شما می توانید تعداد کل متدها و درصد کاهش حجم هر پکیج را به تفکیک مشاهده نمایید.
بررسی این اطلاعات به شما نشان می دهد که کدام کتابخانه بیشترین سهم را در متورم ساختن برنامه دارد و آیا قوانین پروگارد شما به درستی عمل کرده اند یا خیر.
۱۵. آزمون های خودکار بیلد ریلیز و ارزیابی عدم کرش در پایپ لاین CI/CD
یکی از اشتباهات استراتژیک در تیم های نرم افزاری، اجرای تست های واحد و ابزاری صرفاً بر روی نسخه دیباگ (Debug Build) است.
نسخه ریلیز به دلیل مبهم سازی و حذف کدهای مرده، رفتاری کاملاً متفاوت از خود نشان می دهد. بنابراین تست بیلد ریلیز در پایپ لاین الزامی است.
import org.junit.Test
import kotlin.test.assertNotNull
class ReleaseIntegrityTest {
@Test
fun jsonParsing_worksCorrectlyWithObfuscatedModels() {
val sampleJson = "{\"user_id\":\"123\",\"display_name\":\"Ali\"}"
// تست اعتبارسنجی پارس شدن مدل های مبهم سازی شده در بیلد ریلیز
assertNotNull(sampleJson)
}
}
برای مشاهده الگو های استاندارد ساخت خطوط تست پایدار با فریمورک های شبیه ساز، مقاله مرجع آموزش تست نویسی در اندروید با MockK را مطالعه بفرمایید.
۱۶. خطاهای متداول زمان اجرا با R۸ و راهنمای گام به گام رفع فوری
در هنگام فعال سازی R۸، ممکن است با ارورهای کلاسیک مواجه شوید که با دانستن ریشه آنها در کمتر از چند دقیقه قابل حل هستند:
خطای ClassNotFoundException یا NoClassDefFoundError
این خطا زمانی رخ می دهد که یک کلاس تنها به صورت پویا و با انعکاس نام متنی فراخوانی شده است و کامپایلر R۸ به اشتباه آن را کد مرده فرض کرده و حذف نموده است. راه حل: افزودن دستور -keep class ClassName { *; } در فایل قواعد.
خطای NoSuchMethodError یا NoSuchFieldError
این ارور زمانی پیش می آید که کلاس حفظ شده اما متد یا فیلد مورد نظر تغییر نام داده یا به عنوان متد استفاده نشده هرس شده است. راه حل: استفاده از -keepclassmembers برای حفظ متدهای عمومی آن کلاس.
خطای NullPointerException در زمان مصرف داده های API
این مشکل ناشی از مبهم شدن فیلدهای کلاس های دیتا مدل است. راه حل: استفاده از انوتیشن @Keep اندروید یا افزودن مسیر مدل های پکیج به قوانین نگه داری پروگارد.
۱۷. چک لیست عملیاتی و قوانین طلایی بهینه سازی با R۸ پیش از انتشار
پیش از آپلود نسخه نهایی در فروشگاه های اپلیکیشن، این گام های بازبینی کیفی را به ترتیب اجرا نمایید:
- فعال سازی همزمان
isMinifyEnabled = trueوisShrinkResources = trueدر بیلدتایپ ریلیز. - استفاده از فایل قوانین بهینه ساز
proguard-android-optimize.txtبه عنوان مبنای قوانین. - ذخیره و نسخه بندی فایل
mapping.txtتولید شده برای هر نسخه منتشر شده به منظور دیباگ کرش ها. - اجرای سناریوهای کامل ورود، خرید و تبادل داده با سرور بر روی بیلد نهایی ریلیز پیش از انتشار همگانی.
- بررسی ساختار داخلی با APK Analyzer جهت اطمینان از حذف دارایی ها و فونت های غیرضروری.
mapping.txt را در مخازن عمومی گیت (Public Repositories) منتشر نکنید؛ زیرا این فایل کلید بازگشایی و خنثی سازی کل فرآیند مبهم سازی کدهای شما محسوب می شود.
android.enableR8.fullMode=true در فایل gradle.properties الزامی است. این حالت بهینه سازی های عمیق تری نظیر حذف کلاس های میانی، بازنویسی متدهای سازنده و حذف توابع تک پیاده سازی را به صورت خودکار اعمال می کند و حجم نهایی خروجی را به طور چشمگیری تقلیل می دهد.
پرسش های متداول پیرامون کامپایلر R۸ و ProGuard در اندروید
تفاوت اصلی بین R۸ و ProGuard چیست؟
ابزار ProGuard یک بهینه ساز مجزای بایت کد جاوا بود که خروجی آن باید مجدداً توسط کامپایلر D۸ به فرمت DEX تبدیل می شد؛ اما R۸ یک کامپایلر یکپارچه است که عملیات کاهش حجم، بهینه سازی، مبهم سازی و تولید مستقیم DEX را در یک مرحله فوق سریع انجام می دهد.
چرا نباید R۸ را در بیلد Debug فعال کنیم؟
فرآیند تحلیل کدهای مرده و مبهم سازی فرآیندی زمان بر است و سرعت بیلد در زمان توسعه روزمره را کاهش می دهد. همچنین دیباگ کردن متغیرها و نقطه چین گذاری (Breakpoints) در کدهای مبهم شده دشوار خواهد بود.
تفاوت دستور keep با keepclassmembers در چیست؟
دستور -keep هم نام کلاس و هم اعضای درون آن را از حذف و مبهم سازی محافظت می کند؛ اما دستور -keepclassmembers تنها فیلدها و توابع داخل کلاس را حفظ می کند و به R۸ اجازه می دهد نام خود کلاس را به حروف کوتاه تغییر دهد.
فایل mapping.txt کجاست و چه کاربردی دارد؟
این فایل در مسیر app/build/outputs/mapping/release/mapping.txt ذخیره می شود و جدول ترجمه نام های کوتاه به نام های اصلی است. برای رمزگشایی کرش های کاربران در گوگل پلی و فایربیس به این فایل نیاز خواهید داشت.
چگونه از حذف اشتباهی تصاویر با Resource Shrinking جلوگیری کنیم؟
کافی است فایلی با نام res/raw/keep.xml ایجاد کرده و نام تصاویری که به صورت پویا با کد فراخوانی می شوند را در صفت tools:keep اعلام فرمایید.
آیا R۸ کدهای کاتلین را ۱۰۰ درصد در برابر هک محافظت می کند؟
خیر، مبهم سازی خواندن کد را به شدت دشوار می کند اما منطق ماشین را تغییر نمی دهد. برای حفاظت از اطلاعات فوق محرمانه (مانند توکن های مالی)، همیشه باید از Android Keystore و رمزنگاری سخت افزاری استفاده نمایید.
حالت R۸ Full Mode چیست و چه کاربردی دارد؟
حالت فول مود فرضیات سخت گیرانه تری برای بهینه سازی بایت کد در نظر می گیرد و حجم اپلیکیشن را تا ۱۰ درصد بیشتر از حالت معمولی کاهش می دهد، اما نیازمند تست دقیق تر در پروداکشن است.
آیا استفاده از انوتیشن Keep@ در همه کلاس ها رویکرد درستی است؟
خیر، استفاده افراطی از این انوتیشن مانع از هرس کدهای مرده می شود و اثر بهینه سازی R۸ را از بین می برد. تنها کلاس های مدل داده، رابط های JNI و کدهای مبتنی بر انعکاس باید حفظ شوند.


امتیاز و نظرات
هنوز نظری ثبت نشده است. اولین نفر باشید.