سلام. یکی از چالشهای همیشگی در توسعه اپلیکیشنهای اندروید، مدیریت Configuration Changes (مانند چرخش صفحه یا تغییر تم) است. در Jetpack Compose، ابزارهای مختلفی برای مدیریت State داریم، اما همیشه یک جای خالی احساس میشد؛ جایی که میخواستیم یک آبجکت سنگین یا غیرقابل سریالسازی (Non-serializable) را بدون استفاده از ViewModel در چرخش صفحه حفظ کنیم.
اخیراً گوگل Retain API را معرفی کرده است که دقیقاً برای همین منظور طراحی شده است. در این پست به بررسی این API جدید، تفاوت آن با ابزارهای قبلی و زمان مناسب برای استفاده از آن میپردازیم.
بیشتر بخوانید: بررسی Retain API در Jetpack Compose: پر کردن شکاف میان remember و ViewModel
مشکل کجاست؟
در Compose، ما معمولاً از remember استفاده میکنیم. اما همانطور که میدانید، با چرخش صفحه (Activity Recreation)، مقادیر داخل remember پاک میشوند.
راهکار قبلی rememberSaveable است. این تابع عالی عمل میکند، اما دو محدودیت بزرگ دارد:
- محدودیت نوع داده: دادهها باید Serializable یا Parcelable باشن، داخل
Bundleجا شوند . - محدودیت حجم: برای ذخیره آبجکتهای سنگین یا پیچیده مثل
ExoPlayer،Bitmapیا کلاسهای پیچیدهای که خودمان کنترلی روی سریالسازی آنها نداریم، مناسب نیست.
راهکار جدید: retain
تابع retain دقیقاً در شکاف بین remember و ViewModel قرار میگیرد. این تابع به شما اجازه میدهد هر نوع آبجکتی (حتی آنهایی که سریالسازی نمیشوند) را در حافظه نگه دارید تا از چرخش صفحه جان سالم به در ببرند.
ویژگیهای کلیدی retain:
- بقا در چرخش صفحه: مقادیر با recreate شدن اکتیویتی از بین نمیروند.
- حفظ نمونه اصلی (Instance): برخلاف
rememberSaveableکه آبجکت را بازسازی میکند،retainدقیقاً همان نمونه (Reference) قبلی را نگه میدارد. - بدون نیاز به سریالسازی: میتوانید هر چیزی، از یک Listener ساده تا یک نمونه سنگین Video Player را در آن نگه دارید.
مثال کاربردی: مدیا پلیر
تصور کنید میخواهید یک ExoPlayer را در یک Composable بسازید. اگر از remember استفاده کنید، با هر چرخش صفحه پلیر دوباره ساخته شده و پخش ویدیو قطع میشود. rememberSaveable هم کار نمیکند چون ExoPlayer قابل سریالسازی نیست. اینجا retain میدرخشد:
Kotlin
@Composable
fun VideoPlayer() {
val context = LocalContext.current.applicationContext
// این آبجکت در چرخش صفحه حفظ میشود
val exoPlayer = retain {
ExoPlayer.Builder(context).build()
}
// استفاده از پلیر در UI...
}
مفهوم جدید: RetainedEffect
همراه با این API، مفهومی شبیه به DisposableEffect داریم که رفتار هوشمندانهتری دارد. اگر شما نیاز دارید وقتی Composable “واقعاً” از صفحه حذف شد (نه فقط موقع چرخش صفحه) منابعی را آزاد کنید، باید از مکانیزم پاکسازی retain استفاده کنید.
در retain، برخلاف DisposableEffect، کد پاکسازی (Cleanup) هنگام چرخش صفحه اجرا نمیشود. تنها زمانی اجرا میشود که آن Composable برای همیشه از ساختار UI (Hierarchy) حذف شود.
مقایسه: retain در برابر ViewModel
شاید بپرسید “چرا از همان ViewModel استفاده نکنیم؟”
داکیومنتهای اندروید تأکید میکنند که retain جایگزین ViewModel نیست.
| ویژگی | retain | ViewModel |
| طول عمر (Scope) | محدود به یک Composable خاص (Positional) | محدود به صفحه (Screen) یا Navigation Graph |
| هدف اصلی | منطق UI، کشهای داخلی، اشیاء سنگین (UI Plumbing) | منطق بیزنس (Business Logic)، مدیریت State صفحه |
| وابستگی | مستقل، بدون نیاز به Hilt/Dagger | معمولاً با Hilt و SavedStateHandle ترکیب میشود |
| بقا در Process Death | ❌ خیر (اگر سیستم اپ را بکشد، دیتا میپرد) | ✅ بله (اگر با SavedStateHandle ترکیب شود) |
چه زمانی از چی استفاده کنیم؟
- از
rememberاستفاده کنید: برای Stateهای ساده و موقتی که با چرخش صفحه مهم نیست اگر ریست شوند (مثل انیمیشنهای لحظهای). - از
rememberSaveableاستفاده کنید: برای ذخیره ورودی کاربر (تکسباکسها)، موقعیت اسکرول و Stateهای کوچکی که باید حتی اگر پروسه اپ کشته شد، باقی بمانند. - از
ViewModelاستفاده کنید: برای مدیریت کلی دیتای صفحه، ارتباط با دیتابیس/سرور و منطقهای پیچیده. - از
retainاستفاده کنید:- زمانی که نیاز دارید یک آبجکت سنگین یا پیچیده (مثل Video Player، Socket Connection، Image Loader) را فقط در یک Composable خاص نگه دارید.
- زمانی که میخواهید یک کامپوننت Reusable بسازید که state داخلی خودش را مدیریت کند و نمیخواهید خود را مجبور به ساخت ViewModel کنید.
جمعبندی
قابلیت retain ابزاری قدرتمند برای نویسندگان کتابخانهها (Library Authors) و توسعهدهندگانی است که کامپوننتهای پیشرفته UI میسازند. این API مدیریت منابع را در چرخش صفحه بسیار ساده میکند، اما باید مراقب بود که از آن به عنوان جایگزینی برای معماری استاندارد MVVM استفاده نشود.
برای توضیحات بیشتر، این ویدیو از فیلیپ می تونه خیلی مفید باشه.


دیدگاهتان را بنویسید