مدیریت تغییرات پیکربندی

برخی‌از پیکربندی‌های دستگاه ممکن است درحین اجرای برنامه تغییر کند. این موارد شامل، اما نه محدود به، این موارد می‌شود:

  • اندازه نمایش برنامه
  • تراکم صفحه‌نمایش (مقیاس‌بندی نمایش)
  • جهت صفحه‌نمایش
  • اندازه و سیاهی قلم
  • منطقه زبانی
  • زمینه تیره دربرابر زمینه روشن
  • دردسترس بودن صفحه‌کلید

بیشتر این تغییرات پیکربندی به‌دلیل تعامل کاربر رخ می‌دهد. برای مثال، چرخاندن یا تا کردن دستگاه مقدار فضای صفحه نمایش دردسترس برنامه شما را تغییر می‌دهد. اتصال به نمایشگرهای خارجی یا انتقال برنامه بین نمایشگرهایی با تراکم پیکسل متفاوت، تراکم صفحه و اندازه نمایش را تغییر می‌دهد. به‌همین ترتیب، تغییر تنظیمات دستگاه مانند مقیاس‌بندی نمایشگر، اندازه قلم، زبان، یا زمینه ترجیحی، مقادیر مربوطه را در Configuration شیء تغییر می‌دهد.

این پارامترها معمولاً به تغییرات به‌اندازه کافی بزرگ در واسط کاربر برنامه شما نیاز دارند که پلاتفرم Android سازوکار ویژه‌ای برای زمانی که تغییر می‌کنند دارد. این سازوکار Activity بازآفرینی است.

تفریح و سرگرمی در فضای باز

وقتی تغییر پیکربندی رخ می‌دهد، سیستم Activity را بازسازی می‌کند. برای انجام این کار، سیستم onDestroy را فرا می‌خواند و نمونه Activity موجود را ازبین می‌برد. سپس نمونه جدیدی را بااستفاده از onCreate ایجاد می‌کند، و این نمونه جدید Activity با پیکربندی جدید و به‌روزشده مقداردهی اولیه می‌شود. این همچنین به این معنی است که سیستم واسط کاربر را با پیکربندی جدید بازسازی می‌کند.

معمولاً Activity به‌عنوان میزبان برای عناصر ترکیبی عمل می‌کند. وقتی Activity بازسازی می‌شود، «نوشتن» نیز واسط کاربر شما را بااستفاده از مقادیر پیکربندی جدید بازسازی می‌کند.

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

نمونه تفریح

ترکیب‌پذیری را درنظر بگیرید که عنوان ثابتی را بااستفاده از منبع رشته‌ای نمایش می‌دهد:

// In the res/values/strings.xml file
// <string name="compose">Jetpack Compose</string>

// In your Compose code
Text(
    text = stringResource(R.string.compose)
)

وقتی Activity ایجاد می‌شود، عنصر ترکیبی Text پیکربندی فعلی (مثل زبان) را می‌خواند و منبع رشته‌ای مناسب را حل می‌کند.

اگر زبان تغییر کند، سیستم فعالیت را بازآفرینی می‌کند. وقتی این اتفاق می‌افتد، ‫Compose رابط کاربری را بازسازی می‌کند. چون stringResource از پیکربندی کنونی می‌خواند، عنوان به‌طور خودکار به مقدار بومی‌سازی‌شده صحیح به‌روزرسانی می‌شود.

بازآفرینی همچنین هر وضعیتی را که به‌عنوان فیلد در Activity نگهداری می‌شود پاک می‌کند.

برای حفظ وضعیت میانای کاربر درطول تغییرات پیکربندی، از الگوهای مدیریت وضعیت توصیه‌شده استفاده کنید. از ViewModel برای داده‌ها و منطق کسب‌وکار و از rememberSaveable برای وضعیت سطح واسط کاربر استفاده کنید. با این سازوکارها، وضعیت شما از بازآفرینی Activity جان سالم به‌در می‌برد و واسط کاربر برای انعکاس پیکربندی جدید به‌روز می‌شود.

برای اطلاعات بیشتر درباره ذخیره کردن وضعیت در Compose، به ذخیره کردن وضعیت واسط کاربر در Compose مراجعه کنید.

انتظارات کاربر

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

