自己把自己坑了一把,在一篇文章中说到,如果有盆友需要就分享一篇binder机制的文章。时隔好多年了似的,我都忘了这事了,今天居然真有需要的。首先感谢盆友的信任,其次正好我去年写了一个binder机制的实例,正好总结总结。
再次欢迎大家在公众号上来给我们留言,交流技术上的问题。
首先这篇文章不说binder机制的原理,如果盆友需要可以看看这一篇文章。这算一篇笔记吧,记一下项目中用到的binder机制。
什么是binder?
binder简单点说就是用来进程间通信的工具。
有什么优点这里也不累述了,有很多资料,大概总结下:
- 性能方面:相比socket传输效率高,开销小。
- 安全方面:传统的IPC通信方式没有任何措施,Android为每个安装好的应用程序分配了自己的UID,进程的UID是鉴别进程身份的重要标志。
概述图
说明:
Client进程:使用服务的进程。
Server进程:提供服务的进程。
ServiceManager进程:ServiceManager的作用是将字符形式的Binder名字转化成Client中对该Binder的引用,使得Client能够通过Binder名字获得对Server中Binder实体的引用。
Binder驱动:驱动负责进程之间Binder通信的建立,Binder在进程之间的传递,Binder引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。
工作流程
本小节来源:https://www.cnblogs.com/itgungnir/p/6640120.html
假设:客户端的程序Client运行在进程A中,服务端的程序Server运行在进程B中。
由于进程的隔离性,Client不能读写Server中的内容,但内核可以,而Binder驱动就是运行在内核态,因此Binder驱动帮我们进行请求的中转。
有了Binder驱动,Client和Server之间就可以打交道了,但是为了实现功能的单一性,我们为Client和Server分别设置一个代理:Client的代理Proxy和Server的代理Stub。这样,由进程A中的Proxy和进程B中的Stub通过Binder驱动进行数据交流,Server和Client直接调用Stub和Proxy的接口返回数据即可。
此时,Client直接调用Proxy这个聚合了Binder的类,我们可以使用一系列的Manager来屏蔽掉Binder的实现细节,Client直接调用Manager中的方法获取数据,这样做的好处是Client不需要知道Binder具体怎么工作。
项目实例
平台:hisi98cv200
功能:在ActivityManager添加一个接口给上层应用获取系统堆栈
1、通信接口
它定义了Server端可接收的方法调用,也就是Client可以发起的远程调用。
代码路径:frameworks/base/core/java/android/app/IActivityManager.java
public interface IActivityManager extends IInterface {
... ...
public Map<String,String> getAllStackInfo() throws RemoteException;
... ...
String descriptor = "android.app.IActivityManager";
... ...
int GET_ALLTASKINFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 500;
}
RemoteException
所有接口方法需要声明RemoteException异常,跨进程操作时目标服务进程总可能意外终止,或者服务类调用Parcel.writeException()来通知异常发生,这样Client端接口方法的调用就抛出RemoteException。descriptor
对当前接口的一个字符串标识,Binder类的attachInterface()和queryLocalInterface()方法会用到它。FIRST_CALL_TRANSACTION
对应接口方法getAllStackInfo()的命令code。IInterface
Binder-IPC要求的标准实现方式是,通信接口需要继承接口IInterface。它定义了asBinder()方法用来返回接口实例关联的IBinder对象。
这是因为一般正是Server端的Binder子类会实现通信接口,然后,Client是无法拿到Server端的IActivityManager 对象的,所以,为Client定义一个本地的IActivityManager 的代理实现类,该实现类使用BinderProxy调用Server端方法获取结果。
也就是通常两端实现接口IActivityManager 的地方都密切关联了一个可以用来远程通信的IBinder对象,而asBinder()就是用来返回这个IBinder的。
经试验,这不是必须的。
2、client代理类
有了通信接口后,紧接着需要实现Server和Client使用IBinder进行数据交互的部分。也就是transact()的逻辑。
IActivityManager 的实现是位于Server端的,Client端无法得到其对象。所以,Client端定义一个接口的代理实现类。
代码路径:frameworks/base/core/java/android/app/ActivityManagerNative.java
class ActivityManagerProxy implements IActivityManager
{
... ...
public Map<String,String> getAllStackInfo() throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
mRemote.transact(GET_ALLTASKINFO_TRANSACTION, data,reply, 0);
reply.readException();
Map<String,String> map = null;
map = reply.readHashMap(HashMap.class.getClassLoader());
data.recycle();
reply.recycle();
return map;
}
... ...
}
代理类ActivityManagerProxy 对getAllStackInfo()的实现是通过mRemote来发送远程调用。
此处的mRemote是连接到Server时,Server端Binder关联的BinderProxy对象。
上面getAllStackInfo()中写入和读取数据的顺序必须和Server端的Binder.onTransact()对应——主要就是code的取值和data、reply中数据的写入读取顺序。
3、Server端通信类
代码路径:frameworks/base/core/java/android/app/ActivityManagerNative.java
public abstract class ActivityManagerNative extends Binder implements IActivityManager
{
... ...
static public IActivityManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IActivityManager in =
(IActivityManager)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}
return new ActivityManagerProxy(obj);
}
... ...
static public IActivityManager getDefault() {
return gDefault.get();
}
... ...
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
switch (code) {
... ...
case GET_ALLTASKINFO_TRANSACTION:{
Map<String,String> map = getAllStackInfo();
reply.writeNoException();
reply.writeMap(map);
return true;
}
... ...
}
... ...
}
... ...
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
IActivityManager am = asInterface(b);
return am;
}
};
... ...
}
可以看到ActivityManagerNative.onTransact()和ActivityManagerProxy.transact()是对应的,后者的每一个code和前者都有一个case语句去处理。重要的是,有关data、reply参数的数据的写入和读取顺序也严格对应。
在onTransact()中调用了getAllStackInfo()方法,它由最终的ActivityManagerNative 的子类去完成。getAllStackInfo()是业务方法,和通信细节没有关系。
ActivityManagerNative 作为Binder子类,它实现的IInterface.asBinder()直接返回其对象本身——它既是IInterface的子类,又是Binder子类。
1、client是如何连接到services的?
调用路径:应用---getDefault---services---gDefault ---asInterface---ActivityManagerProxy
最终调用到ActivityManagerProxy代理上。
2、直接调用到了services,为什么还要绕一下调用到代理上去呢?
因为services是一个进程,client是一个进程,通过ActivityManagerNative.onTransact()和ActivityManagerProxy.transact()进行通信,直接调用services是不行的。
4、service的实现
代码路径:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
... ...
final ActivityStackSupervisor mStackSupervisor;
... ...
public synchronized Map<String,String> getAllStackInfo(){
Map<String,String> map = mStackSupervisor.getAllStackInfoLocked();
return map;
}
... ...
}
ActivityManagerService这个类继承了ActivityManagerNative,实现了getAllStackInfo方法。
由上可知,mStackSupervisor的类型是ActivityStackSupervisor ,
所以具体实现是在:frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
public final class ActivityStackSupervisor implements DisplayListener {
... ...
Map<String,String> getAllStackInfoLocked() {
Map<String,String> pivotalAppList = null;
pivotalAppList = new HashMap<String,String>();
for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
ArrayList<ActivityStack> stacks = activityDisplay.mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
for (int i=stack.mLRUActivities.size()-1; i>=0; i--) {
final ActivityRecord r = stack.mLRUActivities.get(i);
String className = r.realActivity.getClassName();
String appName = r.packageName;
String oldAppName = pivotalAppList.get(appName);
//不存在map中进行添加,保证只添加一个应用的最上层activity
if (oldAppName == null)
{
pivotalAppList.put(appName, className);
}
}
}
}
return pivotalAppList;
}
}
5、应用层调用
这个就是应用层调用的实例。
public static String getAppClass(String PackageName) throws RemoteException {
String className = null;
if(mAm == null){
mAm = ActivityManagerNative.getDefault();
}
Map<String,String> map = mAm.getAllStackInfo();
for (String key : map.keySet()) {
String value = map.get(key);
}
if(PackageName.equals(pivotalApp1)){
className = map.get(pivotalApp1);
}else if(PackageName.equals(pivotalApp2)){
className = map.get(pivotalApp2);
}else if(PackageName.equals(pivotalApp3)){
className = map.get(pivotalApp3);
}
return className;
}
结束语
以上就是小编在项目中用到的binder机制。binder机制已经出来很久了,在网上有各种各样的说明、详解以及各种实例。小编的这一篇是一篇基于android系统activitymanager上开发的笔记,并不是一篇详解binder机制的文章。如果需要了解更多,可以参考网上更多的资料。
最后还是感谢那位盆友,写完这篇文章对binder机制又清晰了很多。也欢迎更多的盆友来我们公众号交流技术问题,或其他问题。
参考:
https://www.cnblogs.com/itgungnir/p/6640120.html
https://www.cnblogs.com/everhad/p/6246551.html