Android Development Permission Adaptation Guide

举报
yd_217846120 发表于 2026/08/24 08:47:26 2026/08/24
【摘要】 Android Development Permission Adaptation Guide IntroductionPermission adaptation is one of the most critical and error-prone aspects of Android development. Since Android 6.0 (API 23), Google int...

Android Development Permission Adaptation Guide

Introduction

Permission adaptation is one of the most critical and error-prone aspects of Android development. Since Android 6.0 (API 23), Google introduced the runtime permission mechanism, fundamentally changing how apps request and use sensitive permissions. With each new Android version, additional restrictions have been added — from background execution limits to one-time permissions, from package visibility filtering to granular media permissions.

This guide provides a comprehensive, version-by-version walkthrough of permission adaptation requirements, common pitfalls, and best practices to help your app remain compatible and well-behaved across the entire Android ecosystem.


1. Permission Classification

Android permissions are divided into three categories:

1.1 Normal Permissions

These permissions are automatically granted at install time. They do not require runtime requests and cannot be revoked by the user through the system UI.

Examples:

  • INTERNET
  • ACCESS_NETWORK_STATE
  • VIBRATE
  • WAKE_LOCK
  • SET_WALLPAPER

1.2 Dangerous Permissions

These permissions grant access to sensitive user data or system features and must be requested at runtime on Android 6.0 and above.

Examples:

  • READ_CONTACTS / WRITE_CONTACTS
  • ACCESS_FINE_LOCATION / ACCESS_COARSE_LOCATION
  • READ_EXTERNAL_STORAGE / WRITE_EXTERNAL_STORAGE
  • CAMERA
  • RECORD_AUDIO
  • READ_PHONE_STATE
  • CALL_PHONE
  • SEND_SMS

1.3 Signature Permissions

These permissions are granted automatically only if the requesting app is signed with the same certificate as the app that declared the permission.

Example:

  • ACCESS_MOCK_LOCATION

Permission Groups

Dangerous permissions are organized into groups. When one permission in a group is granted, subsequent requests for other permissions in the same group are automatically granted without user interaction. However, you should never rely on this behavior — always request each permission explicitly.


2. Runtime Permissions (Android 6.0+, API 23)

2.1 The Core Flow

The runtime permission request flow consists of three steps:

  1. Check whether the permission is already granted using ContextCompat.checkSelfPermission().
  2. Request the permission using ActivityCompat.requestPermissions() if not granted.
  3. Handle the result in onRequestPermissionsResult().

2.2 Basic Implementation

private static final int REQUEST_CODE_LOCATION = 1001;

private void requestLocationPermission() {
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
            != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this,
                new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                REQUEST_CODE_LOCATION);
    } else {
        // Permission already granted, proceed with location work
        startLocationUpdates();
    }
}

@Override
public void onRequestPermissionsResult(int requestCode,
        String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == REQUEST_CODE_LOCATION) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            startLocationUpdates();
        } else {
            if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                    Manifest.permission.ACCESS_FINE_LOCATION)) {
                // User denied but did not check "Never ask again"
                showPermissionExplanationDialog();
            } else {
                // User selected "Never ask again" — guide them to settings
                showGoToSettingsDialog();
            }
        }
    }
}

2.3 Explaining Why You Need the Permission

Before requesting a dangerous permission, you should explain to the user why your app needs it. Use shouldShowRequestPermissionRationale() to determine whether to show an educational UI:

  • Returns false on the first request (before the user has seen the system dialog).
  • Returns true if the user previously denied the request without checking “Never ask again.”
  • Returns false if the user selected “Never ask again.”

2.4 Handling “Never Ask Again”

When the user selects “Never ask again,” the system dialog will no longer appear for that permission. Your only recourse is to guide the user to the system Settings screen to grant the permission manually:

private void openAppSettings() {
    Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
    intent.setData(Uri.parse("package:" + getPackageName()));
    startActivity(intent);
}

