赞
踩
考察Android 系统中Pid 以及Uid的概念和理解
众所周知,Pid是进程ID,Uid是用户ID,只是Android和计算机不一样,计算机每个用户都具有一个Uid,哪个用户start的程序,这个程序的Uid就是那个那个用户,而Android中每个程序都有一个Uid,默认情况下,Android会给每个程序分配一个普通级别互不相同的 Uid,如果用互相调用,只能是Uid相同才行,这就使得共享数据具有了一定安全性,每个软件之间是不能随意获得数据的。而同一个application 只有一个Uid,所以application下的Activity之间不存在访问权限的问题。
Android中的PID全称为Process Identifier,来源于Linux中,在进程启动的时候系统会为进程分配一个独一无二的标识,进程销毁后PID会被系统回收,但是在Android中一般不会重新分配,后面的进程PID会比前面的进程的大。但是对同一个安卓应用可以具有多个PID,添加也很方便,只需要在声明类时指定进程名称即可,代码如下:
<activity android:name=".TestActivty" android:process="com.xiaohan.test"/>
添加很方便,但是不能随意使用,因为在同一个应用(PID)中,设计到程序之间最多的是线程间的通信,一旦独立出PID则涉及到进程间通信,类似于不同的两个应用,当然也可以通过上面的私有暴露和权限暴露的方式实现数据的通信,但是系统的开销较大。
Android中的UID一般认为是User Identifier,同样来源于Linux中。但是在Android中不太一样,Android最初的设计是单用户,所以UID并不是为了区别用户的,而是为了不同程序间进行数据共享。Android中每个程序都有一个Uid,默认情况下,Android会给每个程序分配一个普通级别互不相同的Uid,因此如果程序期望互相调用,只有Uid相同才行,这就使得共享数据具有了一定安全性,每个软件之间是不能随意获得数据的。而同一个application只有一个Uid,所以application下的Activity之间不存在访问权限的问题。
android中uid用于标识一个应用程序,uid在应用安装时被分配,并且在应用存在于手机上期间,都不会改变。一个应用程序只能有一个uid,多个应用可以使用sharedUserId 方式共享同一个uid,前提是这些应用的签名要相同。
在应用启动的时候,或者手机启动的时候,PackageManagerService都会去解析apk,并且为app分配一个UID,具体的流程如下(基于Android11):
PackageManagerService.java 中在PMS 构造函数初始化的时候会执行到一个函数scanDirTracedLI(),在scanDirTracedLI()中会执行scanDirLI(),然后会执行scanPackageTracedLI(),然后再执行scanPackageLI(),然后再执行addForInitLI(),然后执行scanPackageNewLI(),当然这个过程中会涉及到很多代码细节,建议大家去学习本章中关于PKMS部分的内容。在这个scanPackageNewLI函数中会执行mSettings.getSharedUserLPw(),去获取共享用户,如果没有就创建新的,大家看下面的代码:
SharedUserSetting getSharedUserLPw(String name, int pkgFlags, int pkgPrivateFlags, boolean create) throws PackageManagerException { SharedUserSetting s = mSharedUsers.get(name); if (s == null && create) { s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags); s.userId = acquireAndRegisterNewAppIdLPw(s); //核心代码 if (s.userId < 0) { // < 0 means we couldn't assign a userid; throw exception throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Creating shared user " + name + " failed"); } Log.i(PackageManagerService.TAG, "New shared user " + name + ": id=" + s.userId); mSharedUsers.put(name, s); } return s; }
上面的代码显示,如果没有为这个app创建SharedUserSetting 信息且需要创建一个,那么就创建一个,然后再调用acquireAndRegisterNewAppIdLPw函数为其分配UID。
private int acquireAndRegisterNewAppIdLPw(SettingBase obj) { // Let's be stupidly inefficient for now... final int size = mAppIds.size(); //code1 从0开始,找到第一个未使用的ID,此处对应之前有应用被移除的情况,复用之前的ID for (int i = mFirstAvailableUid; i < size; i++) { if (mAppIds.get(i) == null) { mAppIds.set(i, obj); return Process.FIRST_APPLICATION_UID + i; } } //code2 最多只能安装 9999 个应用 // None left? if (size > (Process.LAST_APPLICATION_UID - Process.FIRST_APPLICATION_UID)) { return -1; } mAppIds.add(obj); // code3 可以解释为什么普通应用的UID 都是从 10000开始的 return Process.FIRST_APPLICATION_UID + size; }
从上面我们可以看到,给APP分配的UID是遵守如下几个原则:1)从code1知道,id是可以复用的,当一个app卸载了,那么它的id会被后面安装的app复用,2)从code2得知,用户id不能超过19999,也就是说可以安装的app数不能超过9999个,否则安装的app没有uid分配;3)每个UID 都是一个大于 Process.FIRST_APPLICATION_UID 的数字,也就是都大于10000;
在Android中一个UID的对应的就是一个可执行的程序,程序在Android系统留存期间,其UID不变。 在Android中采用沙箱的概念来管理程序,不同的程序具有唯一的UID和PID,通过该UID来标识其所具有的“资源”,包括文件目录、数据库的访问、网络、传感器和日志等,和Linux一样,相互之间互不影响。
不同的应用程序一般是运行在不同的进程中,相互之间的“资源”不可以访问,但可以通过进程共享的方式,实现不同程序之间的数据访问主要是针对Activity、Service和ContentProvider,其实现方式按照权限暴露级别分为:完全暴露、权限提示暴露和私有暴露三种方式。
完全暴露
是指通过android:exported=”true”实现,在AndroidMaindfest.xml中申明Activity、Service或ContentProvider时,将该属性设置为true后,就表明该类允许外界的数据访问。如果在申明时添加了intentFilter属性,则默认exported就为true,此时也可强制的设置为fasle。如未做其他设置(exported/intentFilter),则默认exported为fasle,即不对外暴露。如下代码所示:
<activity android:name=".TestActivty" android:exported="true"/>
权限提示暴露
在AndroidMaindfest.xml中申明Activity、Service或ContentProvider时,添加了permission,表明如果其他应用需要访问该类时,需要在该应用添加该类声明时的权限, 声明私有的权限,如下所示。
<activity android:name=".TestActivty" android:permission="com.xiaohan.permission"/>
//添加访问的权限说明
<uses-permission android:name="com.xiaohan.permission"/>
私有暴露
不同于以上两种类的暴露方式,如果想对于不同应用之间可以互相访问任何数据,则需要通过sharedUserId+同一套签名的方式实现,只有这样才能运行在同一个进程中(同一个沙箱中)保证数据的相互访问。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xiaohan.test" //应用包名
android:sharedUserId="com.xiaohan.sharedUID" //暴露的唯一标识
android:sharedUserLabel="@string/app_name" //必须是引入资源文件中的字符串
android:versionCode="1"
android:versionName="1.1.0"
//安装位置,默认在内部目录,还包括auto:自动、 preferExternal:外置SD卡中
android:installLocation="internalOnly">
不同的应用具有唯一的UID,同一个UID可具有不同的PID;
针对不同的PID之间数据的暴露可采用私有暴露和权限暴露,针对不同的UID之间可通过完全暴露的方式;
如果一个应用是系统应用,则不需要其他应用暴露,便可直接访问该应用的数据。
我整理了一套Android面试题合集,除了以上面试题,还包含【Java 基础、集合、多线程、虚拟机、反射、泛型、并发编程、Android四大组件、异步任务和消息机制、UI绘制、性能调优、SDN、第三方框架、设计模式、Kotlin、计算机网络、系统启动流程、Dart、Flutter、算法和数据结构、NDK、H.264、H.265.音频编解码、FFmpeg、OpenMax、OpenCV、OpenGL ES】
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。