Binder 跨进程通信框架
主要围绕服务注册、服务发现、服务调用
进程间通信有两个部分组成:
- 进程间通信的 核心 实现;也就是我们上面讲的客户端与服务端之间的通信逻辑(拷贝核心)。
- 维护进程通信 框架;比如复杂场景下多个进程同时互相通信,如何维护。
那么是第一部分难还是第二部分难呢?自然是第二部分相对复杂一些。
进程间通信实现简单,但是要实现进程间通信的机制,就非常难了。
进程间通信框架,不论 Binder 或其他,都绕不过三条定律:
- 服务注册。比如我要找某一个服务,但我自己肯定没办法找到服务,所以要求该服务主动注册;这就是服务注册。
- 服务查找/服务发现。
- 服务调用。
比如货拉拉是个平台,我运货需要一个大卡车,假如此时货拉拉没有大卡车,那么就没法叫大卡车,所以需要大卡车在平台注册,这就是服务注册。
之后我在货拉拉下单,指定了卡车的长度,之后货拉拉就把它平台注册过的所有卡车进行遍历,直到找到符合要求的卡车,这一步是服务查找/服务发现。
货拉拉遍历到了大卡车,才知道大卡车在哪里,之后把大卡车联系方式给到我,而我则通过联系方式去联系大卡车,这就叫服务调用。
而上面的这三个步骤,并不是 Linux 的 Binder 驱动做的;因为 ****Binder 只是实现了进程通信,但Binder 通信框架的维护工作并不是 Binder 驱动做的,而是 ServiceManager 做的。 Binder 在内核进程(Linux 内核),ServiceManager 在用户进程。
system_server 与 ServiceManager 的关系
Android 系统启动流程概述
Android是Linux系统上研制出来的,Linux内核必然占一个进程,该进程号为0。
安卓手机启动时候长按音量下键+电源键会进入到 Fastboot 模式,这个fastboot 就叫做引导程序;最开始是引导程序先运行(即 BootLoader 加载)。
如果正常运行则是通过引导程序默认去加载Linux内核(即 kernel加载)。
init 进程
之后启动 init 进程,init 进程不是内核进程,我们将它叫做守护进程;守护进程守护的目标是关键服务; 比如 AMS、PMS、system_server、ServiceManager 都不属于关键服务;手机主要功能是拨打电话,只有和电话相关的,如:铃声、网络、多媒体等属于关键服务;
下图中, PID 是进程号,PPID 是父进程号(Parent PID);可以看到 init 进程的 PPID 是 0 号进程,0 号进程则是内核进程。
init 进程有两个功能:
- 启动关键服务进程。
- 守护关键服务进程。
关键服务进程 如:屏幕触摸事件,假设我们通过指令杀死掉这个关键服务进程,会发现进程又被 init 拉起(生成新的PID),并且其 PPID 为 1 号进程(即 init 进程)。
但是,有些关键服务它不支持重新拉起,比如杀死zygote进程,那么手机就会关机重启。
注意:有些进程 PPID 为 1,但并不代表他就是关键服务进程,只是由 init 进程开启,并没有保活拉起的保护服务。
ServiceManager、system_server
引导程序开启内核进程(之后引导程序会销毁掉),内核进程开启 init 进程;init 进程开启谁呢?
我们在 adb shell 中查找 ServiceManager;PID 数字越低,代表该进程启动优先级越高(即启动的越早)。
可以看到 ServiceManager 进程的 PID 为 1290, 父进程 ID 是 1 号进程(即 init 进程);
再继续找 system_server 进程,PID 为 1459, 其父进程 ID 是 1294,并不是 init 进程;
我们找到 1294,发现 1294 是 zygote 进程;而 zygote 进程的父进程为 1,即 init 进程;
这样一来,ServiceManager 进程优先级 > zygote 进程优先级 > system_server 进程优先级。
那么启动顺序如下: init 开启ServiceManager 后, init 再开启 zygote,之后 zygote 开启 system_server。
system_server 负责开启一些服务,比如说:ams、pms、wms 等服务(ams、pms、wms 都只有一个),这些服务都运行在 system_server 进程中,那么服务如何提供给其他 APP 进程呢?后面再继续讲。
当 APP 想从 system_server 调用服务时,需要直接去找 ServiceManager 申请,ServiceManager 决定是否提供服务后,再让 system_server 去执行。这就是 APP、system_server、ServiceManager 之间的三角关系。
手写ServiceManager 进程通信框架方案分析
Android Binder 通信框架的原理涉及到 ServiceManager,先不讲源码;现在从手写 ServiceManager 的角度切入去讲解服务注册、服务查找/服务发现、服务调用。之后再从源码逐个对应角色。
我们继续使用两个不同进程的Activity,使他们实现进程通信。
方案一:为什么单例模式进行跨进程通信不可行
假如我们使用单例类,通过该单例类来实现数据的传输通信,单例类代码如下:
package com.example.binder;
import com.example.binder.bean.UserInfoBean;
public class XunYouSingleton {
private final static class XunYouSingletonHolder {
private static XunYouSingleton singleton = new XunYouSingleton();
}
public static XunYouSingleton newInstance() {
return XunYouSingletonHolder.singleton;
}
private UserInfoBean userInfo;
private XunYouSingleton() {
// 私有构造器,防止外部直接实例化
}
public UserInfoBean getUserInfo() {
return userInfo;
}
public void setUserInfo(UserInfoBean userInfo) {
this.userInfo = userInfo;
}
}
进程A通过setUserInfo
存储信息,进程B通过getUserInfo
获取消息,此时进程B访问的单例对象和进程A的单例对象不是同一个,会拿到空值的userInfo。但是由于单例在多进程中是没法使用,只有单进程模式下才有效。所以通过单例模式无法实现跨进程通信。
在 Android 的多进程环境中,每个进程都有自己的虚拟机和独立的内存空间,这导致每个进程会创建自己的单例实例,而无法共享数据
方案二:搭建跨进程框架实现跨进程通信-使用示例
跨进程通信框架必然少不了服务注册、服务发现(查找) 、服务调用这几个步骤;我们的设计思路则是,继续保留方案一的单例模式,但是单例跨进程是无法使用的;所以我们要设计一个ServiceManager成员,而它的职责就是进行 服务的注册、查找、发现。
服务注册:比如我们在进程A中,通过ServiceManager注册了XunYouSingleton
的单例对象:
ServiceManager.getDefault.add()
注册服务
服务发现:之后想在进程B读取该数据,那么先进行XunYouSingleton
的查找,来获取XunYouSingleton
服务:
服务调用: 之后调用查找到的服务(服务调用),来获取通信数据:
我们接下来就按照这种思路来实现属于我们自己的ServiceManager,实现跨进程通信。
System_Server源码角度了解服务注册
前面提到,init进程会调用Servicemanager进程,Servicemanger进程开启system_server进程。
而system_server进程的作用是启动服务,如ams、pms、wms等;
先看systemserver系统源码:
📎SystemServer.java (API-29)
SystemServer掌握很多资源,但是它听从ServiceManager的指挥。
SystemServer有main()函数,它由zygote进程孵化拉起。
在SystemServer的构造函数中,没有看到什么有效信息;
直接来到run函数,看最后面几行代码,找到startBootstrapServices()
SystemServer在startBootstrapServices()
函数启动了系统所需的关键服务。
翻译:
启动系统启动所需的一小堆关键服务。
这些服务具有复杂的相互依赖关系,这就是为什么我们在这里将它们全部初始化的原因。
除非你的服务也与这些依赖关系纠缠在一起,否则它应该在其他函数中进行初始化。
所以SystemServer在被启动后的流程为:main()->run();run()函数中执行了startBootstrapServices()
函数。
startBootstrapServices源码分析
进入到startBootstrapServices()
源码;
AMS服务开启
首先看到ActivityManagerService,即AMS;AMS的作用是管理Activity生命周期
ActivityTaskManagerService atm = mSystemServiceManager.startService(ActivityTaskManagerService.Lifecycle.class).getService();
mActivityManagerService = ActivityManagerService.Lifecycle.startService(mSystemServiceManager, atm);
mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
mActivityManagerService.setInstaller(installer);
mWindowManagerGlobalLock = atm.getGlobalLock();
创建ATMS服务
ActivityTaskManagerService atm = mSystemServiceManager.startService(ActivityTaskManagerService.Lifecycle.class).getService();
首先第一行代码可以分为两部分 ①mSystemServiceManager.startService(ActivityTaskManagerService.Lifecycle.class)
②.getService()
;
它先通过SystemManager开启了ActivityTaskManagerService.Lifecycle.class
服务;我们分别了解下mSystemServiceManager.startService
和
ActivityTaskManagerService.Lifecycle.class
是什么,做了什么;
mSystemServiceManager.startService
startService
只做一件事情,反射实例化对象;通过Class
获取到全类名,之后通过全类名获取构造参数后constructor.newInstance
反射构造实例化对象。最后开启该服务并把对象返回。
实例化的目的只有一个,唤起调用实例对象的构造方法;
public <T extends SystemService> T startService(Class<T> serviceClass) {
try {
final String name = serviceClass.getName();
Slog.i(TAG, "Starting " + name);
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartService " + name);
// Create the service.
if (!SystemService.class.isAssignableFrom(serviceClass)) {
throw new RuntimeException("Failed to create " + name
+ ": service must extend " + SystemService.class.getName());
}
final T service;
try {
Constructor<T> constructor = serviceClass.getConstructor(Context.class);
service = constructor.newInstance(mContext);
} catch (InstantiationException ex) {
throw new RuntimeException("Failed to create service " + name
+ ": service could not be instantiated", ex);
} catch (IllegalAccessException ex) {
throw new RuntimeException("Failed to create service " + name
+ ": service must have a public constructor with a Context argument", ex);
} catch (NoSuchMethodException ex) {
throw new RuntimeException("Failed to create service " + name
+ ": service must have a public constructor with a Context argument", ex);
} catch (InvocationTargetException ex) {
throw new RuntimeException("Failed to create service " + name
+ ": service constructor threw an exception", ex);
}
startService(service);
return service;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
}
ActivityTaskManagerService.Lifecycle.class
我们来到ActivityTaskManagerService.Lifecycle.class
的构造方法;可以看到ActivityTaskManagerService.Lifecycle
是ActivityTaskManagerService
的一个内部类。
ActivityTaskManagerService.Lifecycle
在构造函数中,很直接的创建了一个ActivityTaskManagerService实例;由此可见,ActivityTaskManagerService.Lifecycle
就是一个包装类,它包装了一个ActivityTaskManagerService。
.getService()
我们只要通过这个包装类对象调用.getService()
就能得到AMS。
public static final class Lifecycle extends SystemService {
private final ActivityTaskManagerService mService;
public Lifecycle(Context context) {
super(context);
mService = new ActivityTaskManagerService(context);
}
@Override
public void onStart() {
publishBinderService(Context.ACTIVITY_TASK_SERVICE, mService);
mService.start();
}
@Override
public void onUnlockUser(int userId) {
synchronized (mService.getGlobalLock()) {
mService.mStackSupervisor.onUserUnlocked(userId);
}
}
@Override
public void onCleanupUser(int userId) {
synchronized (mService.getGlobalLock()) {
mService.mStackSupervisor.mLaunchParamsPersister.onCleanupUser(userId);
}
}
public ActivityTaskManagerService getService() {
return mService;
}
}
所以ActivityTaskManagerService atm = mSystemServiceManager.startService(ActivityTaskManagerService.Lifecycle.class).getService();
中getService()
返回的对象就是ActivityTaskManagerService
。
创建AMS服务
来到第二行代码:
mActivityManagerService = ActivityManagerService.Lifecycle.startService(mSystemServiceManager, atm);
创建了ActivityTaskManagerService
后,接下来才是开启AMS;我们去ActivityManagerService.Lifecycle
类源码中,它和ActivityTaskManagerService
是类似的结构,都是通过lifecycle包装类来构建的。
首先来到ActivityManagerService.Lifecycle.startService
函数源码中:
startService
有两个参数,一个是前面创建的ATMS实例对象,另一个是SystemServiceManager。
ActivityManagerService.Lifecycle持有ATMS实例后,再使用SystemServiceManager去创建AMS实例对象。之后返回创建的Service实例对象。
注:在API 28版本中,是没有ATMS这步的,而是直接创建AMS。
API 29之后才加入了ATMS,ATMS其实是AMS职责的分割,ATMS专注于管理活动的任务堆栈、前后台调度和多窗口等现代UI需求。
public static final class Lifecycle extends SystemService {
private final ActivityManagerService mService;
private static ActivityTaskManagerService sAtm;
public Lifecycle(Context context) {
super(context);
mService = new ActivityManagerService(context, sAtm);
}
public static ActivityManagerService startService(
SystemServiceManager ssm, ActivityTaskManagerService atm) {
sAtm = atm;
return ssm.startService(ActivityManagerService.Lifecycle.class).getService();
}
@Override
public void onStart() {
mService.start();
}
@Override
public void onBootPhase(int phase) {
mService.mBootPhase = phase;
if (phase == PHASE_SYSTEM_SERVICES_READY) {
mService.mBatteryStatsService.systemServicesReady();
mService.mServices.systemServicesReady();
} else if (phase == PHASE_ACTIVITY_MANAGER_READY) {
mService.startBroadcastObservers();
} else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
mService.mPackageWatchdog.onPackagesReady();
}
}
@Override
public void onCleanupUser(int userId) {
mService.mBatteryStatsService.onCleanupUser(userId);
}
public ActivityManagerService getService() {
return mService;
}
}
使AMS持有SystemServiceManager
第三行代码,SystemService调用AMS对象的setSystemServiceManager()
函数,将ServiceManager实例传给AMS:
mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
AMS初始化电源管理
前面已经创建了AMS实例,继续往下走,找到mActivityManagerService.initPowerManagement();
AMS设置系统进程setSystemProcess
再往下找AMS,看到mActivityManagerService.setSystemProcess();
这行代码;
直接进入函数内部源码,只需要了解第一行代码ServiceManager.addService(...)
这行代码将我们当前的AMS服务注册到了ServiceManager。而addService()
则涉及到跨进程通信:
public void setSystemProcess() {
try {
// 1. 将 AMS 注册到 ServiceManager 中
ServiceManager.addService(Context.ACTIVITY_SERVICE, this, /* allowIsolated= */ true,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);
ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
ServiceManager.addService("meminfo", new MemBinder(this), /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_HIGH);
ServiceManager.addService("gfxinfo", new GraphicsBinder(this));
ServiceManager.addService("dbinfo", new DbBinder(this));
if (MONITOR_CPU_USAGE) {
ServiceManager.addService("cpuinfo", new CpuBinder(this),
/* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
}
ServiceManager.addService("permission", new PermissionController(this));
ServiceManager.addService("processinfo", new ProcessInfoService(this));
ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
"android", STOCK_PM_FLAGS | MATCH_SYSTEM_ONLY);
mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader());
synchronized (this) {
ProcessRecord app = mProcessList.newProcessRecordLocked(info, info.processName,
false,
0,
new HostingRecord("system"));
app.setPersistent(true);
app.pid = MY_PID;
app.getWindowProcessController().setPid(MY_PID);
app.maxAdj = ProcessList.SYSTEM_ADJ;
app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
mPidsSelfLocked.put(app);
mProcessList.updateLruProcessLocked(app, false, null);
updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
}
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(
"Unable to find android system package", e);
}
// Start watching app ops after we and the package manager are up and running.
mAppOpsService.startWatchingMode(AppOpsManager.OP_RUN_IN_BACKGROUND, null,
new IAppOpsCallback.Stub() {
@Override public void opChanged(int op, int uid, String packageName) {
if (op == AppOpsManager.OP_RUN_IN_BACKGROUND && packageName != null) {
if (mAppOpsService.checkOperation(op, uid, packageName)
!= AppOpsManager.MODE_ALLOWED) {
runInBackgroundDisabled(uid);
}
}
}
});
}
手写IPC进程通信框架,了解服务注册、发现、调用
接下来我们通过手写IPC进程通信框架来了解服务注册、发现、调用流程。
IPC进程通信框架的主要内容就是三个部分:服务注册、发现、调用。我们一起来简单实现,以帮助更好的了解服务注册、发现、调用这三个过程。
代码地址: https://gitee.com/linxunyou/android_binder_123
手写IPC进程通信框架之服务注册
我们开始自己实现属于自己的服务注册。
服务注册需要ServiceManager和Service;我们创建两个类,一个是服务管理类ServiceManager,另一个是要被注册的服务 MyService;
package com.example.ipccore;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import androidx.annotation.Nullable;
public class TuYaService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
注意ServiceManager是个单例,在ServiceManager中创建服务注册的方法 addService()
;
package com.example.ipcxunyou;
public class ServiceManager {
private static final ServiceManager ourInstance = new ServiceManager();
public static ServiceManager getDefault(){
return ourInstance;
}
//服务注册
public void add(String key,Class<?> clazz){
}
}
接下来看服务注册怎么做,服务注册的本质就是将服务(Service)登记到服务管理中心(ServiceManager) ,每个服务有对应的功能(key)。所以ServiceManager为了方便管理,就有做一个记录,当需要某个服务时,ServiceManager则根据key去查找对应的服务。
服务管理中心为了便于管理服务,它做了个缓存中心;而缓存中心有三张表;
表1记录了,类名对应的类class,我们通过类名获取对应服务的类class。
提供服务的人不会主动联系需要服务者;而是要由需要服务者主动找提供服务的人,提供服务。这个是服务发现。
表2记录,类名所对应服务可提供的所有函数。(value为一个函数集合)
表3记录,类名所对应的所有该类实例对象。
我们创建缓存中心CacheCenter
,在其内部创建三张表:类表、实例表、方法表。
package com.example.ipccore;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
public class CacheCenter {
private static final CacheCenter ourInstance = new CacheCenter();
public static CacheCenter getDefault(){
return ourInstance;
}
//类表
private final ConcurrentHashMap<String,Class<?>> mClassMap;
//方法表
private final ConcurrentHashMap<String, ConcurrentHashMap<String, Method>> mAllMethodMap;
//实例对象表
private final ConcurrentHashMap<String,Object> mInstanceObjectMap;
public CacheCenter() {
mClassMap = new ConcurrentHashMap<>();
mInstanceObjectMap = new ConcurrentHashMap<>();
mAllMethodMap = new ConcurrentHashMap<>();
}
}
在ServiceManager中获取缓存中心CacheCenter实例:并将注册到ServiceManger的服务登记到缓冲中心。
public class ServiceManager {
private static final ServiceManager ourInstance = new ServiceManager();
CacheCenter cacheCenter=CacheCenter.getDefault();
public static ServiceManager getDefault(){
return ourInstance;
}
//服务注册
public void add(String key,Class<?> clazz){
cacheCenter.register(key,clazz);
}
}
CacheCenter创建register
方法:在register中进行 类表、方法表的存储;
package com.example.ipccore;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
public class CacheCenter {
private static final CacheCenter ourInstance = new CacheCenter();
public static CacheCenter getDefault(){
return ourInstance;
}
//类表
private final ConcurrentHashMap<String,Class<?>> mClassMap;
//方法表
private final ConcurrentHashMap<String, ConcurrentHashMap<String, Method>> mAllMethodMap;
//实例对象表
private final ConcurrentHashMap<String,Object> mInstanceObjectMap;
public CacheCenter() {
mClassMap = new ConcurrentHashMap<>();
mInstanceObjectMap = new ConcurrentHashMap<>();
mAllMethodMap = new ConcurrentHashMap<>();
}
public void register(String key, Class<?> clazz) {
//第一步,先将class注册到类表中去。
registerClass(key,clazz);
//第二步,注册方法
registerMethod(key,clazz);
//第三张表需要在 服务注册时将服务对象初始化吗?
// 答:不能,只有当发生服务调用时才进行初始化。否则有些服务一辈子不会被调用导致性能浪费。
//所以在服务注册时,不初始化服务实例对象。而是在服务调用时进行服务的实例化。
}
private void registerMethod(String key, Class<?> clazz) {
//遍历类中方法进行记录
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
ConcurrentHashMap<String, Method> map = mAllMethodMap.get(clazz.getName());
//首次没有map则新建
if (map==null) {
map=new ConcurrentHashMap<>();
mAllMethodMap.put(clazz.getName(),map);
}
//key:能不能直接用方法名作为Key?不能,存在重载函数。
// 所以key需要做方法签名
String methodParameters = getMethodParameters(method);
map.put(methodParameters,method);
}
}
//无参则是method.getName值,有参则是 method.getName+"_"+参数类型。
//类似于JNI的函数签名
private String getMethodParameters(Method method) {
StringBuilder result = new StringBuilder();
result.append(method.getName());
Class<?>[] classes = method.getParameterTypes();
int length=classes.length;
if (length==0){
return result.toString();
}
for (int i = 0; i < length; i++) {
result.append("_").append(classes[i].getName());
}
return result.toString();
}
//将Class存到类表中
private void registerClass(String key, Class<?> clazz) {
mClassMap.put(key, clazz);
}
}
服务注册就是做上述的事情,但真实的源码中会比我们写的这种难,源码中并非使用hashmap 而是使用红黑树;该红黑树处于native驱动层实现,不易了解。
使用:创建服务并完成注册
接下来我们创建服务并使用上面的ServiceManager进行服务的注册。
比如我们有个Service,我们使用它来进行进程间通信,跨进程通信主要目的是通信,通信是使用序列化数据,我们这边就使用String类型进行传输;同样是基于Binder进行通信;创建一个AIDL,创建一个request
接口。进程A通过Request发送消息给进程B,B将信息作为Request返回值给到A。
我们写的跨进程通讯,就类似于一种http请求:服务器准备好接口,由客户端调用其接口得到返回数据。
所以我们的步骤如下:
- 创建AIDL
// ITuyaBinderInterface.aidl
package com.example.ipccore;
interface ITuyaBinderInterface {
String transact(String request);
}
2. 声明创建SecondActivity,并指定在不同进程
3. 在Activity中通过ServiceManager服务调用Open函数
- 创建Open函数,使调用端Activity与服务端建立通讯
public class ServiceManager {
private static final ServiceManager ourInstance = new ServiceManager();
CacheCenter cacheCenter=CacheCenter.getDefault();
public static ServiceManager getDefault(){
return ourInstance;
}
ITuyaBinderInterface tuyaBinderInterface;
public void open(Context context){
open(context,null);
}
private void open(Context context, String packageName) {
bind(context.getApplicationContext(),packageName,TuYaService.class);
}
private void bind(Context applicationContext, String packageName, Class<ServiceManager> serviceManagerClass) {
TuyaServiceConnection tuyaServiceConnection = new TuyaServiceConnection();
Intent intent = new Intent(applicationContext, serviceManagerClass);
applicationContext.bindService(intent,tuyaServiceConnection,Context.BIND_AUTO_CREATE);
}
private class TuyaServiceConnection implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
tuyaBinderInterface = ITuyaBinderInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
//服务注册
public void add(String key,Class<?> clazz){
cacheCenter.register(key,clazz);
}
}
5. 创建注解
package com.example.ipcxunyou;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ClassId {
String value();
}
@ClassId
注解
- 用于标记类,并为其赋予一个唯一标识符 (
value
)。 - 目标是在运行时可以通过反射解析该注解,找到对应的实现类或接口,用于动态加载或实例化。
功能举例: 通过 @ClassId
注解标记的值 com.example.ipcxunyou.Stub
,可以在运行时反射获取并识别 Proxy
接口,可能用于服务注册或查找。
- 创建IUserManager与UserManager
package com.example.ipcxunyou;
import com.example.ipcxunyou.bean.UserInfo;
//提供给客户端的服务接口
@ClassId("com.example.ipcxunyou.Stub")
public interface Proxy {
public UserInfo getUser();
public void setUser(UserInfo user);
}
IUserManager
接口
- 注解:
-
- 使用
@ClassId("com.example.ipcxunyou.UserManager")
标记,表明这是一个服务类。 - 可能的用途是在客户端通过注解标识动态生成或查找到对应的远程服务实现。
- 使用
- 接口设计:
-
- 提供了两个方法:
UserInfo getUser()
:获取用户信息。void setUser(UserInfo user)
:设置用户信息。
- 提供了两个方法:
功能作用:
- 作为客户端的服务接口。
- 在实际实现中,可能会通过动态代理生成此接口的具体实现类,使客户端透明地调用远程服务。
package com.example.ipcxunyou;
import com.example.ipcxunyou.bean.UserInfo;
public class UserManager implements IUserManager {
private static UserManager sInstance=null;
public static synchronized UserManager getInstance() {
if (sInstance == null) {
sInstance=new UserManager();
}
return sInstance;
}
private UserManager(){
}
@Override
public UserInfo getUser() {
return null;
}
@Override
public void setUser(UserInfo user) {
}
}
UserManager
类
设计模式:单例模式
-
UserManager
是一个单例类,提供了线程安全的getInstance()
方法确保只有一个实例。- 通常在 IPC 框架中,
UserManager
是服务端用于接收和处理客户端请求的组件。
功能作用:
- 在服务端,通过
UserManager
类提供全局唯一的服务实例,用于管理实际的服务实现。
潜在用途:
- 可能会在
UserManager
类中添加具体逻辑,比如:
-
- 处理请求的队列。
- 对服务接口(如
IUserManager
)的具体实现进行包装。
IUserManager等价于Proxy;UserManager等价于Stub。客户端在访问时,获取的是Proxy,只能调用Proxy中声明的函数功能。
- 在MainActivity中注册UserManager,之后从SecondActivity跨进程访问UserManager服务实例。
手写IPC进程通信框架之服务发现
服务发现之请求发送
- 在Activity中查找服务:
- 在ServiceManager中实现getInstance服务查找:
我们在B进程中,想要访问A进程中注册的服务,那么此时B进程中是没有IUserManager服务的,所以要如何发现服务呢?答案是通过发送信号来调用。
我们把所有需要调用该方法的参数信息,全部发送给服务器;所需信息有:类、方法名、方法参数;且方法参数是不固定的。我们需要将这些信息转为一个可序列化的数据进行发送;比如String类型。我们这边使用Json进行发送信息。
服务发现:实际上是把需要调用的服务,以JSON的形式发送过去,类似一种HTTP请求(Handler也是类似如此)。 - 那么我们发送的Json消息的格式为:
{
"type": 1,//服务发现 or 服务调用
"className":"com.example.ipcxunyou",//类名
"methodName":"getInstance",//函数名
"requestParamters":[{"parameterClassName":"java.lang.String","parameterValue":"李四"}],//参数类型与参数值
}
4. 生成JavaBean类:
package com.example.ipccore.request;
import java.util.List;
/**
* JavaBean类,用于封装请求信息。
*/
public class RequestBean {
/**
* 请求的类名
*/
private String className;
/**
* 请求的方法名
*/
private String methodName;
/**
* 请求参数的列表
*/
private List<RequestParameter> requestParamters;
// Getter 和 Setter 方法
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public List<RequestParameter> getRequestParamters() {
return requestParamters;
}
public void setRequestParamters(List<RequestParameter> requestParamters) {
this.requestParamters = requestParamters;
}
}
package com.example.ipccore.request;
public class RequestParameter {
/**
* 参数类型的全类名
*/
private String parameterClassName;
/**
* 参数的值
*/
private String parameterValue;
// Getter 和 Setter 方法
public String getParameterClassName() {
return parameterClassName;
}
public void setParameterClassName(String parameterClassName) {
this.parameterClassName = parameterClassName;
}
public String getParameterValue() {
return parameterValue;
}
public void setParameterValue(String parameterValue) {
this.parameterValue = parameterValue;
}
}
5. 客户端发送请求信息给服务端,从而查找服务:
public <T>T getInstance(Class<T> clazz,Object... parameters) {
//服务发现:实际上是把需要调用的服务,以JSON的形式发送过去
sendRequest(clazz,null,parameters, TuYaService.TYPE_GET);
}
Gson gson=new Gson();
//服务发现 type=1;服务调用 type=2。
private <T> void sendRequest(Class<T> clazz, Method method, Object[] parameters,int type) {
//构建请求参数
RequestBean requestBean = new RequestBean();
requestBean.setType(type);
if (parameters!=null && parameters.length>0){
RequestParameter[] requestParameters = new RequestParameter[parameters.length];
for (int i = 0; i < parameters.length; i++) {
Object parameter = parameters[i];
String parameterClassName = parameter.getClass().getName();
String parameterValue=gson.toJson(parameter);
RequestParameter requestParameter = new RequestParameter(parameterClassName, parameterValue);
requestParameters[i]=requestParameter;
}
requestBean.setRequestParameters(requestParameters);
}
String className = clazz.getAnnotation(ClassId.class).value();
String methodName = method==null?"getInstance":method.getName();
requestBean.setClassName(className);
requestBean.setMethodName(methodName);
String request = gson.toJson(requestBean);
//通过 copy_from_user 拷贝到物理内存。
// 再通过mmap函数,将数据传递到Service。
//当前函数执行在SecondActivity所在的F进程,但是执行transact函数会调用主进程的Service,
// 所以此时transact在主进程
try {
//传递给服务端进程,调用服务端的transact函数
tuyaBinderInterface.transact(request);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
{"className":"com.example.ipcxunyou.UserManager","methodName":"getInstance","requestParameters":[{"parameterClassName":"java.lang.Integer","parameterValue":"123"},{"parameterClassName":"java.lang.String","parameterValue":""nihao""}],"type":1}
- 在服务端
TuYaService.transact()
中处理收到的消息,对其反序列化,并创建makeParameterObject
函数解析得到入参;
package com.example.ipccore;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import com.example.ipccore.request.RequestBean;
import com.example.ipccore.request.RequestParameter;
import com.google.gson.Gson;
public class TuYaService extends Service {
//服务端才有 BBBinder
BBBinder bbBinder;
//服务发现
public static final int TYPE_GET=1;
//服务调用
public static final int TYPE_INVOKE=2;
Gson gson=new Gson();
CacheCenter cacheCenter = CacheCenter.getInstance();
@Override
public IBinder onBind(Intent intent) {
return new ITuyaBinderInterface.Stub() {
@Override
public String transact(String request) throws RemoteException {
//反序列化数据,将JSON数据转为JavaBean
RequestBean requestBean = gson.fromJson(request, RequestBean.class);
//判断是服务发现还是服务调用
switch (requestBean.getType()){
case TYPE_GET:
//服务发现,最终是构建服务对应的实例对象。
// 即:调用getInstance,将方法反射到method,并invoke执行。
//解析拿到入参。
Object[] parameters = makeParameterObject(requestBean);
//从方法表中拿到method(需要先进行服务注册,注册类表与方法表)。服务发现是查找 getInstance()获取实例
Method method = cacheCenter.getMethod(requestBean);
//调用Method,这里是getInstance,得到实例对象。
Object object = bbBinder.onTransact(null,method, parameters);
//将初始化后的实例对象存起来,存到实例对象表中
cacheCenter.putObject(requestBean.getClassName(),object);
break;
case TYPE_INVOKE:
//服务调用
break;
}
return "你好呀baby";
}
};
}
private Object[] makeParameterObject(RequestBean requestBean){
//调用函数的参数
Object[] mParameters = null;
RequestParameter[] requestParameters = requestBean.getRequestParameters();
if (requestParameters!=null && requestParameters.length>0){
mParameters=new Object[requestParameters.length];
for (int i = 0; i < requestParameters.length; i++) {
RequestParameter requestParameter = requestParameters[i];
Class<?> clazz = cacheCenter.getClassType(requestParameter.getParameterClassName());
mParameters[i]=gson.fromJson(requestParameter.getParameterValue(),clazz);
}
}else {
mParameters = new Object[0];
}
return mParameters;
}
}
public Method getMethod(RequestBean requestBean) {
ConcurrentHashMap<String, Method> map = mAllMethodMap.get(requestBean.getClassName());
if (map!=null){
String key = getMethodParameters(requestBean);
return map.get(key);
}
return null;
}
public static String getMethodParameters(RequestBean requestBean) {
StringBuilder result = new StringBuilder();
result.append(requestBean.getMethodName());
if (requestBean.getRequestParameters()!=null && requestBean.getRequestParameters().length>0){
return result.toString();
}
int length=requestBean.getRequestParameters().length;
if (length==0){
return result.toString();
}
for (int i = 0; i < length; i++) {
result.append("_").append(requestBean.getRequestParameters()[i].getParameterClassName());
}
return result.toString();
}
通过BBBInder反射获取getInstance()函数得到实例对象。BBBinder内部主要做的反射工作。
在TuyaService中声明BBBinder,并通过BBBinder.onTransact
调用Method,得到实例对象。
package com.example.ipccore;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class BBBinder {
/**
* 构建对象
* @param method
* @param parameters
* @return
*/
public Object onTransact(Object instance,Method method, Object[] parameters) {
Object object;
try {
object=method.invoke(instance,parameters);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
return object;
}
}
通过CacheCenter.putObject()
将初始化的实例对象存储到第三张表(实例对象表)
/**
*
* @param className
* @param instance
*/
public void putObject(String className, Object instance) {
mInstanceObjectMap.put(className, instance);
}
BBBinder与BPBinder
BPBinder
全称: Binder Proxy Binder
- 是 Binder 的代理类,运行在客户端进程,用于向服务端发送请求。
BBBinder
全称: Binder Base Binder
- 是 Binder 的基类,运行在服务端进程,用于接收和处理客户端的请求。
客户端中使用BPBinder,服务端中使用BBBinder;BPBinder在客户端中负责封装并发送请求消息,BBBinder则主要是在服务端中接收与处理请求消息。
BPBinder与BBBinder的区别 | ||
---|---|---|
特性 | BPBinder | BBBinder |
角色 | 客户端代理 | 服务端实现 |
运行位置 | 客户端进程 | 服务端进程 |
职责 | 向服务端发送请求,通过 transact() 方法调用 | 接收客户端请求,通过 onTransact() 处理请求 |
关系 | 通过 Binder 驱动与服务端的 BBBinder 通信 | 处理来自客户端 BPBinder 的请求 |
通信方式 | 调用 transact() 传递请求 | 重写 onTransact() 解析并执行请求 |
手写IPC进程通信框架之服务调用
在ServiceManager.getInstance()
中,我们通过sendRequest()
发送请求来初始化实例对象,但是这种发送消息的方式是不可能跨进程拿到其他进程的实例对象的。
对于这个问题,我们采用动态代理去实现,假装返回实例对象。客户端等于拿到了一个代理,客户端通过这个代理间接的调用目标服务的函数,而代理类内部对其进行处理。使客户端在调用时无感。
public <T>T getInstance(Class<T> clazz,Object... parameters) {
//服务发现:实际上是把需要调用的服务,以JSON的形式发送过去
sendRequest(clazz,null,parameters, TuYaService.TYPE_GET);
//发送请求后没有实例对象返回
//通过动态代理实现
return getProxy(clazz);
}
private <T> T getProxy(Class<T> clazz) {
ClassLoader classLoader = mContext.getClassLoader();
/**
* public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
* loader:classLoader
* interfaces:我们启动的服务实例,它所对外暴露的接口。如UserManager的对外接口是IUserManager
* InvocationHandler:代理处理器,Proxy.newProxyInstance返回对象后,
* 我们会通过该对象内部方法,之后会进入到InvocationHandler内部中。
* 这个InvocationHandler在Binder中叫做BPBinder。
*/
return (T) Proxy.newProxyInstance(classLoader,new Class[]{clazz},new BPBinder(clazz));
}
而BPBinder则是在Proxy.newProxyInstance()
动态代理中,作为一个处理者的角色(处理客户端发起的服务调用申请,并代理发送给服务端):
package com.example.ipccore;
import android.util.Log;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class BPBinder implements InvocationHandler {
private Class clazz;
public <T> BPBinder(Class<T> clazz) {
this.clazz=clazz;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.e("xunyou", "invoke: "+method.getName());
return null;
}
}
当我们调用返回的动态代理提供的接口时:
//服务发现
public void onClickServerFind(View view) {
IUserManager userManager = ServiceManager.getDefault().getInstance(IUserManager.class);
userManager.getUser();
}
userManager.getUser();
会触发BPBinder的public Object invoke(...)
回调,由BPBinder将要跨进程调用的函数及信息,转成序列化数据进行跨进程传输调用。
在BPBinder.invoke()
中,将要调用的函数信息通过ServiceManager.sendRequest()
完成序列化与信息发出。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//由BPBinder将要跨进程调用的函数及信息,转成序列化数据进行跨进程传输调用。
Log.e("xunyou", "invoke: "+method.getName());
ServiceManager.getDefault().sendRequest(clazz,method,args,TuYaService.TYPE_INVOKE);
return null;
}
在BPBinder
的系统源码中,transact函数内部调用了 则是通过copy_from_user
来进行跨进程通信:
之后会走到TuYaService的transact()
函数中,通过type来区分服务发现与服务调用:
服务调用与服务发现的逻辑基本一致,但是服务调用使用实例方法,因此要将服务发现时初始化的对象实例传入;
ServiceManager
作为客户端发送请求消息调用SendRequest
,
SendRequest
会走到TuYaService的public String transact(String request)
回调(此时在主进程中,而非客户端进程)中,我们在这进行服务方法的调用,并且将得到的实例对象进行序列化后返回到客户端的tuyaBinderInterface.transact()
中,之后作为SendRequest
的返回值返回到BPBinder中。
@Override
public IBinder onBind(Intent intent) {
return new ITuyaBinderInterface.Stub() {
@Override
public String transact(String request) throws RemoteException {
//反序列化数据,将JSON数据转为JavaBean
RequestBean requestBean = gson.fromJson(request, RequestBean.class);
//判断是服务发现还是服务调用
switch (requestBean.getType()){
case TYPE_GET:
//服务发现,最终是构建服务对应的实例对象。
// 即:调用getInstance,将方法反射到method,并invoke执行。
//解析拿到入参。
Object[] parameters = makeParameterObject(requestBean);
//从方法表中拿到method(需要先进行服务注册,注册类表与方法表)。服务发现是查找 getInstance()获取实例
Method method = cacheCenter.getMethod(requestBean);
//调用Method,这里是getInstance,得到实例对象。
//第一个参数 (null) 表示调用的是一个静态方法。如果是实例方法,这里需要传入对象实例。
Object instance = bbBinder.onTransact(null,method, parameters);
//将初始化后的实例对象存起来,存到实例对象表中
cacheCenter.putObject(requestBean.getClassName(),instance);
break;
case TYPE_INVOKE:
//服务调用
Log.e("xunyou", "transact: 服务调用-> "+request);
//获取之前在服务发现时初始化的实例对象
Object object = cacheCenter.getObject(requestBean.getClassName());
//获取方法
Method method1 = cacheCenter.getMethod(requestBean);
//生成入参参数
Object[] objects = makeParameterObject(requestBean);
//通过BBBinder调用对应服务功能
Object result = bbBinder.onTransact(object,method1, objects);
//将返回的实例对象进行序列化后返回到客户端。
String data = gson.toJson(result);
return data;
}
return "";
}
};
}
BPBinder调用ServiceManager.getDefault().sendRequest
发送完信息后,得到服务端返回的序列化数据,对序列化数据进行反序列化后生成新的实例对象,并返回到客户端中。
package com.example.ipccore;
import android.text.TextUtils;
import android.util.Log;
import com.google.gson.Gson;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class BPBinder implements InvocationHandler {
private Class clazz;
public <T> BPBinder(Class<T> clazz) {
this.clazz=clazz;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//由BPBinder将要跨进程调用的函数及信息,转成序列化数据进行跨进程传输调用。
Log.e("xunyou", "invoke: "+method.getName());
String str = ServiceManager.getDefault().sendRequest(clazz, method, args, TuYaService.TYPE_INVOKE);
if (!TextUtils.isEmpty(str)) {
Object object = new Gson().fromJson(str, method.getReturnType());
return object;
}
Log.e("xunyou", "invoke--str: "+str);
return null;
}
}
手写IPC进程通信框架之验证
在主进程MainActivity中进行服务注册、 以及服务数据初始化:
在F进程SecondActivity中:
ServiceManager.getDefault.open(this)
开启AIDL以建立与主进程的通信连接(AIDL内部是copy_from_user的native实现,我们是简化了)。
ServiceManager.getDefault.getInstance(IUserManager.class)
获取IUserManager的动态代理。
userManager.getUser()
调用动态代理的getUser(),其内部由BPBinder封装调用信息发给服务端;
服务端通过cacheCenter
缓存中心获取初始化后的实例对象,对其进行函数调用;
之后将调用后返回的实例对象进行序列化后,返回给BPBinder,BPBinder将序列化数据反序列化生成新的实例对象,给到客户端使用。
执行结果:
Question
1. UserManager就是ServiceManager,SecondActivity就是进程f?
是的
2. 注册服务的是谁?他在那个进程?
答:负责注册的不是个服务,而是ServiceManager,它只是个对象。
3. AMS是不是在注册时就初始化了,可前面不是说服务发现时才会初始化服务吗?
答:AMS、PMS他们是特殊的服务,在系统开机时就已经初始化,所以会马上注册。而其他一般服务,都是在服务发现阶段才会初始化。
4. AMS、PMS是不是单独进程?
AMS与PMS不属于单独进程;由ServiceManager启动的服务他们都属于 ServiceManager 进程,不会开启新进程。
AMS和PMS都是在 SystemServer->main()
->run()
->startBootstrapServices()
中初始化的;
ASM是通过反射构建,AMS在SystemServer进程,并未开启新进程。
而PMS(PackManagerService) 的启动则较为奇葩,也是在startBootstrapServices()
中,但其直接执行PackManagerService.main()
;在java中,如果直接调用main
函数,通常会开启新进程。
而这里调用的main函数,传入了很多入参进去,所以这个main函数并不是进程的入口函数,它只是普通函数,并不是入口函数。所以可以确定,PMS是SystemServer进程中启用的,并没有开启独立进程,所以其属于SystemServer进程。
PMS的main()
源码:
public static PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
// Self-check for initial settings.
PackageManagerServiceCompilerMapping.checkProperties();
PackageManagerService m = new PackageManagerService(context, installer,
factoryTest, onlyCore);
m.enableSystemUserPackages();
ServiceManager.addService("package", m);
final PackageManagerNative pmn = m.new PackageManagerNative();
ServiceManager.addService("package_native", pmn);
return m;
}
5. 在哪个进程开启Service,Service就属于哪个进程?
是的。
6. 不用Service,应用层可以实现进程通信吗?
可以,比如使用Socket、file方式都可以实现进程间通信。但进程通信框架的核心是服务注册、服务发现、服务调用,它们和跨进程通信的性质是不一样的。
手写跨进程通信框架流程总结
- 主进程MainActivity中,为UserManager注册服务。同时为UserManager(UserManager就是一个服务)添加数据。
- ServiceManager内部有一个缓存数据中心cacheCenter;其内部有三张表,分别记录
**类表:key:类名 value:类(xxx.class);
****方法表: key:类名 value:方法表(map)。 方法表:key:方法签名(方法名+参数类型名) value:方法实例(value)
**实例对象表:key:类名 value:实例对象 - 实例对象表只有在服务(普通服务)被查找时才会进行初始化并存入表中。但是系统服务除外,如AMS、PMS在系统启动就先被初始化,之后存入数据缓存中心的实例对象表中。
- F进程SecondActivity想要获取主进程UserManager中的数据;则需要先调用
ServiceManager.getDefault().open(this);
建立通信链接; - ServiceManager内部我们是通过AIDL实现的,当然也可以通过native层mmp来实现。
- F进程查找服务,通过ServiceManager查找UserManager服务
IUserManager userManager = ServiceManager.getDefault().getInstance(IUserManager.class);
- ServiceManger此时要进行服务发现;首先封装请求参数进行组装发送,再通过TuYaService+AIDL使数据来到
transact
主进程中,之后在transact
中将封装的序列化数据进行反序列化,再由BBBinder通过反射方式实例化服务端对象,并存入缓存中心。 - 接着ServiceManger通过动态代理方式,将服务的接口代理返回给客户端F进程,并由BPBinder来代理服务实例的功能。
- 客户端调用服务的功能,BPBinder则将客户端调用的方法信息通过 请求方式,发送到服务端。
- 之后由服务端返回执行后的结果(序列化数据)给到BPBinder,BPBinder将返回的序列化数据进行反序列化后给到客户端。