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

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

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

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

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

فعالیت تفریحی

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

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

نمونه تفریحی

TextView را در نظر بگیرید که عنوان ثابتی را با استفاده از android:text="@string/title" نمایش می دهد، همانطور که در یک فایل XML طرح بندی تعریف شده است. هنگامی که نما ایجاد می شود، متن را دقیقاً یک بار بر اساس زبان فعلی تنظیم می کند. اگر زبان تغییر کند، سیستم فعالیت را دوباره ایجاد می کند. در نتیجه، سیستم همچنین نمای را دوباره ایجاد می کند و آن را بر اساس زبان جدید به مقدار صحیح اولیه می دهد.

این بازی همچنین هر حالتی را که به عنوان فیلد در Activity یا هر یک از Fragment ، View یا سایر اشیاء موجود در آن نگهداری می شود، پاک می کند. این به این دلیل است که Activity recreation یک نمونه کاملاً جدید از Activity و UI ایجاد می کند. علاوه بر این، Activity قدیمی دیگر قابل مشاهده یا معتبر نیست، بنابراین هرگونه ارجاع باقیمانده به آن یا اشیاء موجود در آن قدیمی است. آنها می توانند باگ، نشت حافظه و خرابی ایجاد کنند.

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

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

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

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

سه رویکرد اصلی وجود دارد که می‌توانید برای حفظ وضعیت مرتبط از طریق «تفریح Activity استفاده کنید. اینکه کدام مورد استفاده شود بستگی به نوع حالتی دارد که می خواهید حفظ کنید:

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

برای مطالعه جزئیات در مورد APIهای هر یک از اینها، و زمانی که استفاده از هر کدام مناسب است، به Save states UI مراجعه کنید.

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

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

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

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

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

برخی از تغییرات پیکربندی همیشه باعث شروع مجدد فعالیت می شود. شما نمی توانید آنها را غیرفعال کنید. به عنوان مثال، نمی‌توانید تغییر رنگ‌های پویا معرفی شده در اندروید 12L (سطح API 32) را غیرفعال کنید.

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

در سیستم View ، هنگامی که تغییری در پیکربندی رخ می دهد که برای آن Recreation Activity غیرفعال کرده اید، فعالیت با Activity.onConfigurationChanged() تماس می گیرد. هر نماهای پیوست شده نیز با View.onConfigurationChanged() تماس می گیرند. برای تغییرات پیکربندی که به android:configChanges اضافه نکرده‌اید، سیستم طبق معمول فعالیت را دوباره ایجاد می‌کند.

متد onConfigurationChanged() یک شی Configuration دریافت می کند که پیکربندی دستگاه جدید را مشخص می کند. فیلدهای موجود در شی Configuration را بخوانید تا مشخص کنید پیکربندی جدید شما چیست. برای ایجاد تغییرات بعدی، منابعی را که در رابط کاربری خود استفاده می کنید به روز کنید. هنگامی که سیستم این روش را فراخوانی می کند، شی Resources فعالیت شما به روز می شود تا منابع را بر اساس پیکربندی جدید برگرداند. این به شما امکان می‌دهد بدون اینکه سیستم فعالیت شما را مجدداً راه‌اندازی کند، عناصر رابط کاربری خود را بازنشانی کنید.

به عنوان مثال، پیاده سازی onConfigurationChanged() زیر بررسی می کند که آیا یک صفحه کلید در دسترس است یا خیر:

کاتلین

override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)

    // Checks whether a keyboard is available
    if (newConfig.keyboardHidden === Configuration.KEYBOARDHIDDEN_YES) {
        Toast.makeText(this, "Keyboard available", Toast.LENGTH_SHORT).show()
    } else if (newConfig.keyboardHidden === Configuration.KEYBOARDHIDDEN_NO) {
        Toast.makeText(this, "No keyboard", Toast.LENGTH_SHORT).show()
    }
}

جاوا

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    // Checks whether a keyboard is available
    if (newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_YES) {
        Toast.makeText(this, "Keyboard available", Toast.LENGTH_SHORT).show();
    } else if (newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO){
        Toast.makeText(this, "No keyboard", Toast.LENGTH_SHORT).show();
    }
}

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

حفظ حالت

هنگامی که از این تکنیک استفاده می کنید، همچنان باید در طول چرخه زندگی عادی فعالیت حالت خود را حفظ کنید. این به دلیل موارد زیر است:

  • تغییرات اجتناب ناپذیر: تغییرات پیکربندی که نمی توانید از آنها جلوگیری کنید می توانند برنامه شما را مجددا راه اندازی کنند.
  • مرگ فرآیند: برنامه شما باید بتواند مرگ فرآیند آغاز شده توسط سیستم را مدیریت کند. اگر کاربر برنامه شما را ترک کند و برنامه به پس‌زمینه برود، ممکن است سیستم برنامه را از بین ببرد.

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

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

شی Configuration در سلسله مراتب Compose UI با LocalConfiguration ترکیب محلی موجود است. هر زمان که تغییر می کند، توابع ترکیبی که از LocalConfiguration.current می خوانند دوباره ترکیب می شوند. برای اطلاعات در مورد نحوه عملکرد محلی های ترکیب، به داده های محدوده محلی با CompositionLocal مراجعه کنید.

مثال

در مثال زیر، یک composable تاریخ را با یک فرمت خاص نمایش می دهد. Composable با فراخوانی 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 میزبان کد Compose باید از تغییرات پیکربندی منطقه انصراف دهد. برای انجام این کار، android:configChanges روی locale|layoutDirection تنظیم کنید.

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

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

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

برای ارائه یک تجربه کاربری خوب، بهترین شیوه های زیر را رعایت کنید:

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

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

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

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

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

هنگامی که Recreation Activity برای تغییرات پیکربندی مبتنی بر اندازه غیرفعال می‌کنید، سیستم Activity دوباره ایجاد نمی‌کند. در عوض، یک تماس با Activity.onConfigurationChanged() دریافت می کند. هر نمای پیوست شده با View.onConfigurationChanged() تماس دریافت می کند.

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

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

در Android نسخه 7.0 (سطح API 24) و بالاتر، Activity تفریحی فقط برای تغییرات پیکربندی مبتنی بر اندازه در صورتی انجام می‌شود که تغییر اندازه قابل توجه باشد. هنگامی که سیستم به دلیل اندازه ناکافی یک Activity ایجاد نمی کند، ممکن است سیستم به جای Activity.onConfigurationChanged() و View.onConfigurationChanged() را فراخوانی کند.

برخی از اخطارها در رابطه با تماس‌های Activity و View » در زمانی که Activity دوباره ایجاد نمی‌شود وجود دارد:

  • در Android 11 (سطح API 30) تا Android 13 (سطح API 33)، Activity.onConfigurationChanged() فراخوانی نمی شود.
  • یک مشکل شناخته شده وجود دارد که در آن ممکن است در برخی موارد View.onConfigurationChanged() در Android 12L (سطح API 32) و نسخه های اولیه Android 13 (سطح API 33) فراخوانی نشود. برای اطلاعات بیشتر، این شماره عمومی را ببینید. از آن زمان در نسخه های بعدی اندروید 13 و اندروید 14 به این موضوع پرداخته شده است.

برای کدهایی که وابسته به گوش دادن به تغییرات پیکربندی مبتنی بر اندازه هستند، توصیه می کنیم به جای تکیه بر Activity recreation یا Activity.onConfigurationChanged() View.onConfigurationChanged() از یک برنامه کاربردی View با یک View.onConfigurationChanged() لغو شده استفاده کنید.