权限界面实现原理
1. 数据来源:PackageManager
Android 系统的PackageManager负责管理所有安装的应用信息,包括权限数据。每个应用的AndroidManifest.xml中声明的权限信息会被系统解析并存储。
关键数据结构:
-
PackageInfo:包含应用的所有信息,如版本、图标、权限等 -
PermissionInfo:单个权限的详细信息 -
PermissionGroupInfo:权限组信息
获取应用权限的代码示例:
java
PackageManager pm = getPackageManager();
try {
// 获取指定应用的PackageInfo,包含权限信息
PackageInfo packageInfo = pm.getPackageInfo(
"com.example.app", // 应用包名
PackageManager.GET_PERMISSIONS // 标志:获取权限信息
);
// 获取应用声明的所有权限
String[] requestedPermissions = packageInfo.requestedPermissions;
if (requestedPermissions != null) {
for (String permission : requestedPermissions) {
try {
// 获取权限详细信息
PermissionInfo permissionInfo = pm.getPermissionInfo(permission, 0);
// 获取权限所属的组
PermissionGroupInfo groupInfo = pm.getPermissionGroupInfo(
permissionInfo.group,
0
);
Log.d("Permission", "权限: " + permissionInfo.loadLabel(pm));
Log.d("Permission", "组: " + groupInfo.loadLabel(pm));
Log.d("Permission", "描述: " + permissionInfo.loadDescription(pm));
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
2. 权限组的定义
Android 系统预定义了多个权限组,每个权限组包含若干相关权限。这些定义在系统源码中(frameworks/base/core/res/res/values/permission_groups.xml)。
常见权限组示例:
xml
<!-- 位置权限组 -->
<permission-group name="android.permission-group.LOCATION"
label="@string/permgroup_location"
description="@string/permgroup_location_desc"
icon="@mipmap/ic_perm_group_location" />
<!-- 包含在位置组中的权限 -->
<permission name="android.permission.ACCESS_FINE_LOCATION"
label="@string/permlab_accessFineLocation"
description="@string/permdesc_accessFineLocation"
protectionLevel="dangerous"
group="android.permission-group.LOCATION" />
<permission name="android.permission.ACCESS_COARSE_LOCATION"
label="@string/permlab_accessCoarseLocation"
description="@string/permdesc_accessCoarseLocation"
protectionLevel="dangerous"
group="android.permission-group.LOCATION" />
3. 界面实现:RecyclerView + 分组展示
系统设置中的权限界面通常使用RecyclerView实现,通过自定义Adapter将权限按组分类展示。
关键实现步骤:
-
数据分组:将获取的权限按组 ID 分类
-
视图类型:定义两种视图(组标题、权限项)
-
展开 / 折叠:实现组的展开 / 折叠功能
简化的 Adapter 代码示例:
java
public class PermissionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_GROUP = 0;
private static final int TYPE_PERMISSION = 1;
private List<Object> items = new ArrayList<>(); // 混合存储组和权限
private Context context;
private PackageManager pm;
public PermissionAdapter(Context context, PackageManager pm) {
this.context = context;
this.pm = pm;
}
// 设置数据并按组分类
public void setData(List<PermissionInfo> permissions) {
// 使用Map按组ID分组
Map<String, List<PermissionInfo>> groupMap = new HashMap<>();
for (PermissionInfo permission : permissions) {
String group = permission.group != null ? permission.group : "";
groupMap.computeIfAbsent(group, k -> new ArrayList<>()).add(permission);
}
// 转换为有序列表(组标题 + 权限项)
items.clear();
for (Map.Entry<String, List<PermissionInfo>> entry : groupMap.entrySet()) {
try {
// 获取组信息
PermissionGroupInfo groupInfo = pm.getPermissionGroupInfo(entry.getKey(), 0);
items.add(groupInfo);
items.addAll(entry.getValue());
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
notifyDataSetChanged();
}
@Override
public int getItemViewType(int position) {
return items.get(position) instanceof PermissionGroupInfo ? TYPE_GROUP : TYPE_PERMISSION;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// 创建不同类型的ViewHolder(组标题和权限项)
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
if (viewType == TYPE_GROUP) {
View view = inflater.inflate(R.layout.item_permission_group, parent, false);
return new GroupViewHolder(view);
} else {
View view = inflater.inflate(R.layout.item_permission, parent, false);
return new PermissionViewHolder(view);
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
// 绑定数据到ViewHolder
if (holder instanceof GroupViewHolder) {
GroupViewHolder groupHolder = (GroupViewHolder) holder;
PermissionGroupInfo groupInfo = (PermissionGroupInfo) items.get(position);
groupHolder.bind(groupInfo);
} else {
PermissionViewHolder permissionHolder = (PermissionViewHolder) holder;
PermissionInfo permissionInfo = (PermissionInfo) items.get(position);
permissionHolder.bind(permissionInfo);
}
}
@Override
public int getItemCount() {
return items.size();
}
// ViewHolder实现省略...
}
权限状态的获取
除了权限定义信息,界面还需要显示权限的当前状态(已授予 / 拒绝)。这需要通过PackageManager查询:
java
// 检查权限状态
int permissionState = pm.checkPermission(
"android.permission.CAMERA", // 权限名称
"com.example.app" // 应用包名
);
if (permissionState == PackageManager.PERMISSION_GRANTED) {
// 权限已授予
} else {
// 权限被拒绝
}
总结
| 组件 | 故事类比 | 技术实现 |
|---|---|---|
| PackageManager | 餐厅许可证数据库 | 获取应用权限信息 |
| 权限组 | 许可证分类展示墙 | 系统预定义的权限分组 |
| RecyclerView | 菜单展示系统 | 实现权限列表的分组展示 |
| 权限状态 | 许可证的当前有效性 | 通过 checkPermission 查询权限状态 |
通过这种方式,Android 系统将应用权限信息组织成用户友好的界面,让用户可以清晰地了解每个应用的权限请求和使用情况