برای درستی‌سنجی اینکه وضعیت در برنامه شما حفظ می‌شود یا نه، می‌توانید کنش‌هایی را انجام دهید که باعث تغییرات پیکربندی می‌شوند، هم زمانی که برنامه در پیش‌زمینه است و هم زمانی که در پس‌زمینه است. این اقدامات شامل موارد زیر می‌شود:

  • درحال چرخاندن دستگاه
  • درحال ورود به حالت چندپنجره‌ای
  • تغییر اندازه برنامه در حالت چندپنجره‌ای یا پنجره با قالب آزاد
  • تا کردن دستگاه تاشو با چند نمایشگر
  • تغییر اندازه نمایش یا مقیاس‌بندی نمایش در تنظیمات سیستم
  • انتقال برنامه بین نمایشگرهایی با تراکم‌های صفحه‌نمایش متفاوت (مثل مانیتورهای خارجی متصل یا نمایشگرهای دستگاه تاشو)
  • تغییر زمینه سیستم، مثل زمینه تیره دربرابر زمینه روشن
  • تغییر اندازه قلم
  • تغییر زبان سیستم یا برنامه
  • متصل یا قطع کردن صفحه‌کلید سخت‌افزاری
  • اتصال یا قطع اتصال پایه

چندین رویکرد وجود دارد که می‌توانید برای حفظ وضعیت مربوطه ازطریق Activity بازآفرینی اتخاذ کنید. اینکه از کدام استفاده کنید به نوع وضعیتی که می‌خواهید حفظ کنید بستگی دارد:

  • ماندگاری محلی برای مدیریت توقف فرایند برای داده‌های پیچیده یا بزرگ. فضای ذخیره‌سازی محلی دائمی شامل پایگاه‌های داده یا DataStore می‌شود.
  • اشیا نگهداری‌شده مانند نمونه‌های ViewModel برای مدیریت وضعیت مرتبط با میانای کاربر در حافظه درحالی‌که کاربر به‌طور فعال از برنامه استفاده می‌کند.
  • rememberSaveable برای حفظ وضعیت واسط کاربر گذرا در تغییرات پیکربندی و مرگ فرایند آغازشده توسط سیستم. این برای حالتی مناسب است که به ورودی کاربر، موقعیت پیمایش، یا پیمایش بستگی دارد اما در ViewModel قرار نمی‌گیرد.

برای خواندن جزئیات مربوط به هریک از این میاناهای برنامه‌سازی کاربردی و اینکه چه زمانی استفاده از هریک مناسب است، به ذخیره وضعیت‌های واسط کاربر مراجعه کنید.

محدود کردن بازآفرینی فعالیت

می‌توانید از بازآفرینی خودکار فعالیت برای تغییرات پیکربندی خاص جلوگیری کنید. در برنامه‌های مدرن «فقط Compose»، رابط کاربر شما درهرصورت بازترکیب می‌شود، اما توصیه می‌شود تغییر پیکربندی را مستقیماً مدیریت کنید.

به‌طور پیش‌فرض، تغییر پیکربندی سیستم را مجبور می‌کند تا «فعالیت» را، ازجمله واسط کاربر و هر شیء مشتق‌شده از «فعالیت»، ازبین ببرد و دوباره ایجاد کند. اگر اعلام کنید که «فعالیت» شما تغییر پیکربندی را خودش مدیریت می‌کند، سیستم از این کار جلوگیری می‌کند. درعوض، فقط شیء Configuration به‌روز می‌شود و Compose رابط کاربر شما را با مقادیر جدید بازسازی می‌کند.

مدیریت تغییرات پیکربندی به‌طور مستقیم در «ترکیب» مزایای متعددی دارد:

  • عملکرد بهبودیافته: بازسازی «میانای کاربری» هزینه کمتری نسبت‌به چرخه بازسازی کامل «فعالیت» دارد، به‌ویژه برای تغییرات جزئی.
  • پویانمایی‌های روان: با جلوگیری از راه‌اندازی مجدد «فعالیت»، می‌توانید پویانمایی‌های پیوسته در تغییرات پیکربندی، مثل گذرهای چیدمان روان درطول چرخش دستگاه، اجرا کنید.
  • حفظ وضعیت: حفظ نمونه «فعالیت» خطر ازدست رفتن وضعیت موقت میانای کاربر را درطول رویدادی مثل چرخش صفحه کاهش می‌دهد. توجه داشته باشید که شما همچنان باید حفظ وضعیت را برای مرگ پردازش آغازشده توسط سیستم مدیریت کنید.

