每日一问 | 可以不借助 bindService,实现跨进程 binder 通信吗?

162 阅读7分钟

截屏2023-08-16 14.28.57.png 原问题出处:每日一问 | 可以不借助 bindService,实现跨进程 binder 通信吗? https://www.wanandroid.com/wenda/show/21630

1.关于bindService

Service组件bindService()大概流程如下:

  • 1.首先,将发起端进程的ServiceConnection(只是Interface对象)包装进InnerConnection(Binder对象)跨进程传递到AMS。
  • 2.然后,AMS解析Intent寻找请求Service的对应的进程,若进程没启动则拉起,若启动了则通过目标进程在AMS的Binder代理对象(ApplicationThreadProxy)执行bind操作将远端的Binder对象发布到AMS。
  • 3.最后,AMS将目标进程对应的Service Binder对象通用InnerConnection.connect()回调ServiceConnection.onServiceConnected()传递到发起进程。
public class MainActivity extends Activity{
    private IBinder mPlusBinder;
	private ServiceConnection mServiceConnPlus = new ServiceConnection(){
        ...
		@Override
		public void onServiceConnected(ComponentName name, IBinder service){
			mPlusBinder = service;
		}
	};
 
	@Override
	protected void onCreate(Bundle savedInstanceState){
            Intent intentPlus = new Intent();
            intentPlus.setAction("com.zhy.aidl.calcplus");
            boolean plus = bindService(intentPlus, mServiceConnPlus,Context.BIND_AUTO_CREATE);
	}
}

Service绑定流程 正如上图所述,Service组件的bind本质是通过AMS完成Binder对象的传递。

2.传递Binder句柄

根据原问题的描述:可以不通过 bindService ,利用别的方式来传递 “binder 驱动”来实现跨进程通信吗? ,显然问题并不是在说单纯的进程间通讯(如果是的话,应该往Unix Socket、共享内存、管道方面展开吧),我理解的是:不通过Service组件bind来传递Binder句柄实现跨进程通讯。其实传递Binder句柄过程中永远离不开Binder Driver(内核驱动)和ServiceManager(Service路由)的支持,下图是Binder抽象分层模型: Binder分层模型

Framework传递Binder

java层实现BinderProxy、Binder

Service端

//定义接口方法
public interface IMyService extends IInterface {
    static final java.lang.String DESCRIPTOR = "com.demo.frameworkBinder.MyServer";
    public void sayHello(String str) throws RemoteException ;
    static final int TRANSACTION_say = android.os.IBinder.FIRST_CALL_TRANSACTION;
}

public class MyService extends Binder implements IMyService{

    public MyService() {
        this.attachInterface(this, DESCRIPTOR);
    }

    @Override
    public IBinder asBinder() {
        return this;
    }

    /** 将MyService转换为IMyService接口 **/
    public static com.demo.frameworkBinder.IMyService asInterface( android.os.IBinder obj) {
        if ((obj == null)) {
            return null;
        }
        android.os.IInterface iInterface = obj.queryLocalInterface(DESCRIPTOR);
        if (((iInterface != null)&&(iInterface instanceof com.demo.frameworkBinder.IMyService))){
            return ((com.demo.frameworkBinder.IMyService) iInterface);
        }
        return null;
    }

    /** 服务端,接收远程消息,处理onTransact方法 **/
    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        switch (code) {
        case INTERFACE_TRANSACTION: {
            reply.writeString(DESCRIPTOR);
            return true;
        }
        case TRANSACTION_say: {
            data.enforceInterface(DESCRIPTOR);
            String str = data.readString();
            sayHello(str);
            reply.writeNoException();
            return true;
        }}
        return super.onTransact(code, data, reply, flags);
    }

    /** 自定义sayHello()方法 **/
    @Override
    public void sayHello(String str) {
        System.out.println("MyService:: Hello, " + str);
    }
}

Client端

//这里的IMyService接口和服务端一样
public class MyServiceProxy implements IMyService {
    private android.os.IBinder mRemote;  //服务端Binder句柄

    public MyServiceProxy(android.os.IBinder remote) {
        mRemote = remote;
    }

    public java.lang.String getInterfaceDescriptor() {
        return DESCRIPTOR;
    }

