时间:2021-01-06 11:22:26 | 栏目:Android代码 | 点击:次
这里我们以拨打电话申请权限来写个小例子,也就是CALL_PHONE,因为拨打电话会涉及用户手机的资费问题,因而被列为了危险权限,在Android6.0系统出现之前,拨打电话功能的实现其实非常简单,修改activity_mainxml中的代码,如下:
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.runtimepermissiontest.MainActivity"> <Button android:id="@+id/make_call" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Make Call" android:textAllCaps="false"/> </android.support.constraint.ConstraintLayout>
我们在布局文件中只定义了一个按钮,当点击按钮时就会去触发拨打电话的逻辑,接着修改MainActivity中的代码,如下:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button makeCall = findViewById(R.id.make_call); makeCall.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { try { Intent intent = new Intent(Intent.ACTION_CALL); intent.setData(Uri.parse("tel:10000")); startActivity(intent); }catch (SecurityException e){ e.printStackTrace(); } } }); } }
可以看到,在按钮的点击事件中,我们构建了一个隐式Intent,Intent的action指定为Intent.ACTION_CALL,这是一个系统内置的打电话的动作,然后在data部分指定了协议是tel,号码是10000,表示打开拨号界面,这个是不需要声明权限的,而Intent.ACTION_CALL则可以直接拨打电话,因此必须申明权限,另外为了防止程序崩溃,我们将所有操作都放在了异常捕获代码块当中。
那么接下来修改AndroidManifest.xml文件,在其中声明如下权限:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.runtimepermissiontest"> <uses-permission android:name="android.permission.CALL_PHONE" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
这样我们就将拨打电话的功能实现了,并且在低于Android6.0系统的手机上都是可以正常运行的,但是如果我们在6.0或者更高版本系统的手机上运行,点击Make Call按钮就没有任何效果,这时观察logcat中的日志,会报如下错误:
错误信息中提示我们Permission Denial,可以看出,是由于权限被禁止所导致的,因为6.0及以上系统在使用危险权限时都必须进行运行时权限处理。
那么下面我们就来尝试修复这个问题,修改MainActivity中的代码,如下:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button makeCall = findViewById(R.id.make_call); makeCall.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED){ ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.CALL_PHONE},1); }else { call(); } } }); } private void call(){ try { Intent intent = new Intent(Intent.ACTION_CALL); intent.setData(Uri.parse("tel:10000")); startActivity(intent); }catch (SecurityException e){ e.printStackTrace(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode){ case 1: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){ call(); }else { Toast.makeText(this,"You denied the permission",Toast.LENGTH_SHORT).show(); } break; default: break; } } }
上面的代码将运行权限的完成流程都覆盖了,下面我们来具体解析一下,说白了,运行时权限的核心就是在程序运行过程中由用户授权我们去执行某些危险操作,程序是不可以擅自做主去执行这些危险操作的,因此,第一步就是要去判断用户是不是已经给过我们授权了,借助的是ContextCompat.checkSelfPermission()方法,checkSelfPermission()方法接收两个参数,第一个参数是Context,这个没什么好说的,第二个参数是具体的权限名,比如打电话的权限名就是Manifest.permission.CALL_PHONE,然后使用方法的返回值和packageManager.PERMISSION_CRANTED做比较,相等就说明用户已经授权,不等就表示用户没有授权。
如果已经授权的话就简单了,直接去执行拨打电话的逻辑操作就可以了,这里我们把拨打电话的逻辑封装到call()方法中,如果没有授权的话,则需要调用ActivityCompat.requestPermissions()方法来向用户申请授权,requestPermissions()方法接收3个参数,第一个参数要求是Activity的实例,第二个参数是一个String数组,我们把要申请的权限名放在数组中即可,第三个参数是请求码,只要是唯一值就可以了,这里传入1。
调用完了requestPermissions()方法之后,系统会弹出一个申请权限的对话框,然后用户可以选择同意或拒绝我们的权限申请,不论是哪种结果,最终都会回调到onRequestPermissionsResult()方法中,而授权的结果则封装在grantResults参数当中,这里我们只需要判断一下最后的授权结果,如果用户同意的话就调用call()方法来拨打电话,如果用户拒绝的话我们只能放弃操作,并且弹出一条失败提示。
现在重新运行一下程序,并点击Make Call按钮,效果如图:
由于用户还没有授权过我们拨打电话权限,因此第一次运行会弹出这样一个权限申请的对话框,用户可以选择同意或者拒绝,比如说这里点击了DENY,结果如图:
由于用户没有同意授权,我们只能弹出一个操作失败的提示,下面我们再次点击Make Call,任然会弹出权限申请的对话框,这次点击ALLOW,结果如图:
可以看到,这次我们就成功进入到拨打电话界面了,并且由于用户已经完成了授权操作,之后再点击Make Call按钮就不会再弹出权限申请对话框了,而是可以直接拨打电话,那可能你会担心,万一以后我又后悔了怎么办?没有关系,用户随时都可以将授予程序的危险权限进行关系,进入Settings-->Apps-->RuntimePermissiontTest-->Permissions,界面如图:
在这里我们就可以对任何授予过的危险权限进行关闭了。