برای غیرفعال کردن بازآفرینی فعالیت برای تغییرات پیکربندی خاص، نوع پیکربندی را به android:configChanges در ورودی <activity> در فایل AndroidManifest.xml اضافه کنید. مقادیر ممکن در اسناد مشخصه android:configChanges نشان داده می‌شود.

کد مانیفست زیر بازآفرینی Activity را برای MyActivity درصورت تغییر جهت صفحه و دردسترس بودن صفحه‌کلید غیرفعال می‌کند:

<activity
    android:name=".MyActivity"
    android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
    android:label="@string/app_name">

‫Android 17

از Android 17 (سطح API 37) شروع می‌شود، سیستم دیگر به‌طور پیش‌فرض فعالیت‌ها را برای چندین تغییر پیکربندی که معمولاً نیازی به بازسازی کامل رابط کاربری ندارند، بازراه‌اندازی نمی‌کند. درعوض، فعالیت‌ها فعال باقی می‌مانند و ازطریق onConfigurationChanged() تماس برگشتی به‌روزرسانی دریافت می‌کنند. تغییرات پیکربندی خاص شامل موارد زیر است:

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

<activity
    android:name=".MyActivity"
    android:recreateOnConfigChanges="keyboard|keyboardHidden|navigation|colorMode|touchscreen|...">
    ...
</activity>

واکنش به تغییرات پیکربندی

‫Jetpack Compose به برنامه شما امکان می‌دهد راحت‌تر به تغییرات پیکربندی واکنش نشان دهد. بااین‌حال، اگر بازآفرینی Activity را برای همه تغییرات پیکربندی غیرفعال کنید درصورتی‌که انجام این کار ممکن باشد، برنامه شما همچنان باید تغییرات پیکربندی را به‌درستی مدیریت کند.

شیء Configuration در سلسله مراتب «میانای کاربر Compose» با ترکیب محلی LocalConfiguration دردسترس است. هرگاه تغییر کند، توابع ترکیبی که از LocalConfiguration.current می‌خوانند دوباره ترکیب می‌شوند. برای اطلاعات درباره نحوه عملکرد محلی‌های ترکیب، داده‌های با حوزه محلی با CompositionLocal را ببینید.

مثال

در مثال زیر، یک عنصر ترکیبی تاریخ را با قالبی خاص نمایش می‌دهد. این عنصر ترکیبی با فراخوانی ConfigurationCompat.getLocales با LocalConfiguration.current به تغییرات پیکربندی منطقه زمانی سیستم واکنش نشان می‌دهد.

@Composable
fun DateText(year: Int, dayOfYear: Int) {
    val dateTimeFormatter = DateTimeFormatter.ofPattern(
        "MMM dd",
        ConfigurationCompat.getLocales(LocalConfiguration.current)[0]
    )
    Text(
        dateTimeFormatter.format(LocalDate.ofYearDay(year, dayOfYear))
    )
}

برای جلوگیری از بازآفرینی Activity هنگام تغییر یافتن منطقه، میزبان Activity کد «نوشتن» باید از تغییرات پیکربندی منطقه انصراف دهد. برای انجام این کار، android:configChanges را روی locale|layoutDirection تنظیم کنید.

تغییرات پیکربندی: مفاهیم کلیدی و روال‌های مطلوب

