Marshmallow
提出了一套新的权限模型,在这一系列的文章中,我们将从技术上探讨如何处理权限问题,以及如何让用户体验更友好。
首先,我们需要将应用所需要的权限进行分类:核心权限与增强功能权限,核心权限是指,应用核心功能所需要的权限,一旦这些权限获取不到,将无法正常工作,而增强功能权限是指,应用拿不到这个权限时,部分周边功能无法使用,使摄像头
权限来说,对于一个相机类应用
来说法,拿不到摄像头权限,就无法拍照,这个应用也就失去了意义。然而,其它功能比如:给已拍摄的照片打上位置坐标所需要的ACCESS_FINE_LOCATION
就不是必需的。
接下来的内容,我们以RECORD_AUDIO
和MODIFY_AUDIO_SETTINGS
权限来举例,为了能获取这2个权限,我们需要先在Manifest
中做如下声明:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.stylingandroid.permissions">
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<application
android:allowBackup="false"
android:fullBackupContent="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme.NoActionBar"
tools:ignore="GoogleAppIndexingWarning">
<activity android:name=".MainActivity" />
<activity
android:name=".PermissionsActivity"
android:label="@string/title_activity_permissions"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
这是自我们从Android API 1以来,获取权限的标准方式。当我们把targetSdkVersion
指定为23或更大时,我们除了在Manifest中声明时,我们还需要在运行的时候申请。很多开发者,因为忘了在运行时申请,同时简单的把自己的targetSDKVersion指定为最高版本,从而导致了大量的Crash。因此,当我们把应用的targetSdkVersion
升级时,我们首先要考虑的就是,关于权限的问题。
另个需要说明的是,现在有很多代码库用于简化我们在运行时期间的权限获取,这些库挺有用的,但我觉得我们需要真正弄明白这些库背后的处理流程,以及用户的选择。否则,使用这些库,将反过来给我们的应用带来问题。这是这一系列文章的主要目的。
Android系统将权限分为2大类,一类是危险的权限,比如RECORD_AUDIO
,一类是普通的权限,比如MODIFY_AUDIO_SETTINGS
。危险的权限是指会导致安全或隐私问题的权限,而普通的权限一般只是让应用获取应用域
以外的数据,可能没有隐私问题或只是轻微的隐私问题。对于普通的权限,我们在Manifest
中声明就好,而对于危险的权限,除了在Manifest
中声明以外,还需要在运行时申请。
在应用中,我们首先需要确认的,就是我们是否已经获取了我们所需要的权限。在API 23及其以上的版本中,Context
新增了一些方法用于处理权限相关的事情,当然,用ContextCompat
来替代Context
也是可以的。示例代码如下所示:
class PermissionsChecker {
private final Context context;
public PermissionsChecker(Context context) {
this.context = context;
}
public boolean lacksPermissions(String... permissions) {
for (String permission : permissions) {
if (lacksPermission(permission)) {
return true;
}
}
return false;
}
private boolean lacksPermission(String permission) {
return ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_DENIED;
}
}
对于方法ContextCompat#checkSelfPermission
来说,他要么返回PackageManager.PERMISSION_DENIED
,PackageManager.PERMISSION_GRANTED
。在Marshmallow
之前的Android版本,并不支持新的权限模型,checkSelfPermission()
的逻辑为直接返回PackageManager.PERMISSION_GRANTED
(如果有在Manifest
中定义的话)。
接下来我们编写一个MainActivity
类,在Activity中检查权限。并测试我们之前的代码。
public class MainActivity extends AppCompatActivity {
private static final String[] PERMISSIONS = new String[] {Manifest.permission.RECORD_AUDIO, Manifest.permission.MODIFY_AUDIO_SETTINGS};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
PermissionsChecker checker = new PermissionsChecker(this);
if (checker.lacksPermissions(PERMISSIONS)) {
Snackbar.make(toolbar, R.string.no_permissions, Snackbar.LENGTH_INDEFINITE).show();
}
.
.
.
}
}
在Marshmallow
之前的版本中,运行效果如下图所示:
在Marshmallow
及其以后的版本中,如果我们没有给定权限,则运行结果如下图所示:
Copyright© 2013-2019