SparseArray实现原理

SparseArray是Android中用于高效存储整数键和对象值的数据结构。它利用二分查找提高查找效率,通过维护两个数组确保键的有序性。在插入和获取元素时,SparseArray展现出比传统HashMap更好的性能,时间复杂度为O(log n)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SparseArray

SparseArray原理:
SparseArray采用两个数组,用来存放key以及value值的,核心思想是通过折半查找来找到key对应的位置,然后取出值,或者插入值!

二分查找

static int binarySearch(int[] array, int size, int value) {
    int lo = 0;
    int hi = size - 1;

    while (lo <= hi) {
        final int mid = (lo + hi) >>> 1;
        final int midVal = array[mid];

        if (midVal < value) {
            lo = mid + 1;
        } else if (midVal > value) {
            hi = mid - 1;
        } else {
            return mid;  // value found
        }
    }
    //没有找到,返回最后下标的相反值
    return ~lo;  // value not present
}

初始化

  public SparseArray(int initialCapacity) {
    if (initialCapacity == 0) {
        mKeys = EmptyArray.INT;
        mValues = EmptyArray.OBJECT;
    } else {
        mValues = ArrayUtils.newUnpaddedObjectArray(initialCapacity);
        mKeys = new int[mValues.length];
    }
    mSize = 0;
}
</
<think>我们正在分析Android12中PackageManagerService的checkUidPermission方法实现原理。根据引用[1]和引用[2]提供的背景,我们知道:-权限信息在应用安装时解析并存储在PMS中(引用[1])-系统服务通过检查调用者UID对应的权限来控制访问(引用[2])结合之前回答的核心流程,我们深入源码分析:###一、核心实现机制在Android12中,`checkUidPermission`的实现位于`PackageManagerService.java`中,主要步骤如下:1.**获取UID对应的设置对象(SettingBase)**:-通过`mSettings.getSettingBase(uid)`获取-如果对象不存在,直接返回`PERMISSION_DENIED`2.**权限状态检查**:-从SettingBase中获取`PermissionsState`对象-调用`permissionsState.checkPermission(permName,0)`检查基础授权状态3.**临时权限处理(Android12新增)**:-如果权限被授予,进一步检查是否属于临时授权(One-timepermission)-通过`isOneTimePermission()`检测权限标志位`FLAG_PERMISSION_ONE_TIME`-若为临时授权且应用在后台(通过AMS的`isUidInBackground(uid)`判断),则返回拒绝###二、源码详解(基于Android12源码)####1.入口方法```java//PackageManagerService.java@OverridepublicintcheckUidPermission(StringpermName,intuid){synchronized(mPackages){//线程安全同步锁returncheckUidPermissionLocked(permName,uid);}}```####2.核心逻辑方法```javaprivateintcheckUidPermissionLocked(StringpermName,intuid){//Step1:获取UID对应的设置对象finalSettingBasesetting=mSettings.getSettingBase(uid);if(setting==null){returnPackageManager.PERMISSION_DENIED;}//Step2:检查权限状态finalPermissionsStatepermissionsState=setting.getPermissionsState();if(permissionsState==null){returnPackageManager.PERMISSION_DENIED;}intresult=permissionsState.checkPermission(permName,0);//deviceId=0表示默认设备//Step3:Android12新增的临时权限处理if(result==PERMISSION_GRANTED){//检查是否为临时授权且应用在后台if(isPermissionOneTime(permName,uid)&&isUidInBackground(uid)){returnPERMISSION_DENIED;//临时授权在后台时拒绝}}returnresult;}```####3.关键支持方法**临时权限检测**:```javaprivatebooleanisPermissionOneTime(StringpermName,intuid){//查询权限标志位intflags=getPermissionFlags(permName,uid);return(flags&FLAG_PERMISSION_ONE_TIME)!=0;}```**后台状态检测**:```javaprivatebooleanisUidInBackground(intuid){//通过Binder调用ActivityManagerServicereturnActivityManager.getService().isUidInBackground(uid);}```###三、数据结构解析####1.`PermissionsState`内部实现权限状态存储在`PermissionsState`对象中,关键数据结构:```java//PermissionsState.javapublicclassPermissionsState{privatefinalArrayMap<String,PermissionData>mPermissions=newArrayMap<>();publicintcheckPermission(Stringpermission,intdeviceId){PermissionDatapermissionData=mPermissions.get(permission);if(permissionData==null){returnPERMISSION_DENIED;}PermissionStatestate=permissionData.getDeviceState(deviceId);return(state!=null&&state.isGranted())?PERMISSION_GRANTED:PERMISSION_DENIED;}}```####2.权限标志位存储在`packages.xml`中持久化存储:```xml<packagename="com.example.app"...><perms><itemname="android.permission.CAMERA"granted="true"flags="16384"/><!--16384=0x4000(FLAG_PERMISSION_ONE_TIME)--></perms></package>```###四、与其他服务的交互1.**与ActivityManagerService(AMS)交互**:-通过`ActivityManager.getService()`获取AMS的Binder代理-调用`isUidInBackground(uid)`判断UID是否处于后台状态2.**与AppOpsService联动**:-权限检查通过后,AppOpsService会记录操作历史-例如摄像头权限使用时更新`OP_CAMERA`计数###五、Android12的改进点1.**临时权限限制**:-新增`FLAG_PERMISSION_ONE_TIME`标志(值$0x4000$)-后台运行时临时权限自动失效2.**性能优化**:-使用细粒度锁`synchronized(mPackages)`代替类级锁-权限状态内存缓存减少Binder调用###总结流程图```mermaidgraphTDA[checkUidPermission]-->B[获取SettingBase对象]B-->C{对象存在?}C-->|No|D[返回拒绝]C-->|Yes|E[查询PermissionsState]E-->F{基础授权状态}F-->|拒绝|G[返回拒绝]F-->|授予|H{临时权限?}H-->|No|I[返回授予]H-->|Yes|J{应用在后台?}J-->|Yes|K[返回拒绝]J-->|No|L[返回授予]```§§相关问题§§1.Android12中临时授权标志如何影响`checkUidPermission`的结果?2.`PermissionsState`类如何管理多用户场景下的权限状态?3.PMS与ActivityManagerService在权限检查中的协作流程是怎样的?4.如何通过ADB命令模拟特定UID的权限授予状态?5.Android12新增的`NEARBY_DEVICES`权限在`checkUidPermission`中有何特殊处理?</think>### Android 12 PMS `checkUidPermission` 实现机制与源码分析 #### 一、核心实现原理 ```mermaid graph TD A[checkUidPermission] --> B[获取UID对应SettingBase] B --> C{对象存在?} C -->|不存在| D[返回PERMISSION_DENIED] C -->|存在| E[查询PermissionsState] E --> F{权限基础状态} F -->|未授予| G[返回PERMISSION_DENIED] F -->|已授予| H[检查临时授权标志] H -->|临时权限且后台| I[返回PERMISSION_DENIED] H -->|非临时或前台| J[返回PERMISSION_GRANTED] ``` #### 二、源码实现分析(基于Android 12源码) ##### 1. 入口方法 ```java // PackageManagerService.java @Override public int checkUidPermission(String permName, int uid) { synchronized (mPackages) { // 线程安全同步锁 return checkUidPermissionLocked(permName, uid); } } ``` - **线程安全**:通过`synchronized (mPackages)`保护共享资源[^1] - **核心委托**:调用内部方法`checkUidPermissionLocked()`执行实际检查 ##### 2. 核心检查逻辑 ```java private int checkUidPermissionLocked(String permName, int uid) { // Step1: 获取UID对应的设置对象 final SettingBase setting = mSettings.getSettingBase(uid); if (setting == null) return PERMISSION_DENIED; // Step2: 查询权限状态 final PermissionsState permissionsState = setting.getPermissionsState(); if (permissionsState == null) return PERMISSION_DENIED; // Step3: 基础权限检查 int result = permissionsState.checkPermission(permName, 0); // Step4: Android 12新增临时权限处理 if (result == PERMISSION_GRANTED) { if ((getPermissionFlags(permName, uid) & FLAG_PERMISSION_ONE_TIME) != 0 // 临时权限标志检查 && ActivityManager.getService().isUidInBackground(uid)) { // AMS后台状态检查 return PERMISSION_DENIED; // 临时权限在后台时拒绝 } } return result; } ``` #### 三、关键组件解析 ##### 1. `PermissionsState.checkPermission()` 实现 ```java // PermissionsState.java public int checkPermission(String permission, int deviceId) { PermissionData permissionData = mPermissions.get(permission); if (permissionData == null) return PERMISSION_DENIED; PermissionState state = permissionData.getDeviceState(deviceId); return (state != null && state.isGranted()) ? PERMISSION_GRANTED : PERMISSION_DENIED; } ``` - **数据结构**:使用`ArrayMap<String, PermissionData>`存储权限状态 - **性能优化**:查询时间复杂度$O(\log n)$ ##### 2. 临时权限处理(Android 12新增) ```java // 检测临时权限标志 private boolean isOneTimePermission(String permName, int uid) { int flags = getPermissionFlags(permName, uid); return (flags & FLAG_PERMISSION_ONE_TIME) != 0; // 0x4000标志位 } // 委托AMS检测后台状态 private boolean isUidInBackground(int uid) { return ActivityManager.getService().isUidInBackground(uid); } ``` - **临时授权标志**:`FLAG_PERMISSION_ONE_TIME = 0x00004000`[^1] - **后台状态检测**:通过Binder调用AMS服务(引用[4]) #### 四、权限数据存储机制 ##### 1. 持久化存储(packages.xml) ```xml <package name="com.example.app"> <perms> <item name="android.permission.CAMERA" granted="true" flags="0x4000"/> <!-- 0x4000表示临时授权 --> </perms> </package> ``` ##### 2. 内存数据结构 ```mermaid classDiagram class Settings { -mPermissionsState: PermissionsState } class PermissionsState { -mPermissions: ArrayMap~String, PermissionData~ +checkPermission() } class PermissionData { -mUserStates: SparseArray~PermissionState~ } class PermissionState { +granted: boolean +flags: int } Settings --> PermissionsState PermissionsState --> PermissionData PermissionData --> PermissionState ``` #### 五、与其他服务协作 1. **与AMS交互**: ```java // 通过Binder调用AMS ActivityManager.getService().isUidInBackground(uid); ``` - 实现原理:获取AMS代理对象(引用[4]),通过`isUidInBackground()`判断进程状态 2. **与安装流程联动**: - 权限在应用安装时解析(引用[1]): ```java // 解析AndroidManifest.xml PermissionInfo permission = new PermissionInfo(); permission.name = parser.getName(); mPermissions.add(permission); ``` #### 六、Android 12 核心改进 1. **临时权限模型**: - 新增`FLAG_PERMISSION_ONE_TIME`标志位 - 后台运行时自动撤销临时权限 2. **性能优化**: - 细粒度锁`synchronized (mPackages)` - 权限状态内存缓存 - 快速失败路径(优先检查空对象) 3. **安全增强**: - 严格的后台权限限制 - 与AMS实时状态同步
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值