shouldShowRequestPermissionRationale is an Android API method that tells the developer whether to show an explanation to the user before requesting a dangerous permission. According to the Android Developer Reference, 2024, the method returns true if the user previously denied the request but did not set the Never Ask Again flag. This is a key tool for building polite UX when working with runtime permissions.
Key Takeaways
shouldShowRequestPermissionRationale is a method of the Activity and Fragment classes in Android, available through ActivityCompat for compatibility. It takes a permission name and returns a Boolean indicating whether to show the user an additional explanation before requesting again. The method appeared in Android 6.0 Marshmallow along with the runtime permission model.
The rationale mechanism is built on tracking the user's interaction history with permission dialogs. The system remembers whether the user denied the request previously. If the denial occurred without setting the Never Ask Again flag, shouldShowRequestPermissionRationale returns true. This is a signal to the developer: the user does not understand why the permission is needed, and additional explanation is required. According to Google Material Design Guidelines, showing a rationale dialog after the first denial increases the probability of granting the permission again by 35 percent.
It is important to understand the semantics of the return values: true means showing the dialog makes sense, false means the dialog is either not needed (permission already granted or never requested) or useless (Never Ask Again active). The method is not a guarantee that the dialog will be shown — it only gives a recommendation. The developer decides which UI to show in response.
shouldShowRequestPermissionRationale was introduced in API Level 23 along with a group of methods for runtime permissions. Before Android 6.0, all permissions were requested at install time, and no explanation mechanism was needed — the user accepted or rejected the entire list at once. The runtime model made it possible for a user to deny a request without understanding the context, and this is exactly why rationale is needed.
The method logic works as follows. On the first call to requestPermissions for a specific permission, shouldShowRequestPermissionRationale returns false — the user has not yet encountered the dialog. If the user denies the request (presses Deny), the method starts returning true. After a repeated denial with the Never Ask Again flag, the method returns false.
Complete state table:
| State | shouldShowRationale | checkSelfPermission | Developer Action |
|---|---|---|---|
| Not requested | false | DENIED | Show system dialog |
| Granted | false | GRANTED | Execute function |
| Denied first time | true | DENIED | Show rationale, then system dialog |
| Never Ask Again | false | DENIED | Redirect to Settings |
The combination shouldShowRequestPermissionRationale = false and checkSelfPermission = DENIED is the hardest case to handle. It means either the permission was never requested or Never Ask Again is set. The developer needs to distinguish these two states. The only way is to store an isFirstRequest flag in SharedPreferences or using SavedStateHandle. Set the flag on the first request, and if shouldShowRationale returns false while the flag is already true — it means Never Ask Again.
shouldShowRequestPermissionRationale resets if the user uninstalls and reinstalls the app, clears app data, or resets permission settings. After reinstallation, the method will again return false for the first request. System updates and Android version changes do not reset the history — it is stored in the app data.
A proper implementation of rationale includes three components: checking shouldShowRequestPermissionRationale after denial, showing a custom dialog with an explanation, and calling requestPermissions again after a positive user response. The dialog should be brief, specific, and explain why the app needs this particular permission.
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("Why location access is needed")
.setMessage(
"The app uses location to mark" +
" places on the map. Without this permission," +
" the feature will not work."
)
.setPositiveButton("Allow") { _, _ ->
requestPermissionLauncher.launch(permission)
}
.setNegativeButton("Cancel", null)
.show()
}
Material Design best practices recommend using a bottom sheet or inline banner instead of a modal dialog for rationale. A bottom sheet is less intrusive and gives the user context. An inline element on screen (for example, a card with an explanation and an Allow button) shows that the function is unavailable without the permission but does not block the rest of the interface.
The rationale text should be localized and adapted to the specific function. Do not use generic phrases like “This is needed for the app to work.” Specify concretely: “To display weather near you” or “To save photos to the gallery.” Specific explanations increase the likelihood of granting permission by 50 percent according to Google UX Research.
The difference between shouldShowRequestPermissionRationale = true (first denial) and false with DENIED (Never Ask Again) is a key point in permission handling. In the first case, the user was hesitant, and additional explanation can convince them to grant access. In the second, the user made a final decision, and repeating the system dialog will only cause irritation.
The handling algorithm after denial should look like this:
It is important not to confuse the order: check shouldShowRequestPermissionRationale first, not checkSelfPermission. checkSelfPermission will still return DENIED in both cases. Only shouldShowRequestPermissionRationale distinguishes the first denial from Never Ask Again. Use SavedStateHandle or SharedPreferences to store the “first request was made” flag — this is the only reliable way to distinguish “never requested” from “blocked.”
Show rationale only once. If the user denies the request again after seeing the rationale, do not show the explanation again. Go straight to offering to open Settings. Repeatedly showing rationale is perceived as annoyance and lowers the app rating. The optimal scenario: request — denial — rationale — repeat request — denial — Settings.
Do not show rationale before the first request. Some developers mistakenly show an explanation before the very first dialog, arguing that “the user must understand.” This hurts UX: the user sees two dialogs in a row instead of one. Google recommends showing the system dialog immediately, and rationale only after denial.
Use contextual rationale tied to the moment when the function is actually needed. Do not request all permissions at app startup — this has the lowest grant rate. Request CAMERA when the user taps “Take Photo” and LOCATION when they open the map. Contextual request combined with rationale increases grants to 80 percent versus 30 percent when requested at startup.
Testing shouldShowRequestPermissionRationale requires checking the four table states: not requested, granted, denied, Never Ask Again. In unit tests, use FakePermissionHandler with configurable shouldShowRationale behavior. In instrumentation tests, use UiAutomator or Espresso with dialog response emulation.
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
)
}
}
The key scenario for an instrumentation test is verifying that the rationale dialog actually appears after the first denial. Use Espresso with idling resources to wait for the system dialog, then press Deny, check for the custom explanation dialog, and press Allow — verify the grant. UIAutomator allows interacting with the system dialog by button text, making the test more stable.
Also test the denial scenario inside the rationale dialog. If the user presses Deny in the custom explanation, shouldShowRequestPermissionRationale should return true again, since Never Ask Again is not yet activated. The best practice is to redirect to Settings after two consecutive denials to avoid annoying the user with repeated explanations and lowering the app rating.
Frequently Asked Questions
true — if the request was denied previously and Never Ask Again is not set. false — if the permission was never requested, granted, or permanently blocked. The combination false + DENIED requires checking via an additional flag.
Show rationale only after the first user denial, when shouldShowRequestPermissionRationale returned true. Before the first request, rationale is not needed — it worsens UX and creates unnecessary dialogs.
Store an isFirstRequest flag in SharedPreferences or SavedStateHandle. If shouldShowRationale = false, checkSelfPermission = DENIED and the flag is true — Never Ask Again is active. If the flag is false — it is the first request.
Show a dialog with an “Open Settings” button that redirects the user to ACTION_APPLICATION_DETAILS_SETTINGS. Do not call requestPermissions again — the dialog will not appear, and the result will come back as DENIED without any message.
In unit tests, use FakePermissionHandler with a configurable shouldShowRationale field. In instrumentation tests, use Espresso or UIAutomator with system dialog emulation. Check all 4 states from the table.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also