    /** 自定义的sayHello()方法 **/
    @Override
    public void sayHello(String str) throws RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            _data.writeString(str);
            mRemote.transact(TRANSACTION_say, _data, _reply, 0);
            _reply.readException();
        } finally {
            _reply.recycle();
            _data.recycle();
        }
    }

    @Override
    public IBinder asBinder() {
        return mRemote;
    }
}

Service的注册和获取及验证

//向ServiceManager注册服务
public class ServerDemo {
    public static void main(String[] args) {
        System.out.println("MyService Start");
        //准备Looper循环执行
        Looper.prepareMainLooper();
        //设置为前台优先级
        android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_FOREGROUND);
        //注册服务
        ServiceManager.addService("MyService", new MyService());
        Looper.loop();
    }
}
//向ServiceManager获取服务
public class ClientDemo {
    public static void main(String[] args) throws RemoteException {
        System.out.println("Client start");
        IBinder binder = ServiceManager.getService("MyService"); //获取名为"MyService"的服务
        IMyService myService = new MyServiceProxy(binder); //创建MyServiceProxy对象
        myService.sayHello("binder"); //通过MyServiceProxy对象调用接口的方法
        System.out.println("Client end");
    }
}

先用ADB命令将ServerDemo,ClientDemo可执行文件,以及ServerDemo.jar,ClientDemo.jar都push到手机:

adb push ServerDemo /system/bin
adb push ClientDemo /system/bin
adb push ServerDemo.jar /system/framework
adb push ClientDemo.jar /system/framework

如果push不成功,那么先执行adb remount,再执行上面的指令;如果还不成功,可能就是权限不够。

如果上述开启成功,通过开启两个窗口运行(一个运行client端,另一个运行server端) Client端 Service端

Native层实现BpBinder、BBinder

Service端和Client端

// MyService.h 头文件声明接口方法、BnMyService Service端、 BpMyService Client端的对象
namespace android
{
    class IMyService : public IInterface
    {
    public:
        DECLARE_META_INTERFACE(MyService); //使用宏,申明MyService
        virtual void sayHello()=0; //定义方法
    };

    //定义命令字段
    enum
    {
        HELLO = 1,
    };

    //申明客户端BpMyService
    class BpMyService: public BpInterface<IMyService> {
    public:
        BpMyService(const sp<IBinder>& impl);
        virtual void sayHello();
    };

    //申明服务端BnMyService
    class BnMyService: public BnInterface<IMyService> {
    public:
        virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
        virtual void sayHello();
    };
}

// IMyService.cpp 实现类
#include "IMyService.h"
namespace android
{
    //使用宏,完成MyService定义
    IMPLEMENT_META_INTERFACE(MyService, "android.demo.IMyService");

    //客户端
    BpMyService::BpMyService(const sp<IBinder>& impl) :
            BpInterface<IMyService>(impl) {
    }

    // 实现客户端sayHello方法
    void BpMyService::sayHello() {
        printf("BpMyService::sayHello\n");
        Parcel data, reply;
        data.writeInterfaceToken(IMyService::getInterfaceDescriptor());
        remote()->transact(HELLO, data, &reply);
        printf("get num from BnMyService: %d\n", reply.readInt32());
    }

    //服务端,接收远程消息,处理onTransact方法
    status_t BnMyService::onTransact(uint_t code, const Parcel& data,
            Parcel* reply, uint32_t flags) {
        switch (code) {
        case HELLO: {    //收到HELLO命令的处理流程
            printf("BnMyService:: got the client hello\n");
            CHECK_INTERFACE(IMyService, data, reply);
            sayHello();
            reply->writeInt32(2015);
            return NO_ERROR;
        }
            break;
        default:
            break;
        }
        return NO_ERROR;
    }

    // 实现服务端sayHello方法
    void BnMyService::sayHello() {
        printf("BnMyService::sayHello\n");
    };
}

Service的注册和获取及验证

// ServerDemo.cpp 向Native层的ServiceManager注册服务
#include "IMyService.h"
int main() {
    //获取service manager引用
    sp < IServiceManager > sm = defaultServiceManager();
    //注册名为"service.myservice"的服务到service manager
    sm->addService(String16("service.myservice"), new BnMyService());
    ProcessState::self()->startThreadPool(); //启动线程池
    IPCThreadState::self()->joinThreadPool(); //把主线程加入线程池
    return 0;
}