این‌ها مفاهیم کلیدی هستند که هنگام کار روی تغییرات پیکربندی باید بدانید:

  • پیکربندی‌ها: پیکربندی‌های دستگاه نحوه نمایش میانای کاربر به کاربر را تعریف می‌کند، مثل اندازه نمایش برنامه، تراکم صفحه، محل، یا زمینه سیستم. در «نوشتن»، می‌توانید بااستفاده از LocalConfiguration به مقادیر پیکربندی دسترسی پیدا کنید.
  • تغییرات پیکربندی: پیکربندی‌ها ازطریق تعامل کاربر تغییر می‌کند. برای مثال، کاربر ممکن است تنظیمات دستگاه یا نحوه تعامل فیزیکی خود با دستگاه را تغییر دهد. هیچ راهی برای جلوگیری از تغییرات پیکربندی وجود ندارد.
  • بازآفرینی Activity: تغییرات پیکربندی به‌طور پیش‌فرض منجر به بازآفرینی Activity می‌شود. این یک سازوکار داخلی برای مقداردهی مجدد وضعیت برنامه برای پیکربندی جدید است.
  • تخریب Activity: بازآفرینی Activity باعث می‌شود سیستم نمونه Activity قدیمی را تخریب کند و نمونه جدیدی را به‌جای آن ایجاد کند. نمونه قدیمی اکنون منسوخ شده است. از نگهداری ارجاع به اشیاء با محدوده چرخه حیات فراتر از محدوده موردنظر آن‌ها خودداری کنید.
  • وضعیت: وضعیت در نمونه Activity قدیمی در نمونه جدید Activity وجود ندارد، زیرا این دو نمونه شیء متفاوت هستند. به‌جای اینکه وضعیت را به «فعالیت» پیوند دهید، از واسط‌های برنامه‌سازی کاربردی توصیه‌شده برای حفظ وضعیت برنامه و کاربر استفاده کنید، همان‌طور که در ذخیره وضعیت‌های میانای کاربر توضیح داده شده است.
  • انصراف دادن: انصراف دادن از بازآفرینی فعالیت برای نوعی تغییر پیکربندی مستلزم این است که برنامه شما در واکنش به پیکربندی جدید به‌درستی به‌روز شود.

برای ارائه تجربه کاربری خوب، روال‌های مطلوب زیر را رعایت کنید:

  • برای تغییرات پیکربندی مکرر آماده باشید: صرف‌نظر از سطح API، عامل شکل، یا ابزارک UI، فرض نکنید که تغییرات پیکربندی نادر است یا هرگز اتفاق نمی‌افتد. وقتی کاربری باعث تغییر پیکربندی می‌شود، انتظار دارد برنامه‌ها به‌روز شوند و با پیکربندی جدید به‌درستی کار کنند.
  • حفظ وضعیت: وقتی Activity بازآفرینی رخ می‌دهد، وضعیت کاربر را ازدست ندهید. وضعیت را همان‌طور که در ذخیره کردن وضعیت‌های میانای کاربر بااستفاده از میاناهای برنامه‌سازی کاربردی مانند ViewModel و rememberSaveable توضیح داده شده است حفظ کنید.
  • از انصراف دادن به‌عنوان راه‌حل سریع خودداری کنید: برای جلوگیری از ازدست رفتن وضعیت، از Activity بازآفرینی انصراف ندهید. برای انصراف دادن از بازآفرینی فعالیت باید به وعده خود برای مدیریت تغییر عمل کنید، و همچنان ممکن است به‌دلیل بازآفرینی از تغییرات پیکربندی دیگر، مرگ فرایند، یا بستن برنامه، وضعیت را ازدست بدهید. غیرفعال کردن کامل بازآفرینی Activity غیرممکن است.Activity وضعیت را همان‌طور که در ذخیره وضعیت‌های واسط کاربر توضیح داده شده است حفظ کنید.
  • از تغییرات پیکربندی اجتناب نکنید: برای اجتناب از تغییرات پیکربندی و Activity بازآفرینی، محدودیت‌هایی برای جهت، نسبت ابعادی، یا قابلیت تغییر اندازه اعمال نکنید. این کار بر کاربرانی که می‌خواهند از برنامه شما به روش ترجیحی خود استفاده کنند تأثیر منفی می‌گذارد.

مدیریت تغییرات پیکربندی براساس اندازه

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

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

محدود کردن بازآفرینی فعالیت برای تغییرات پیکربندی براساس اندازه

وقتی بازآفرینی Activity را برای تغییرات پیکربندی براساس اندازه غیرفعال می‌کنید، سیستم Activity را بازآفرینی نمی‌کند. درعوض، تماسی به Activity.onConfigurationChanged دریافت می‌کند. هر عنصر ترکیبی که LocalConfiguration.current را می‌خواند به‌طور خودکار دوباره ترکیب می‌شود تا اندازه جدید را منعکس کند.

وقتی android:configChanges="screenSize|smallestScreenSize|orientation|screenLayout" در فایل مانیفست خود دارید، بازآفرینی Activity برای تغییرات پیکربندی براساس اندازه غیرفعال است.

منابع بیشتر

برای کسب اطلاعات بیشتر درباره مدیریت تغییرات پیکربندی، منابع تکمیلی زیر را ببینید:

اسناد

محتوا را می‌بیند