2.5 Using the Activity Result API (Recommended)

For modern apps, prefer the ActivityResultContracts API, which replaces the deprecated onRequestPermissionsResult() callback:

private final ActivityResultLauncher<String[]> locationPermissionLauncher =
        registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(),
                result -> {
                    Boolean granted = result.get(Manifest.permission.ACCESS_FINE_LOCATION);
                    if (granted != null && granted) {
                        startLocationUpdates();
                    } else {
                        showPermissionDeniedUI();
                    }
                });

private void requestLocationPermission() {
    locationPermissionLauncher.launch(new String[]{Manifest.permission.ACCESS_FINE_LOCATION});
}

3. Android 7.0 (API 24) Adaptation

3.1 FileProvider for Content Sharing

Android 7.0 enforces StrictMode on file URI exposure. Passing a file:// URI via Intent triggers a FileUriExposedException. You must use FileProvider instead.

Step 1: Declare the provider in AndroidManifest.xml

<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="${applicationId}.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

Step 2: Define file paths in res/xml/file_paths.xml

<paths>
    <external-path name="external_files" path="." />
    <cache-path name="cache" path="." />
    <files-path name="files" path="." />
</paths>

Step 3: Generate the content URI

Uri uri = FileProvider.getUriForFile(context, getPackageName() + ".fileprovider", file);
intent.setDataAndType(uri, "image/jpeg");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

4. Android 8.0 (API 26) Adaptation

4.1 Install Unknown Apps

Android 8.0 requires explicit permission to install apps from unknown sources. Instead of the global setting, each app must request the permission individually:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    if (!getPackageManager().canRequestPackageInstalls()) {
        Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
        intent.setData(Uri.parse("package:" + getPackageName()));
        startActivityForResult(intent, REQUEST_CODE_INSTALL_PERMISSION);
    }
}

4.2 Location Behavior Changes

Android 8.0 limits background location updates. Apps receive location updates only a few times per hour when in the background. To receive more frequent updates, the app must be in the foreground or use a foreground service with a persistent notification.


5. Android 9.0 (API 28) Adaptation

5.1 HTTP Cleartext Traffic Blocked by Default

Android 9.0 blocks all non-HTTPS (cleartext HTTP) traffic by default. To allow cleartext traffic for specific domains, configure res/xml/network_security_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">example.com</domain>
    </domain-config>
</network-security-config>

Reference it in the manifest:

<application
    android:networkSecurityConfig="@xml/network_security_config"
    ... >

5.2 FOREGROUND_SERVICE Permission

Starting a foreground service now requires the FOREGROUND_SERVICE normal permission. Declare it in the manifest:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

5.3 Restricted Phone State Access

Access to phone state information such as getDeviceId() and getImei() requires the READ_PHONE_STATE dangerous permission. Without it, a SecurityException is thrown.


6. Android 10 (API 29) Adaptation

6.1 Scoped Storage

Android 10 introduces scoped storage, a major change to how apps access external storage. Apps targeting API 29 or above can only access their own app-specific directories and media collections by default.

Opt-out (temporary, not recommended for new apps):

<application
    android:requestLegacyExternalStorage="true"
    ... >

Recommended approach: Use MediaStore for media access and Storage Access Framework (SAF) for documents.

6.2 Background Location Permission

Accessing location in the background now requires a separate permission: ACCESS_BACKGROUND_LOCATION. You must first obtain ACCESS_FINE_LOCATION and then request the background permission:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    ActivityCompat.requestPermissions(this,
            new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION},
            REQUEST_CODE_BG_LOCATION);
}

The system shows a separate dialog with an option to “Allow all the time.”

6.3 Activity Recognition Permission

Accessing step counter data now requires the ACTIVITY_RECOGNITION dangerous permission:

<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />

7. Android 11 (API 30) Adaptation

7.1 One-Time Permissions

Android 11 introduces one-time permission grants. When the user selects “Only this time,” the permission is revoked after a short period of app inactivity. Your app must handle permission revocation gracefully at any point during its lifecycle.

7.2 Package Visibility Filtering

Apps can no longer query all installed packages by default. To query or interact with specific other apps, declare a <queries> element in the manifest:

<queries>
    <package android:name="com.example.targetapp" />
</queries>

Or use a broader intent filter:

<queries>
    <intent>
        <action android:name="android.intent.action.SEND" />
        <data android:mimeType="image/jpeg" />
    </intent>
</queries>

If you genuinely need to see all packages, use the QUERY_ALL_PACKAGES permission, but be warned that Google Play has strict policies on its use.

7.3 Permissions Auto-Reset

If an app is unused for several months, the system auto-revokes all granted runtime permissions. Users are notified and can re-grant permissions when they next open the app.

7.4 Foreground Service Types

You must declare a foreground service type when starting a foreground service:

<service
    android:name=".LocationService"
    android:foregroundServiceType="location" />
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    startForegroundService(serviceIntent);
}

7.5 Single-Use Media Permissions

READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE are deprecated for media access. Use granular media permissions instead:

  • READ_MEDIA_IMAGES
  • READ_MEDIA_VIDEO
  • READ_MEDIA_AUDIO

8. Android 12 (API 31) Adaptation

8.1 Exact Alarm Permission

Accessing exact alarms now requires the SCHEDULE_EXACT_ALARM permission:

<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />

8.2 Bluetooth Runtime Permissions

Bluetooth operations that previously required BLUETOOTH and BLUETOOTH_ADMIN (which were normal permissions) now require new runtime permissions:

  • BLUETOOTH_SCAN
  • BLUETOOTH_CONNECT
  • BLUETOOTH_ADVERTISE
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />

8.3 Approximate vs. Precise Location

The location permission dialog now offers two options: “Approximate” and “Precise.” You can request both:

ActivityCompat.requestPermissions(this,
        new String[]{
            Manifest.permission.ACCESS_FINE_LOCATION,
            Manifest.permission.ACCESS_COARSE_LOCATION
        },
        REQUEST_CODE_LOCATION);

If the user grants only approximate location, your app receives ACCESS_COARSE_LOCATION but not ACCESS_FINE_LOCATION. Always handle this gracefully.


9. Android 13 (API 33) Adaptation

9.1 Granular Media Permissions

Android 13 replaces READ_EXTERNAL_STORAGE with three granular permissions:

<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />

You should declare both the new granular permissions and the legacy READ_EXTERNAL_STORAGE (with maxSdkVersion="32") for backward compatibility:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
    android:maxSdkVersion="32" />

9.2 POST_NOTIFICATIONS Permission

Apps targeting API 33 must request the POST_NOTIFICATIONS runtime permission before posting notifications:

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

Request it at runtime and check the result. If denied, notifications will be silently dropped.

9.3 Body Sensors in Background

A new BODY_SENSORS_BACKGROUND permission is required to access body sensors (heart rate, etc.) from the background:

<uses-permission android:name="android.permission.BODY_SENSORS_BACKGROUND" />

10. Android 14 (API 34) Adaptation

10.1 Foreground Service Type Enforcement

Android 14 strictly enforces foreground service types. You must specify the correct type and ensure the corresponding permission is declared. Available types include:

  • dataSync
  • location
  • mediaPlayback
  • mediaProjection
  • phoneCall
  • connectedDevice
  • health
  • camera
  • microphone

The camera and microphone types additionally require the CAMERA and RECORD_AUDIO runtime permissions respectively.

10.2 Partial Photo and Video Access

Users can now grant partial access to their photo library. Your app should handle the case where only a subset of media is accessible. Check the result of MediaStore queries and use MediaStore.createWriteRequest() or MediaStore.createDeleteRequest() for batch operations that require user consent.


11. Best Practices

11.1 Request Permissions at the Point of Need

Do not request all permissions at app launch. Request each permission only when the user initiates an action that requires it. This provides context and improves grant rates.

11.2 Always Check Before Use

Never assume a permission is granted. Always call checkSelfPermission() before accessing a protected resource, because permissions can be revoked at any time (one-time permissions, auto-reset, user manual revocation).

11.3 Handle Denial Gracefully

When a permission is denied, degrade functionality gracefully rather than crashing. Show a clear explanation of what feature is unavailable and how the user can enable it.

11.4 Use Permission Libraries

Consider using established libraries to reduce boilerplate:

  • PermissionsDispatcher — annotation-based permission handling with compile-time code generation.
  • Dexter — a simple runtime permission wrapper with a fluent API.
  • Accompanist Permissions — for Jetpack Compose apps.

11.5 Test on Multiple API Levels

Permission behavior varies significantly across Android versions. Test your app on emulators or physical devices running different API levels (especially API 23, 29, 30, 33, and 34) to verify correct adaptation.

11.6 Declare the Correct targetSdkVersion

Set targetSdkVersion to the latest stable API level. Google Play requires apps to target a recent API level. Properly adapting to a higher target SDK level ensures your app uses the latest security and privacy enhancements.

11.7 Avoid Requesting Unnecessary Permissions

Every permission you request increases user suspicion and reduces trust. Audit your permission list regularly and remove any that are no longer needed. Consider alternative approaches that do not require permissions (e.g., using MediaStore instead of READ_EXTERNAL_STORAGE).


12. Common Pitfalls

Pitfall Cause Solution
SecurityException on file URI Using file:// URI on API 24+ Use FileProvider
FileUriExposedException Sharing file URI via Intent Use FileProvider with FLAG_GRANT_READ_URI_PERMISSION
Location not updating in background Background location limits (API 26+) Use foreground service with notification
Cannot read external storage on API 29+ Scoped storage enforcement Use MediaStore or requestLegacyExternalStorage (temporary)
Notifications silently dropped on API 33+ Missing POST_NOTIFICATIONS permission Request runtime permission before posting
Cannot query other apps on API 30+ Package visibility filtering Declare <queries> in manifest
getDeviceId() throws SecurityException Missing READ_PHONE_STATE Request runtime permission or use Settings.Secure.ANDROID_ID
Permission auto-revoked App unused for months (API 30+) Handle re-request gracefully on app open
Bluetooth scan fails on API 31+ Missing new Bluetooth permissions Request BLUETOOTH_SCAN and BLUETOOTH_CONNECT

13. Quick Reference: Permission Changes by API Level

API Level Android Version Key Permission Change
23 6.0 Runtime permissions introduced
24 7.0 FileProvider required for file URI sharing
26 8.0 Install unknown apps permission; background location limits
28 9.0 HTTP cleartext blocked; FOREGROUND_SERVICE permission
29 10 Scoped storage; background location permission; activity recognition
30 11 One-time permissions; package visibility; permission auto-reset
31 12 Exact alarm permission; new Bluetooth runtime permissions
33 13 Granular media permissions; POST_NOTIFICATIONS
34 14 Foreground service type enforcement; partial media access

14. Conclusion

Permission adaptation is an ongoing process. Each new Android version introduces stricter privacy and security controls, and failing to adapt results in crashes, silent failures, or Google Play rejection. The key principles are:

  1. Request at the point of need — never batch-request at launch.
  2. Check before use — permissions can be revoked at any time.
  3. Handle denial gracefully — degrade features, do not crash.
  4. Stay current — keep targetSdkVersion up to date and test on the latest API levels.
  5. Minimize permissions — request only what you truly need.

By following this guide and adopting a proactive approach to permission management, you can build an app that is robust, privacy-respecting, and compatible across the full range of Android devices.

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。