存在的意义:
在Android 6.0之后(SDK >22)对于Runtime Permission严格控制,在第三方应用打开Runtime permission
的时候系统需要提醒用户,一般的情况下,Runtime permission 默认的权限是denied 的,应用需要获取应用就必要申请,在request 的时候会弹出一个跟用户交互的提示框,而这些就是GrantPermissionActivity 存在的意义。
下面来看下GrantPermissionsActivity 注册的信息:
<activity android:name=".permission.ui.GrantPermissionsActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:excludeFromRecents="true"
android:theme="@style/GrantPermissions"
android:visibleToInstantApps="true">
<intent-filter android:priority="1">
<action android:name="android.content.pm.action.REQUEST_PERMISSIONS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
先来看此Activity 注册的地方,在action 为android.content.pm.action.REQUEST_PERMISSIONS的时候触发。
再来看Activity.java 中的 requestPermissions:
public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
if (requestCode < 0) { //requestCode 必须为正数
throw new IllegalArgumentException("requestCode should be >= 0");
}
if (mHasCurrentPermissionsRequest) {
Log.w(TAG, "Can reqeust only one set of permissions at a time");
// Dispatch the callback with empty arrays which means a cancellation.
onRequestPermissionsResult(requestCode, new String[0], new int[0]);
return;
}
Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
mHasCurrentPermissionsRequest = true;
}
其中buildRequestPermissionsIntent 用来创建所需的intent:
public Intent buildRequestPermissionsIntent(@NonNull String[] permissions) {
if (ArrayUtils.isEmpty(permissions)) {
throw new IllegalArgumentException("permission cannot be null or empty");
}
Intent intent = new Intent(ACTION_REQUEST_PERMISSIONS);
intent.putExtra(EXTRA_REQUEST_PERMISSIONS_NAMES, permissions);
intent.setPackage(getPermissionControllerPackageName());
return intent;
}
首先传的action 就是GrantPermissionsActivity 所需要的action,这个intent 还将需要request 的permissions 带入,最关键的是指定了intent 所指向的package 为PackageInstaller。
到此,我们知道GrantPermissionsActivity 存在是为了处理requestPermissions。
分析源码:
详细的源码可以看:packages/apps/PackageInstaller/src/com/android/packageinstaller/permission/ui/GrantPermissionsActivity.java
主要偏向于UI 逻辑的控制,在这里主要总计几个重要部分:
1、mRequestedPermissions
这个是通过intent 的extra 传过来的,extra 的name 是PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES
mRequestedPermissions = getIntent().getStringArrayExtra(
PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES);
2、GrantPermissionsViewHandlerImpl
这个是用来更新activity UI的重要类
mViewHandler = new com.android.packageinstaller.permission.ui.handheld
.GrantPermissionsViewHandlerImpl(this, getCallingPackage())
.setResultListener(this);
3、setContentView(mViewHandler.createView());
acitivity 将GrantPermissionsViewHandlerImpl 中的createView 出来的View 显示出来。
4、mAppPermissions
mAppPermissions = new AppPermissions(this, callingPackageInfo, null, false,
new Runnable() {
@Override
public void run() {
setResultAndFinish();
}
});
这个AppPermissions 其实就是单个应用所拥有的所有的group permission 的统计,详细看
private final ArrayList<AppPermissionGroup> mGroups = new ArrayList<>();
5、showNextPermissionGroupGrantRequest
读取request 的group 相关信息,通过mViewHandler.updateUi 来更新UI。
mViewHandler.updateUi(groupState.mGroup.getName(), groupCount, currentIndex,
Icon.createWithResource(resources, icon), message,
groupState.mGroup.isUserSet());
6、onClick
public void onClick(View view) {
switch (view.getId()) {
case R.id.permission_allow_button:
if (mResultListener != null) {
view.performAccessibilityAction(
AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
mResultListener.onPermissionGrantResult(mGroupName, true, false);
}
break;
case R.id.permission_deny_button:
mAllowButton.setEnabled(true);
if (mResultListener != null) {
view.performAccessibilityAction(
AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
mResultListener.onPermissionGrantResult(mGroupName, false,
AppPermissionGroup.isStrictOpEnable() ? false : mShowDonNotAsk
&& mDoNotAskCheckbox.isChecked());
}
break;
case R.id.permission_more_info_button:
Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS);
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mAppPackageName);
intent.putExtra(ManagePermissionsActivity.EXTRA_ALL_PERMISSIONS, true);
mActivity.startActivity(intent);
break;
case R.id.do_not_ask_checkbox:
mAllowButton.setEnabled(!mDoNotAskCheckbox.isChecked());
break;
}
}
提示框的click 事件是在GrantPermissionsViewHandlerImpl 这里处理的,最后会通过callback 调回到GrantPermissionsActivity 中。
7、onPermissionGrantResult
public void onPermissionGrantResult(String name, boolean granted, boolean doNotAskAgain) {
GroupState groupState = mRequestGrantPermissionGroups.get(name);
if (groupState.mGroup != null) {
if (granted) {
groupState.mGroup.grantRuntimePermissions(doNotAskAgain,
groupState.affectedPermissions);
groupState.mState = GroupState.STATE_ALLOWED;
} else {
if (!AppPermissionGroup.isStrictOpEnable()) {
groupState.mGroup.revokeRuntimePermissions(doNotAskAgain,
groupState.affectedPermissions);
}
groupState.mState = GroupState.STATE_DENIED;
int numRequestedPermissions = mRequestedPermissions.length;
for (int i = 0; i < numRequestedPermissions; i++) {
String permission = mRequestedPermissions[i];
if (groupState.mGroup.hasPermission(permission)) {
EventLogger.logPermissionDenied(this, permission,
mAppPermissions.getPackageInfo().packageName);
}
}
}
updateGrantResults(groupState.mGroup);
}
if (!showNextPermissionGroupGrantRequest()) {
setResultAndFinish();
}
}
这里的callback 是从GrantPermissionsViewHandlerImpl 回来,确定执行grant 还是revoke。
总结:
其实核心思想就是想通过request 的时候跟用户进行交互,弹出一个提示框。CTA也是这样要求的,存在一个疑问就是,如果第三方应用直接调用check permission 之后grant permission,而不通过request 怎么办,那这样可能就没有交互了。详细可以看下
android grantRuntimePermission 详解。