項目中因為SDK依賴庫的關系,需要将安卓SDK更新到API 23以上。更新以後就需要适配運作時授權。Google将權限分為兩類,一類是Normal Permissions,這類權限一般不涉及使用者隐私,是不需要使用者進行授權的,比如手機震動、通路網絡等;另一類是Dangerous Permission,一般是涉及到使用者隐私的,需要使用者進行授權,比如讀取sdcard、通路通訊錄等。對于23以上的版本,Dangerous Permission應用安裝以後需要運作時授權,顯式授予app相關權限。
1、運作時授權原理
标準的授權過程:
但這裡有個問題,那就是在系統授權彈窗環節,提醒框會有個不再提示的複選框,如果使用者點選不太提示,并拒絕授權,那麼再下次授權的時候,系統授權彈窗的提示框就不會在提示,是以我們很有必要需要自定義權限彈窗提示框,那麼流程圖就變成如下了。
對于這種情況,就需要程式處理,引導使用者進行授權處理。
2、授權處理的方式
方案一:進入首頁面優先處理授權,隻允許使用者授權成功才進行處理,防止權限不足引起一些異常。這種方式最大的優勢就是避免權限不足引起一些不可預見的異常CRASH,而且避免了使用者操作過程中經常出現授權的處理。
微信就是采取這種模式。
方案二:在頁面運作時根據需要授權。這種模式比較靈活,給使用者相對的選擇權。缺點是必須要規避所有的權限不足引起的異常,而且對于一些背景的操作,例如啟動後進行日志資訊采集需要讀取GPS、通訊錄等操作,如果使用者未授權的情況下,采集不到資訊。而且使用者不授予權限并且不在提示彈窗的情況下,基本上這些相關功能失效了。
項目中采用的“方案一+方案二”的方式。
3、授權處理
Activity授權處理分為兩部分,通過requestPermission請求權限,通過onRequestPermissionsResult 處理授權結果。處理顯得比較分散和麻煩。當然可以在基類中統一內建授權處理。
4、RxPermission封裝了授權處理,使用很友善。實作原理:
RxPermission利用Fragment也能申請授權并且能夠接收授權結果的原理,将授權處理內建在一個Fragment中。
1)RxPermission封裝一個專門用于授權的Fragment類RxPermissionsFragment,用于處理申請授權和授權結果的處理;
2)将RxPermissionsFragment對象挂載到Activity上;
3)Activity調用RxPermission申請授權,通過回調接收授權結果。
5、RxPermission使用方法:
請求授權:通過RxPermission的requestEach申請多個權限。
public PermissionManager(Activity activity) {
this.activity = activity;
this.rxPermissions = new RxPermissions(activity);
rxPermissions.setLogging(true);
}
public void setRequestPermissions(String[] permissions) {
this.requestPermissions = permissions;
this.permissionCount = null != permissions ? permissions.length : 0;
this.grantedMap = new HashMap<>();
}
/**
* 請求授權
*/
public void requestPermission() {
grantedMap.clear();
rxPermissions.requestEach(requestPermissions)
.subscribe(new Consumer<Permission>() {
/**
* 授權結果回調
*
* @param permission : 每一個請求的授權權限。
* @throws Exception
*/
@Override
public void accept(Permission permission) throws Exception {
if(permission.granted) {
//全部授權成功
Log.d(TAG, "requestPermission success: " + permission);
grantedMap.put(permission.name, true);
} else if(permission.shouldShowRequestPermissionRationale) {
//顯示授權彈窗
Log.d(TAG, "requestPermission failed: " + permission);
grantedMap.put(permission.name, false);
} else {
grantedMap.put(permission.name, false);
}
//授權完成時進行檢測,如果仍然有權限未擷取彈窗提示
if(grantedMap.size() == permissionCount) {
boolean isAllGranted = true;
//檢查是否仍然存在未授權的情況
Iterator it = grantedMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Boolean> entry = (Map.Entry<String, Boolean>) it.next();
if(!entry.getValue().booleanValue()) {
//仍然存在未授權成功的權限
isAllGranted = false;
break;
}
}
if(!isAllGranted) {
//仍然有權限未授權顯示授權彈窗
showPermissionDialog();
}
}
}
});
}
如果使用者不再顯示授權彈窗,引導使用者去授權
/**
* 打開設定頁面授權設定
*
*/
private void openSettingPermission() {
// vivo 點選設定圖示>加速白名單>我的app
// 點選軟體管理>軟體管理權限>軟體>我的app>信任該軟體
Intent appIntent = activity.getPackageManager().getLaunchIntentForPackage("com.iqoo.secure");
if(appIntent != null){
activity.startActivityForResult(appIntent, REQUEST_CODE_SETTING);
return;
}
// oppo 點選設定圖示>應用權限管理>按應用程式管理>我的app>我信任該應用
// 點選權限隐私>自啟動管理>我的app
appIntent = activity.getPackageManager().getLaunchIntentForPackage("com.oppo.safe");
if(appIntent != null){
activity.startActivityForResult(appIntent, REQUEST_CODE_SETTING);
return;
}
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
intent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
intent.setData(Uri.fromParts("package",
activity.getApplication().getPackageName(), null));
} else {
intent.setAction(Intent.ACTION_VIEW);
intent.setClassName("com.android.settings", "com.android.settings.InstalledAppDetails");
intent.putExtra("com.android.settings.ApplicationPkgName",
activity.getApplication().getPackageName());
}
activity.startActivityForResult(intent, REQUEST_CODE_SETTING);
}