从API 23版本开始,Google修改了应用权限管理的模型。官方也说了这种方式简化了应用的安装过程,不需要在安装和更新应用的时候去给它们授权了;这种Runtime授权模型,把权力给了用户,让用户清楚的管理各个应用的权限。(Google的工程师的事少了,用户也高兴了,程序员苦逼了)
API 23之前,应用权限申请,将应用所需的权限使用<uses-permission>元素在manifest.xml中陈列后即可,系统在应用安装时自动授权。
API 23之后,应用所需的权限依然使用<uses-permission>元素在manifest.xml中陈列,不过,Android把Permission分成了Normal和Dangerous两类。
- Normal permissions do not directly risk the user's privacy. If your app lists a normal permission in its manifest, the system grants the permission automatically.
Normal官方解释该类权限不涉及用户隐私。此类权限使用<uses-permission>元素在manifest.xml中陈列,在用户使用应用时系统自动授权。其中Normal类型包扩:
As of API level 23, the following permissions are classified as聽PROTECTION_NORMAL:
- ACCESS_LOCATION_EXTRA_COMMANDS
- ACCESS_NETWORK_STATE
- ACCESS_NOTIFICATION_POLICY
- ACCESS_WIFI_STATE
- BLUETOOTH
- BLUETOOTH_ADMIN
- BROADCAST_STICKY
- CHANGE_NETWORK_STATE
- CHANGE_WIFI_MULTICAST_STATE
- CHANGE_WIFI_STATE
- DISABLE_KEYGUARD
- EXPAND_STATUS_BAR
- GET_PACKAGE_SIZE
- INSTALL_SHORTCUT
- INTERNET
- KILL_BACKGROUND_PROCESSES
- MODIFY_AUDIO_SETTINGS
- NFC
- READ_SYNC_SETTINGS
- READ_SYNC_STATS
- RECEIVE_BOOT_COMPLETED
- REORDER_TASKS
- REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
- REQUEST_INSTALL_PACKAGES
- SET_ALARM
- SET_TIME_ZONE
- SET_WALLPAPER
- SET_WALLPAPER_HINTS
- TRANSMIT_IR
- UNINSTALL_SHORTCUT
- USE_FINGERPRINT
- VIBRATE
- WAKE_LOCK
- WRITE_SYNC_SETTINGS
- Dangerous permissions can give the app access to the user's confidential data. If your app lists a normal permission in its manifest, the system grants the permission automatically. If you list a dangerous permission, the user has to explicitly give approval to your app.
Dangerous 这一类权限因为涉及用的隐私, 此类权限也要使用<uses-permission>元素在manifest.xml中陈列;在每次使用时还需要用户授权。
第一步:检查授权
// Assume thisActivity is the current activity
int permissionCheck= ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.WRITE_CALENDAR);
第二步:申请授权
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED){
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.READ_CONTACTS)){
// Show an expanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
} else{
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
}
第三步:核实授权结果
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[],int[] grantResults){
switch (requestCode){
case MY_PERMISSIONS_REQUEST_READ_CONTACTS:{
// If request is cancelled, the result arrays are empty.
if(grantResults.length> 0
&& grantResults[0]== PackageManager.PERMISSION_GRANTED){
// permission was granted, yay! Do the
// contacts-related task you need to do.
}else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
return;
}
// other 'case' lines to check for other
// permissions this app might request
}
}
Dangerouspermissions and permission groups.
PermissionGroup | Permissions |
综上来看,对于程序员来说,改变主要在 Dangerous这个类权限的处理,只不过每次都写这些重复的代码,就有点……因此有个想法写个工具类,来简化这个实现。下面不多说了,直接上码:
工具类:
/** * 日 期:16/10/13 下午3:29 * 邮 箱:kexuan52@yeah.net */ public class PermissionTask { static SparseArray<PermissionTask> permissionTasks = new SparseArray<>(); static final int FIRST = 0; //已获得授权 static final int SUCCESS = 1; //授权成功 static final int FAILED = 2; //授权失败 static final int DIALOG = 3; //可能上一次用户禁用了改授权 Params params; public static class Params { int resultCode = -1; Activity activity; String permission; SparseArray<Task> mTask = new SparseArray(); } /*public*/ PermissionTask(Params params) { this.params = params; } public PermissionTask push () { if (permissionTasks.indexOfKey(params.resultCode) < 0) { permissionTasks.put(params.resultCode, this); } else { PermissionTask task = permissionTasks.get(params.resultCode); if (task == null || !task.equals(this)) { permissionTasks.put(params.resultCode, this); } } return this; } public static void pull(int resultCode) { if (permissionTasks.indexOfKey(resultCode) >= 0) { permissionTasks.remove(resultCode); } } public static void requestPermissions(int resultCode) { if (permissionTasks.indexOfKey(resultCode) >= 0) { PermissionTask task = permissionTasks.get(resultCode); if (task != null) { task.requestPermissions(); } } } public static void checkResult(int requestCode, String[] permissions, int[] grantResults) { if (permissionTasks.indexOfKey(requestCode) >= 0) { PermissionTask permissionTask = permissionTasks.get(requestCode); if (permissionTask == null) return; if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { permissionTask.execTask(SUCCESS); } else { permissionTask.execTask(FAILED); } } } boolean checkSelfPermission() { return ActivityCompat.checkSelfPermission(params.activity, params.permission) == PackageManager.PERMISSION_GRANTED; } void requestPermissions() { ActivityCompat.requestPermissions(params.activity,new String[]{params.permission}, params.resultCode); } boolean execTask(int key) { Task task = params.mTask.get(key); if (task != null) { task.todo(); return true; } return false; } public void check() { if (Build.VERSION.SDK_INT < 23) { execTask(SUCCESS); return; } if (!checkSelfPermission()) { if (ActivityCompat.shouldShowRequestPermissionRationale(params.activity, params.permission)) { if (!execTask(DIALOG)) { requestPermissions(); } } else { requestPermissions(); } } else { execTask(FIRST); } } public interface Task { void todo(); } public static class Builder { Params params; public Builder() { params = new Params(); } public Builder setActivity(Activity activity) { params.activity = activity; return this; } public Builder setPermission(String permission) { params.permission = permission; return this; } public Builder setResultCode(int resultCode) { params.resultCode = resultCode; return this; } public Builder setFirstTask(Task task) { params.mTask.put(FIRST, task); return this; } public Builder setSuccessTask(Task task) { params.mTask.put(SUCCESS, task); return this; } public Builder setDialogTask(Task task) { params.mTask.put(DIALOG, task); return this; } public Builder setFailedTask(Task task) { params.mTask.put(FAILED, task); return this; } public PermissionTask create() { PermissionTask task = new PermissionTask(params); return task; } } }
Activity流程
public class BaseActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); PermissionTask.checkResult(requestCode, permissions, grantResults); } }
测试demo
public class PermissionTestActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // setContentView(R.layout.activity_main); new PermissionTask.Builder() .setActivity(this) .setPermission(Manifest.permission.CAMERA) .setResultCode(100) .setFirstTask(()->{Log.e("task","First task");}) .setDialogTask(()->{ Log.e("task","Dialog task"); new AlertDialog.Builder(PermissionTestActivity.this) .setTitle("权限申请") .setMessage("申请使用摄像头") .setPositiveButton("OK", (dialog, which) -> { PermissionTask.requestPermissions(100); }) .setNegativeButton("Cancel",null) .create().show(); }) .setSuccessTask(()->{Log.e("task","SUCCESS task");}) .setFailedTask(()->{Log.e("task","FAILED task");}) .create().push().check(); } }