【android系统】binder通信机制--记一次项目开发中用到的实例

自己把自己坑了一把,在一篇文章中说到,如果有盆友需要就分享一篇binder机制的文章。时隔好多年了似的,我都忘了这事了,今天居然真有需要的。首先感谢盆友的信任,其次正好我去年写了一个binder机制的实例,正好总结总结。

再次欢迎大家在公众号上来给我们留言,交流技术上的问题。

首先这篇文章不说binder机制的原理,如果盆友需要可以看看这一篇文章。这算一篇笔记吧,记一下项目中用到的binder机制。

Binder进程间通信机制(图文解析)

图文详解 Android Binder跨进程通信机制 原理

什么是binder?

binder简单点说就是用来进程间通信的工具。

有什么优点这里也不累述了,有很多资料,大概总结下:

  1. 性能方面:相比socket传输效率高,开销小。
  2. 安全方面:传统的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

 

 

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值