shouldShowRequestPermissionRationale — یک متد Android API است که به توسعهدهنده میگوید آیا قبل از درخواست مجوز خطرناک باید به کاربر توضیح نشان دهد یا خیر. بر اساس Android Developer Reference, 2024، متد rational اگر کاربر قبلاً درخواست را رد کرده اما پرچم Never Ask Again را تنظیم نکرده باشد، true برمیگرداند. این ابزاری کلیدی برای ساخت UX مودبانه هنگام کار با مجوزهای زمان اجرا است.
نکات اصلی
shouldShowRequestPermissionRationale — متدی از کلاس Activity و Fragment در Android است که از طریق ActivityCompat برای سازگاری در دسترس است. این متد نام مجوز را میگیرد و Boolean برمیگرداند که نشان میدهد آیا قبل از درخواست مجدد باید به کاربر توضیح اضافی نشان داده شود. این متد در Android 6.0 Marshmallow همراه با مدل مجوزهای زمان اجرا معرفی شد.
مکانیزم rationale بر اساس ردیابی تاریخچه تعامل کاربر با دیالوگهای مجوز ساخته شده است. سیستم به خاطر میسپارد که آیا کاربر قبلاً درخواست را رد کرده است. اگر رد بدون تنظیم پرچم Never Ask Again انجام شده باشد، shouldShowRequestPermissionRationale true برمیگرداند. این سیگنالی به توسعهدهنده است: کاربر نمیفهمد چرا مجوز نیاز است و توضیح اضافی لازم است. طبق Google Material Design Guidelines، نمایش دیالوگ rationale پس از اولین رد، احتمال اعطای مجدد مجوز را 35 درصد افزایش میدهد.
درک معنای مقادیر بازگشتی مهم است: true به این معنی است که نمایش دیالوگ منطقی است، false — دیالوگ یا لازم نیست (مجوز قبلاً اعطا شده یا هرگز درخواست نشده) یا بیفایده است (Never Ask Again فعال است). متد تضمین نمیکند که دیالوگ نمایش داده شود — فقط توصیه میکند. توسعهدهنده خودش تصمیم میگیرد در پاسخ چه UI نشان دهد.
shouldShowRequestPermissionRationale در API Level 23 همراه با گروهی از متدها برای مجوزهای زمان اجرا معرفی شد. قبل از Android 6.0 همه مجوزها هنگام نصب درخواست میشدند و مکانیزم توضیح لازم نبود — کاربر کل لیست را یکباره قبول یا رد میکرد. مدل زمان اجرا موقعیتی را ممکن کرد که کاربر درخواست را بدون درک زمینه رد میکند و دقیقاً به همین دلیل rationale نیاز است.
منطق متد به این صورت است. در اولین فراخوانی requestPermissions برای یک مجوز خاص، shouldShowRequestPermissionRationale false برمیگرداند — کاربر هنوز با دیالوگ مواجه نشده است. اگر کاربر درخواست را رد کرده (دکمه Deny را زده)، متد شروع به بازگرداندن true میکند. پس از رد مجدد با پرچم Never Ask Again، متد false برمیگرداند.
جدول کامل وضعیتها:
| وضعیت | shouldShowRationale | checkSelfPermission | اقدام توسعهدهنده |
|---|---|---|---|
| درخواست نشده | false | DENIED | نمایش دیالوگ سیستمی |
| اعطا شده | false | GRANTED | اجرای تابع |
| رد شده برای اولین بار | true | DENIED | نمایش rationale، سپس دیالوگ سیستمی |
| Never Ask Again | false | DENIED | هدایت به تنظیمات |
ترکیب shouldShowRequestPermissionRationale = false و checkSelfPermission = DENIED — سختترین حالت برای پردازش است. به این معنی است که یا مجوز هرگز درخواست نشده یا Never Ask Again تنظیم شده است. توسعهدهنده باید این دو حالت را از هم تشخیص دهد. تنها راه — ذخیره پرچم firstRequest در SharedPreferences یا استفاده از SavedStateHandle. در اولین درخواست پرچم را تنظیم کنید و اگر shouldShowRationale false برگرداند و پرچم قبلاً true باشد — یعنی Never Ask Again فعال است.
shouldShowRequestPermissionRationale زمانی بازنشانی میشود که کاربر برنامه را حذف و دوباره نصب کند، دادههای برنامه را پاک کند یا تنظیمات مجوز را بازنشانی کند. پس از نصب مجدد، متد دوباره برای اولین درخواست false برمیگرداند. بهروزرسانیهای سیستمی و تغییر نسخه Android تاریخچه را بازنشانی نمیکنند — در دادههای برنامه ذخیره میشود.
پیادهسازی صحیح rationale شامل سه مؤلفه است: بررسی shouldShowRequestPermissionRationale پس از رد، نمایش دیالوگ سفارشی با توضیح و فراخوانی مجدد requestPermissions پس از پاسخ مثبت کاربر. دیالوگ باید مختصر، مشخص و توضیح دهد که چرا برنامه دقیقاً به این مجوز نیاز دارد.
private fun requestLocationWithRationale() {
val permission = Manifest.permission.ACCESS_FINE_LOCATION
when {
ContextCompat.checkSelfPermission(
this, permission
) == PackageManager.PERMISSION_GRANTED -> {
startLocationTracking()
}
ActivityCompat.shouldShowRequestPermissionRationale(
this, permission
) -> {
showRationaleDialog(permission)
}
else -> {
requestPermissionLauncher.launch(permission)
}
}
}
private fun showRationaleDialog(
permission: String
) {
AlertDialog.Builder(this)
.setTitle("چرا دسترسی به موقعیت مکانی نیاز است")
.setMessage(
"برنامه از موقعیت مکانی برای علامتگذاری" +
" مکانها روی نقشه استفاده میکند. بدون این مجوز" +
" عملکرد کار نخواهد کرد."
)
.setPositiveButton("اجازه دادن") { _, _ ->
requestPermissionLauncher.launch(permission)
}
.setNegativeButton("لغو", null)
.show()
}
بهترین روشهای Material Design استفاده از bottom sheet یا بنر درونخطی به جای دیالوگ مدال برای rationale را توصیه میکنند. Bottom sheet کمتر مزاحم است و به کاربر زمینه میدهد. عنصر درونخطی در صفحه (مثلاً کارتی با توضیح و دکمه اجازه) نشان میدهد که عملکرد بدون مجوز در دسترس نیست، اما بقیه رابط را مسدود نمیکند.
متن rationale باید محلیسازی شده و با عملکرد خاص تطبیق داده شود. از عبارات کلی مانند «این برای کار برنامه لازم است» استفاده نکنید. مشخصاً بگویید: «برای نمایش آب و هوای نزدیک شما» یا «برای ذخیره عکسها در گالری». طبق Google UX Research، توضیحات مشخص احتمال اعطای مجوز را 50 درصد افزایش میدهد.
تفاوت بین shouldShowRequestPermissionRationale = true (رد اول) و false در حالت DENIED (Never Ask Again) — نکته کلیدی در پردازش مجوزها است. در حالت اول کاربر مردد بود و توضیح اضافی میتواند او را به اعطای دسترسی متقاعد کند. در حالت دوم — کاربر تصمیم نهایی را گرفته و دیالوگ سیستمی مجدد فقط باعث ناراحتی میشود.
الگوریتم پردازش پس از رد باید به این صورت باشد:
مهم است ترتیب را اشتباه نگیرید: ابتدا shouldShowRequestPermissionRationale را بررسی کنید، نه checkSelfPermission را. checkSelfPermission در هر دو حالت DENIED برمیگرداند. فقط shouldShowRequestPermissionRationale رد اول را از Never Ask Again متمایز میکند. از SavedStateHandle یا SharedPreferences برای ذخیره پرچم «درخواست اول انجام شد» استفاده کنید — این تنها راه مطمئن برای تشخیص «درخواست نشده» از «مسدود شده» است.
rationale را فقط یک بار نمایش دهید. اگر کاربر پس از rationale دوباره درخواست را رد کرد — دیگر توضیح را نشان ندهید. مستقیماً به پیشنهاد باز کردن تنظیمات بروید. نمایش مکرر rationale به عنوان مزاحمت تلقی میشود و رتبه برنامه را کاهش میدهد. سناریوی بهینه: درخواست — رد — rationale — درخواست مجدد — رد — تنظیمات.
قبل از اولین درخواست rationale را نشان ندهید. برخی توسعهدهندگان به اشتباه قبل از اولین دیالوگ توضیح نشان میدهند و استدلال میکنند که «کاربر باید بفهمد». این UX را بدتر میکند: کاربر به جای یک دیالوگ، دو دیالوگ پشت سر هم میبیند. Google توصیه میکند دیالوگ سیستمی را فوراً نشان دهید و rationale را فقط پس از رد.
از rationale زمینهای استفاده کنید که به لحظه نیاز واقعی به عملکرد گره خورده است. همه مجوزها را هنگام شروع برنامه درخواست نکنید — این پایینترین نرخ اعطا است. وقتی کاربر دکمه «عکس گرفتن» را زد CAMERA را درخواست کنید و وقتی نقشه را باز کرد LOCATION را. درخواست زمینهای همراه با rationale اعطا را تا 80 درصد در مقابل 30 درصد در درخواست اولیه افزایش میدهد.
تست shouldShowRequestPermissionRationale نیاز به بررسی چهار وضعیت جدول دارد: درخواست نشده، اعطا شده، رد شده، Never Ask Again. در تستهای واحد از FakePermissionHandler با رفتار قابل تنظیم shouldShowRationale استفاده میشود. در تستهای ابزاری — UiAutomator یا Espresso با شبیهسازی پاسخها به دیالوگهای سیستمی.
class RationaleViewModelTest {
private val handler = FakePermissionHandler()
private val viewModel = PermissionsViewModel(handler)
fun testFirstDenial_shouldShowRationale() {
handler.shouldShowRationale = true
handler.cameraResult =
PermissionResult.DENIED(true)
viewModel.onCameraRequested()
assertEquals(
PermissionUiState.Denied(true),
viewModel.uiState.value
)
}
fun testNeverAskAgain_redirectToSettings() {
handler.shouldShowRationale = false
handler.cameraResult =
PermissionResult.DENIED(false)
viewModel.onCameraRequested()
assertEquals(
PermissionUiState.RedirectToSettings,
viewModel.uiState.value
)
}
}
سناریوی کلیدی برای تست ابزاری — بررسی اینکه دیالوگ rationale واقعاً پس از اولین رد نمایش داده میشود. از Espresso با idling resources برای انتظار دیالوگ سیستمی استفاده کنید، سپس Deny را بزنید، ظاهر شدن دیالوگ سفارشی با متن توضیح را بررسی کنید و Allow را بزنید — اعطا را بررسی کنید. UIAutomator امکان تعامل با دیالوگ سیستمی از طریق متن دکمه را فراهم میکند که تست را پایدارتر میکند.
همچنین ارزش تست سناریوی رد در داخل دیالوگ rationale را دارد. اگر کاربر در توضیح سفارشی Deny را زده باشد، shouldShowRequestPermissionRationale باید دوباره true برگرداند، زیرا Never Ask Again هنوز فعال نشده است. بهترین روش — پس از دو رد متوالی مستقیماً به تنظیمات هدایت کنید تا کاربر را با توضیحات تکراری ناراحت نکنید و رتبه برنامه را کاهش ندهید.
سوالات متداول
true — اگر درخواست قبلاً رد شده و Never Ask Again تنظیم نشده است. false — اگر مجوز درخواست نشده، اعطا شده یا برای همیشه مسدود شده است. ترکیب false + DENIED نیاز به بررسی از طریق پرچم اضافی دارد.
rationale را فقط پس از اولین رد کاربر، زمانی که shouldShowRequestPermissionRationale true برگرداند، نشان دهید. قبل از اولین درخواست rationale لازم نیست — این UX را بدتر میکند و دیالوگهای اضافی ایجاد میکند.
پرچم isFirstRequest را در SharedPreferences یا SavedStateHandle ذخیره کنید. اگر shouldShowRationale = false، checkSelfPermission = DENIED و پرچم = true — یعنی Never Ask Again فعال شده است. اگر پرچم = false — این اولین درخواست است.
دیالوگی با دکمه «باز کردن تنظیمات» نشان دهید که کاربر را به ACTION_APPLICATION_DETAILS_SETTINGS هدایت میکند. requestPermissions را دوباره فراخوانی نکنید — دیالوگ ظاهر نمیشود و نتیجه بدون پیام DENIED میآید.
در تستهای واحد از FakePermissionHandler با فیلد قابل تنظیم shouldShowRationale استفاده کنید. در تستهای ابزاری — Espresso یا UIAutomator با شبیهسازی دیالوگ سیستمی. هر 4 وضعیت جدول را بررسی کنید.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید