首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【Android】动态获取权限 && 通过Launcher和回调接口实现权限申请

【Android】动态获取权限 && 通过Launcher和回调接口实现权限申请

作者头像
三三是该溜子
发布2025-08-27 08:23:38
发布2025-08-27 08:23:38
26000
代码可运行
举报
文章被收录于专栏:该溜子的专栏该溜子的专栏
运行总次数:0
代码可运行

三三要成为安卓糕手

一:常见的权限

1:一般正常权限(安装时直接授予)

这些权限敏感度不高,在清单中声明即可

代码语言:javascript
代码运行次数:0
运行
复制
<uses-permission android:name="android.permission.INTERNET"/> //网络访问
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>//允许应用访问网络状态
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>//允许访问wifi
<uses-permission android:name="android.permission.WAKE_LOCK"/>//允许应用防止手机进入休眠状态
<uses-permission android:name="android.permission.VIBRATE"/>//允许应用手机震动

2:运行时权限

这些权限有一定敏感度,除了在清单中声明,还需要在代码中动态获取

代码语言:javascript
代码运行次数:0
运行
复制
ACCESS_FINE_LOCATION:访问精确的位置(使用 GPS 和网络提供的位置)
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
ACCESS_COARSE_LOCATION:访问粗略的位置(使用网络提供的位置)
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
READ_CONTACTS:读取联系人数据
<uses-permission android:name="android.permission.READ_CONTACTS"/>
WRITE_CONTACTS:写入联系人数据
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
READ_PHONE_STATE:读取电话状态(例如设备 ID、网络状态)
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
CALL_PHONE:直接拨打电话
<uses-permission android:name="android.permission.CALL_PHONE"/>
READ_CALL_LOG:读取通话记录
<uses-permission android:name="android.permission.READ_CALL_LOG"/>
WRITE_CALL_LOG:写入通话记录
<uses-permission android:name="android.permission.WRITE_CALL_LOG"/>
SEND_SMS:发送 SMS 短信
<uses-permission android:name="android.permission.SEND_SMS"/>
READ_SMS:读取 SMS 短信
<uses-permission android:name="android.permission.READ_SMS"/>
READ_EXTERNAL_STORAGE:读取外部存储的数据(在android13设备上,如果只是对媒体文件图片、视频、音频,那么可以只做单独的权限申请)
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
WRITE_EXTERNAL_STORAGE:写入外部存储的数据。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
访问图片文件
<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"/>
CAMERA:访问设备的相机。
<uses-permission android:name="android.permission.CAMERA"/>
RECORD_AUDIO:录制音频。
<uses-permission android:name="android.permission.RECORD_AUDIO"/>

3:需要在设置页面手动授予的权限

这类权限涉及到比较高的系统权限或者是用户隐私,应用内部不能直接获取,所以需要跳转系统设置页:

代码语言:javascript
代码运行次数:0
运行
复制
//SYSTEM_ALERT_WINDOW:允许应用在其他应用的上层显示窗口。用户需要在设置中手动授予这个权限
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
Intentintent=newIntent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivity(intent);

//REQUEST_INSTALL_PACKAGES:允许应用安装其他 APK 文件。用户需要在设置中手动授予这个权限。
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivity(intent);

//WRITE_SETTINGS:允许应用修改系统设置。用户需要在设置中手动授予这个权限。
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
Intentintent=newIntent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivity(intent);

//ACCESS_NOTIFICATION_POLICY:允许应用访问或修改通知策略。用户需要在设置中手动授予这个权限
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY"/>
Intent intent = new Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);
startActivity(intent);

//MANAGE_EXTERNAL_STORAGE:允许应用访问所有外部存储文件。用户需要在设置中手动授予这个权限。
//安卓13(api版本33)开始,安卓引入“分区存储模式”以限制应用对存储文件的访问,当前这个权限几乎相当于自由读写所有文件
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
startActivity(intent);

二:如何申请权限

在清单文件中进行权限声明

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
代码语言:javascript
代码运行次数:0
运行
复制
public class MyPermissionActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);

        findViewById(R.id.btn_record).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int selfPermission = checkSelfPermission(Manifest.permission.RECORD_AUDIO);
                if(selfPermission != PackageManager.PERMISSION_GRANTED){
                    requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO},200);
                }else {
                    Toast.makeText(MyPermissionActivity.this, "录音权限已经获取过了", Toast.LENGTH_SHORT).show();
                }
            }
        });

    }

    /**
     * 处理用户返回的权限授权结果
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if(requestCode == 200){
            if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
                Toast.makeText(MyPermissionActivity.this, "获取录音权限成功", Toast.LENGTH_SHORT).show();
            }else{
                Toast.makeText(MyPermissionActivity.this, "获取录音权限失败", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

1:checkSelfPermission

获取权限,需要传参permission;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2:Manifest.permission.RECORD_AUDIO

audio [ˈɔːdiəʊ] 声音的

这是一个常量,这里还有很多权限对应的常量值

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3:PackageManager.PERMISSION_GRANTED

被授权成功的意思,相对应的PackageManager.PERMISSION.DENIED

denied [dɪ’naɪəd] 拒绝 grant 授权

4:请求权限

代码语言:javascript
代码运行次数:0
运行
复制
 requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO},200);
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
  • 参数一:是一个字符串数组,放需要请求的权限
  • 参数二:处理请求码;理解成session就行,一对一家教那种感觉

5:onResultPermissionsResult

既然有请求码,自然少不了用户处理完权限申请这件事,安卓接收结果的方法

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

grantResults放着返回的结果集合,这里内部只是简单的用弹窗处理了一下显示结果

三:效果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

四:完善用户拒绝权限逻辑

场景:当用户勾选不在询问并且拒绝权限后,代码内部不可以在去获取权限了,此时需要引导用户去设置中自行勾选权限

1:完善拒绝else中的代码

代码语言:javascript
代码运行次数:0
运行
复制
		else{
                //用户拒绝权限

                //应该展示请求许可的原因吗
                boolean b = shouldShowRequestPermissionRationale(Manifest.permission.RECORD_AUDIO);
                if(!b){
                    //勾选了不在询问,一直在上一个else循环中,需要用户手动去设置页面加权限
                    new AlertDialog.Builder(MyPermissionActivity.this)
                            .setTitle("需要麦克风权限")
                            .setMessage("应用需要麦克风权限来录音,请前往设置开启权限")
                            .setPositiveButton("前往设置", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                                    Uri uri = Uri.fromParts("package", getPackageName(), null);
                                    intent.setData(uri);
                                    startActivity(intent);
                                }
                            })
                            .setNegativeButton("取消",null)
                            .show();

                }else{
                    //用户之前拒绝过权限,但是没有勾选不再询问
                    Toast.makeText(MyPermissionActivity.this, "获取录音权限失败", Toast.LENGTH_SHORT).show();
                }
            }

2:shouldShowRequestPermissionRationale

rationale 英 [ˌræʃəˈnɑːl] 理论的说明

译为:是否应该向用户显示请求权限的解释说明? 返回类型为boolean

返回 true 的情况

  • 当用户之前拒绝过该权限请求,并且没有勾选 “不再询问” 选项时,这种情况下,建议向用户解释为什么需要这个权限

返回 false 的情况

  • 系统第一次请求该权限,方法返回 false ,因为还没有过拒绝记录,不需要额外的解释。
  • 用户之前拒绝权限请求时勾选了 “不再询问” 选项,此时该方法也返回 false 。再次调用 requestPermissions 方法请求权限,系统会直接弹出权限拒绝的结果,此时就需要单独处理了
(1)参数_权限名称
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

参数 permission 就是你要检查的具体权限名称,这里的Manifest.permission.RECORD_AUDIO就是录音权限,之前用过

3:AlertDialog弹窗

在复习一下,积极按钮在右侧,消极在左侧;这里可以使用lambda表达式会更简洁;

(1)Settings.ACTION_APPLICATION_DETAILS_SETTINGS
代码语言:javascript
代码运行次数:0
运行
复制
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
  • 这里创建了一个 Intent 对象,并指定其动作(Action)为 Settings.ACTION_APPLICATION_DETAILS_SETTINGS。这个动作常量的作用是,告诉系统要启动的是应用详情设置页面的相关组件。
(2)Uri
代码语言:javascript
代码运行次数:0
运行
复制
Uri uri = Uri.fromParts("package", getPackageName(), null);
  • Uri 是统一资源标识符,用于标识和定位资源。
  • Uri.fromParts 方法用于构建一个 Uri 对象。这里传入三个参数:
    • 第一个参数 "package"Uri 的 scheme(模式),表示这是与应用包相关的 Uri
    • 第二个参数 getPackageName() 会获取当前应用的包名,这样就能定位到当前应用的设置详情。
    • 第三个参数 null 表示 fragment(片段),这里不需要,所以设为 null

4:完善效果展示

像极了爱情~~~~~第一次拒绝,第二次拒绝且不再询问

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

五:另一种权限处理流程

第一次申请权限

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

请求权限

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第一次拒绝权限

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

======================================================================

第二次申请权限

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
点击确定,第二次发出权限请求
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第二次拒绝权限,让用户自己去设置吧

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因为用户已经勾选过 “不再询问”了,所以往后在点击申请权限按钮后, requestPermissions 不会再弹出系统权限申请对话框,而是一直走这一段代码

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

流程通了

1:核心代码

代码语言:javascript
代码运行次数:0
运行
复制
 findViewById(R.id.btn_location).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //获取位置权限
                //返回true:一般是因为之前用户拒绝过了这个权限,但是没勾选不在询问
                //返回false:有可能是第一次请求权限,或者是用户选择了“不再询问”
                boolean b = shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION);
                if(b){
                    new AlertDialog.Builder(MyPermissionActivity.this)
                            .setTitle("权限请求")
                            .setMessage("我接下来,需要请求你的定位,因为我要跟踪你")
                            .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    //再次弹出权限申请
                                    requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION},100);
                                }
                            })
                            .setNegativeButton("取消",null)
                            .show();

                }else{
                    //第一次请求权限:无需解释
                    requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION},100);
                }
            }
        });
代码语言:javascript
代码运行次数:0
运行
复制
else if(requestCode == 100){
            //检查是否获得了权限
            if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
                Toast.makeText(MyPermissionActivity.this,"已获得位置权限",Toast.LENGTH_SHORT).show();
            }else{
                //用户拒绝权限
                boolean b = shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION);
                if(!b){
                    //用户勾选了"不在询问"
                    //让用户自己去设置页面手动授权
                    new AlertDialog.Builder(MyPermissionActivity.this)
                            .setTitle("需要位置权限")
                            .setMessage("应用需要位置权限,请前往设置开启权限")
                            .setPositiveButton("前往设置", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                                    Uri uri = Uri.fromParts("package", getPackageName(), null);
                                    intent.setData(uri);
                                    startActivity(intent);
                                }
                            })
                            .setNegativeButton("取消",null)
                            .show();
                }else{
                    Toast.makeText(MyPermissionActivity.this, "已拒绝位置权限", Toast.LENGTH_SHORT).show();
                }
            }
        }

一:引入

((20250812195602-2syxexr “知识回顾”)):之前在学习页面跳转的时候,学习过用lanch进行页面跳转,和利用ActivityResultLauncher进行数据传输;Contracts中预定义了很多方法,其中就有页面跳转;本篇文章展示一下权限相关的用法

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

二:代码总览

代码语言:javascript
代码运行次数:0
运行
复制
public class ARLauncherActivity extends AppCompatActivity {
    private ActivityResultLauncher<String> activityResultLauncher = registerForActivityResult(
            new ActivityResultContracts.RequestPermission(),
            new ActivityResultCallback<Boolean>() {
                @Override
                public void onActivityResult(Boolean o) {
                    if(o){
                        //获取权限成功
                        Toast.makeText(ARLauncherActivity.this, "同意授权", Toast.LENGTH_SHORT).show();
                    }else {
                        //获取权限失败
                        boolean b = shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION);
                        if (b){
                            //没有点击不在询问
                            Toast.makeText(ARLauncherActivity.this, "拒绝了权限,没有勾选不在询问", Toast.LENGTH_SHORT).show();
                        }else {
                            //勾选了不在询问
                            Toast.makeText(ARLauncherActivity.this,"拒绝了权限,勾选不在询问",Toast.LENGTH_SHORT).show();
                        }
                    }

                }
            }
    );


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_arlauncher);

        findViewById(R.id.btn_location).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int i = checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION);
                //如果没有获取权限
                if (i != PackageManager.PERMISSION_GRANTED){
                    activityResultLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION);
                }
            }
        });

    }


}

1:Contracts.RequestPermission()

这里使用的是RequestPermission,第二个参数可以理解成我们注册了一个permission回调函数,用户处理完权限,会触发onActivityResult回调方法,o作为返回值

2:泛型的使用

之前我们在使用页面跳转的时候给ActivityResultLauncher定义的泛型是Intent;这里我们传参的是Manifest.permission.ACCESS_FINE_lOCATION它的本质是一个字符串,所以这里我们定义的泛型是String

效果展示

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

总结:请求权限的两种方式

代码语言:javascript
代码运行次数:0
运行
复制
//方式一:
 activityResultLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION);
代码语言:javascript
代码运行次数:0
运行
复制
//方式二:
requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION},100);
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-08-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一:常见的权限
    • 1:一般正常权限(安装时直接授予)
    • 2:运行时权限
    • 3:需要在设置页面手动授予的权限
  • 二:如何申请权限
    • 1:checkSelfPermission
    • 2:Manifest.permission.RECORD_AUDIO
    • 3:PackageManager.PERMISSION_GRANTED
    • 4:请求权限
    • 5:onResultPermissionsResult
  • 三:效果
  • 四:完善用户拒绝权限逻辑
    • 1:完善拒绝else中的代码
    • 2:shouldShowRequestPermissionRationale
      • (1)参数_权限名称
    • 3:AlertDialog弹窗
      • (1)Settings.ACTION_APPLICATION_DETAILS_SETTINGS
      • (2)Uri
    • 4:完善效果展示
  • 五:另一种权限处理流程
    • 第一次申请权限
    • 第一次拒绝权限
    • 第二次申请权限
      • 点击确定,第二次发出权限请求
    • 第二次拒绝权限,让用户自己去设置吧
  • 1:核心代码
  • 一:引入
  • 二:代码总览
    • 1:Contracts.RequestPermission()
    • 2:泛型的使用
  • 总结:请求权限的两种方式
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档