// ClientDemo.cpp 向Native层的ServiceManager获取服务
#include "IMyService.h"
int main() {
    //获取service manager引用
    sp < IServiceManager > sm = defaultServiceManager();
    //获取名为"service.myservice"的binder接口
    sp < IBinder > binder = sm->getService(String16("service.myservice"));
    //将biner对象转换为强引用类型的IMyService
    sp<IMyService> cs = interface_cast < IMyService > (binder);
    //利用binder引用调用远程sayHello()方法
    cs->sayHello();
    return 0;
}
  • 1.因为cpp文件要编译生成可执行文件才能push到手机运行:利用Android.mk(源码build文件)编译上述代码,在Android的源码中,通过mm编译后,可生成两个可执行文件ServerDemo,ClientDemo。
  • 2.push文件到手机执行:首先将编译后的这两个ServerDemo,ClientDemo可执行文件push到手机:
adb push ServerDemo /system/bin
adb push ClientDemo /system/bin

如果push不成功,那么先执行adb remount,再执行上面的指令;如果还不成功,可能就是权限不够。

如果上述开启成功,通过开启两个窗口运行(一个运行client端,另一个运行server端) Native Client Native Service

3.Framework 层有哪些地方使用了这些方式进行通信

java层

对于java层来说,注册服务最多的地方无疑是SystemServer进程(从Zygote启动进程fork而来,主要负责注册启动各种系统必要的Service),然后应用进程向ServiceManagerService.getService()就能获取到Binder句柄了,因为服务太多,这里只举例WMS和IMS:

// frameworks/base/services/java/com/android/server/SystemServer.java
public final class SystemServer {
    private void startOtherServices() {
		...
            Slog.i(TAG, "Input Manager");
            inputManager = new InputManagerService(context);

            Slog.i(TAG, "Window Manager");
            wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore);
            ServiceManager.addService(Context.WINDOW_SERVICE, wm);
            ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
		...
	}
}

native层

因为native层是cpp文件,注册Service有点和java层不一样,以SurfaceFlinger(负责绘制应用UI的核心,其功能是将所有Surface合成工作)为例,因为在系统启动过程中指定rc文件执行初始化cpp文件:

//init.rc
service surfaceflinger /system/bin/surfaceflinger
    class core
    user system
    group graphics drmrpc
    onrestart restart zygote
    writepid /dev/cpuset/system-background/tasks

surfaceflinger服务属于核心类(core class),另外,当surfaceflinger重启时会触发zygote的重启。 surfaceflinger服务启动的起点便是如下的main()函数,完成注册后从Native层的ServiceManager获取Binder句柄即可。

// frameworks/native/services/surfaceflinger/main_surfaceflinger.cpp
int main(int, char**) {
    ProcessState::self()->setThreadPoolMaxThreadCount(4);
	//启动线程池
    sp<ProcessState> ps(ProcessState::self());
    ps->startThreadPool();

    //实例化surfaceflinger
    sp<SurfaceFlinger> flinger =  new SurfaceFlinger();

    setpriority(PRIO_PROCESS, 0, PRIORITY_URGENT_DISPLAY);
    set_sched_policy(0, SP_FOREGROUND);

    //初始化
    flinger->init();

    //发布surface flinger,注册到Service Manager
    sp<IServiceManager> sm(defaultServiceManager());
    sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false);

    // 运行在当前线程
    flinger->run();

    return 0;
}

4.题外话

有同学看到这些觉得难,其实,这并不奇怪,一般写应用界面根本就不会接触到,因为这需要了解Framework基本的知识才行。我认为学习Framework层需要具备以下基础:

  • 1.c++基础
  • 2.Linux内存基础(大抵理解kernel进程的管理和调度机制,内存管理和内存寻址,I/O驱动设备(字符设备、块设备、网络设备)和调度机制等)

如果没有以上这2个必备的基础,你在看Binder源码很可能停留在表面,当然前面所说的也只不过是整个Binder的冰山一角而已。学习新东西往往有共性所在,正如你会Jetpack Compose,那么你也会70%的Flutter了。不管看多少书,更重要的是自己思考,动手重复的实践!也许这个过程很耗时间,但是,这个不断以代码去验证自己的某些猜想的过程,也许便是这是鸿洋、郭霖前辈们的技术成长的历程吧。 Linus

空谈误国,实干兴邦。