正在我们在开发的时候,涉及到需要跨进程通信,尤其是较为复杂的,需要拿到服务端 binder 代理进行交互的方式。
一般流程为:
- 编写 aidl 文件;
- 实现一个 Service 子类(独立进程),复写 onBind 方法,返回 Binder's Stub;
- 主进程通过调用 bindService,间接拿到 binder 驱动,然后实现 binder 通信;
更进一步的说,很多时候,我们也可以不依赖 aidl 文件,自己去实现Stub、Proxy类,利用 binder 驱动通过 transact 方法与服务端 Stub onTransact 进行跨进程交互。
但是依然是需要依赖 bindService 方法,去获取 binder驱动。
不过我们今天的关注点在于:
- 可以不通过 bindService ,利用别的方式来传递 “binder 驱动”来实现跨进程通信吗?
- framework 层有哪些地方使用了 1 的方式进行通信?
更多问答 >>
-
2022-05-06 11:37
-
每日一问 | Service onStartCommand 返回STICKY是如何做到被拉活的?
2022-07-24 11:50 -
2022-05-27 20:55
-
2022-06-12 14:41
-
每日一问 | 被声明为private final 的内部类,能生成一个子类对象吗?逆天篡改~
2022-04-15 21:13 -
每日一问 .class vs Class.forName() vs loadClass() 类加载傻傻分不清楚?
2022-02-11 14:22 -
每日一问 | 脱糖对于Android 打包期间插桩的有什么影响?
2022-03-07 21:26 -
每日一问 | 如何构造一个 hide interface 的实现类?
2022-02-08 23:51 -
每日一问 | android hidden api 不是禁用反射,以及如何突破,「元反射」不行了?
2022-02-08 23:51
xxxxxxxxxx
每日一问 | 可以不借助 bindService,实现跨进程 binder 通信吗?
正在我们在开发的时候,涉及到需要跨进程通信,尤其是较为复杂的,需要拿到服务端 binder 代理进行交互的方式。一般流程为:
更进一步的说,很多时候,我们也可以不依赖 aidl 文件,自己去实现Stub、Proxy类,利用 binder 驱动通过 transact 方法与服务端 Stub onTransact 进行跨进程交互。但是依然是需要依赖 bindService 方法,去获取 binder驱动。可以参考:Android aidl Binder框架浅析
不过我们今天的关注点在于
1.关于bindService
Service组件bindService()大概流程如下:
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组件的bind本质是通过AMS完成Binder对象的传递。
2.传递Binder句柄
根据原问题的描述:可以不通过 bindService ,利用别的方式来传递 “binder 驱动”来实现跨进程通信吗? ,显然问题并不是在说单纯的进程间通讯(如果是的话,应该往Unix Socket、共享内存、管道方面展开吧),我理解的是:不通过Service组件bind来传递Binder句柄实现跨进程通讯。其实传递Binder句柄过程中永远离不开Binder Driver(内核驱动)和ServiceManager(Service路由)的支持,下图是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
如果上述开启成功,通过开启两个窗口运行(一个运行client端,另一个运行server端)
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; }
adb push ServerDemo /system/bin adb push ClientDemo /system/bin
如果上述开启成功,通过开启两个窗口运行(一个运行client端,另一个运行server端)
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层需要具备以下基础:
如果没有以上这2个必备的基础,你在看Binder源码很可能停留在表面,当然前面所说的也只不过是整个Binder的冰山一角而已。学习新东西往往有共性所在,正如你会Jetpack Compose,那么你也会70%的Flutter了。不管看多少书,更重要的是自己思考,动手重复的实践!也许这个过程很耗时间,但是,这个不断以代码去验证自己的某些猜想的过程,也许便是这是鸿洋、郭霖前辈们的技术成长的历程吧。
能否不用 service manager 的 addService?这玩意儿会被 selinux 挡掉。
系统不允许没权限user添加service是出于binder安全、高效的设计,因为binder句柄传递永远绕不开ServiceManager、BInder Driver,所以你考虑换一种IPC跨进程通 ...查看更多
系统不允许没权限user添加service是出于binder安全、高效的设计,因为binder句柄传递永远绕不开ServiceManager、BInder Driver,所以你考虑换一种IPC跨进程通讯方式,如Socket、Pipe、共享内存等等。
这个和launcher进程有什么关系呀?
这里launcher进程是指请求bindService的Client进程,表示发起请求的进程,不是桌面launcher进程。
可通过Contentprovider
参考https://github.com/luckybilly/CC/blob/master/cc/src/main/java/com/billy/cc/core/component/remote/RemoteCursor.java
还可以通过广播接收器peek
1.有权限就用ServiceManager#addService,没权限用ContentProvider传递binder
2.framework里几乎所有的各种Manager都是ServiceManager#addService实现的
好处是获取方法是同步的
是否可以用 socket 通信呢?
可以
爱奇艺有个方案也不知适不适用https://github.com/iqiyi/Andromeda
这个的核心还是bindService实现的