系统服务依赖的是Binder构架。
Binder和BinderProxy直接继承IBinder。
通信:服务Proxy(如:ActivityManagerService)通过BinderProxy,再通过BpBinder,再将请求传到Binder驱动。Binder驱动通过ServiceManager得到注册的IBinder对应的JavaBBinder,再层层上到java层的真正服务对象。这样可以实现跨进程通信。
在以上的构架基础上,ADIL为我们简化了整个通信的搭建过程。
首先,你要定义一个aidl格式的文件,如:
package com.udnderstanding.samples;
interface ImyServer{
int foo(String str);
}
这样就定义了一个名为IMyServer的Binder服务,并提供了一个可以跨Binder调用的接口foo()。
一个AIDL文件将被aidl工具解析成三个产物:
1.IMyServer接口:它仅仅用来在Java 中声明IMyServer.aidl中所声明的接口。
2.IMyServer.Stub类:这个继承自Binder类的抽象类实现了Bn端与Binder通信相关的代码
3.IMyServer.Stub.Proxy类。这个类实现了Bp端与Binder通信相关的代码。
在完成爱的了解析后,为了实现一个Bn端,开发者需要继承IMyServer.Stub类并实现其抽象方法。
每个MyServer实例都具有了作为Bn端的能力。
典型用法:
1.将MyServer类的实例通过ServiceManager.addService(String name, IBinder service)将其注册为一个系统服务。
2.在一个Android标准Service的onBind()方法中将其作为返回值使之可以被其他进程访问
3.通过binder调用将其传递给另外一个进程,使之成为一个跨进程的回调对象。
但是殊途同归,在Bp端所在进程中,一旦获取了IMyServer的BinderProxy(通过ServiceManager.geService()、onSrviceConnected()、或者其他方式),就可以通过如下方式获得一个IMyServer.Proxy:
//就是最终需要获得com.understanding.samples.IMyServer.Stub.Proxy,asInterface()返回就是这个类型
IMyServer remote = IMyServer.Stub.asInterface(binderProxy);
remote.foo("Hello AIDL");
下面是aidl工具自动解析成的文件。
public interface IMyServer extends android.os.IInterface{
//自动生成一个内部类
public static abstract class Stub extends android.os.Binder implements com.understanding.samples.IMyServer{
/********code:对应不同的方法,表示proxy所需调用的方法。
data:从proxy打包传过来的参数
在proxy发出请求后,最终会调用Stub的子类的onTransact()方法,而onTransact()中会调用foo(),所以子类需实现foo()
*/
public boolena onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
throws android.os.RemoteException{
switch(code){
......
case TRANSACTION_foo:{
......//从data中读取参数_arg0
//Stub类的子类需要实现foo()方法
int _result = this.foo(_arg0);
......//向reply中写入_result
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
//调用了Bp端的foo对应会调用Bn端的foo。Bp端的foo()的逻辑是固定的,就是将传过来的参数打包传Bn端。
private static class Proxy implements com.understanding.samples.IMyServer{
......//Proxy类的其他实现
public int foo(java.lang.String str)
throws android.os.RemoteException{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try{
......//将参数str写入_data
//mRemot就是指向IMyServer Bn端的BinderProxy;可能会阻塞等待返回结果
mRemot.transact(Stub.TRANSCATION_foo, _data, reply, 0);
......//从_replay中读取返回值_result
} finally{
......
}
return _result;
}
static final int TRANSACTION_foo = (android.os.Ibinder.FIRST_CALL_TRANSCATION + 0);
}
}
//声明IMyServer所提供的接口。
public int foo(java.lang.String str) throws android.os.RemoteException;
}
下面说说AIDL在AS上的操作:(以下demo参考自:http://blog.youkuaiyun.com/u011974987/article/details/51243539)
首先在new file 中选择AIDL,会自动生成一个名为“aidl”与java文件夹同级的文件夹,aidl文件就放在该文件夹下。注意包名和java文件一样是文件夹的路径。
// IMyInterface.aidl
package com.example.zhangjinbiao.aidltest;
// Declare any non-default types here with import statements
interface IMyInterface {
String getInfor(String s);
}
写一个Service,在Service中返回一个IBinder(就是服务端stub的子类),然后在使用的时候调用asInterface(IBinder)转为IMyInterface(IInterface),该方法最终会返回一个Proxy。所以客户端最终使用的是Proxy。
下面是客户进程代码:
public class MainActivity extends AppCompatActivity {
public final static String TAG = "AIDLTEST_MainActivity";
private IMyInterface myInterface;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myInterface = IMyInterface.Stub.asInterface(service);
Log.i(TAG, "连接Service 成功");
try {
String s = myInterface.getInfor("我是Activity传来的字符串");
Log.i(TAG, "从Service得到的字符串:" + s);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG, "连接Service失败");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startAndBindService();
}
private void startAndBindService() {
Intent service = new Intent(MainActivity.this, MyService.class);
//startService(service);
bindService(service, serviceConnection, Context.BIND_AUTO_CREATE);
}
}
注意:当客户端和服务端是在不同应用中的,那么IMyInterface怎么能被客户端代码访问到。所以此种情况必须要复
制一份aidl文件到客户端,这样就可以和服务端访问到同一份代码。两边的aidl必需是要一致的。这样,无论是使用哪一个aidl文件产生的类,都是一致且正确的。下面是android介绍AIDL的官方文档中的一句话:
The client must also have access to the interface class, so if the client and service are inseparate applications,
then the client'sapplication must have a copy of the.aidl
filein itssrc/
directory (which generates the
android.os.Binder
interface—providingthe client access to the AIDL methods).
翻译:客户端还必须能够访问接口类,因此如果客户端和服务在单独的应用程序中,那么客户机的应用程序必须具
有一个副本 .aidl文件在其src /目录中(生成android . os.Binder
interface,提供客户端访问AIDL方法。
下面是服务端进程代码:
public class MyService extends Service {
public final static String TAG = "AIDLTEST_MyService";
private IBinder binder = new IMyInterface.Stub() {
@Override
public String getInfor(String s) throws RemoteException {
Log.i(TAG, s);
return "我是 Service 返回的字符串";
}
};
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreat");
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}
需要在Manifest中声明:
<service
android:name=".MyService"
android:process="com.xu.remote" />
声明该Service在独立进程中。
下面是自动生成的IMyInterface.java文件:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: /home/zhangjinbiao/AndroidStudioProject/AIDLTest/app/src/main/aidl/com/example/zhangjinbiao/aidltest/IMyInterface.aidl
*/
package com.example.zhangjinbiao.aidltest;
// Declare any non-default types here with import statements
public interface IMyInterface extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.zhangjinbiao.aidltest.IMyInterface
{
private static final java.lang.String DESCRIPTOR = "com.example.zhangjinbiao.aidltest.IMyInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.zhangjinbiao.aidltest.IMyInterface interface,
* generating a proxy if needed.
*/
public static com.example.zhangjinbiao.aidltest.IMyInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.zhangjinbiao.aidltest.IMyInterface))) {
return ((com.example.zhangjinbiao.aidltest.IMyInterface)iin);
}
return new com.example.zhangjinbiao.aidltest.IMyInterface.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getInfor:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
java.lang.String _result = this.getInfor(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.example.zhangjinbiao.aidltest.IMyInterface
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public java.lang.String getInfor(java.lang.String s) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(s);
mRemote.transact(Stub.TRANSACTION_getInfor, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getInfor = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public java.lang.String getInfor(java.lang.String s) throws android.os.RemoteException;
}
注意:AIDL一般是阻塞式调用,就像调用一般方法一样,要非阻塞调用需要设置IBinder接口类中打FLAG_ONEWAY字段.,在native曾中涉及到Binder调用打基本是阻塞式,而java framework层一般非阻塞式的.
mRemote.transact(Stub.TRANSACTION_getInfor, _data, _reply, 0);
看这句,自动生成的AIDL文件,proxy调用时flag为0,即阻塞调用。如果要非阻塞调用,则需要改为1。
以ActivtyManagerService的binder通信深入Binder的工作原理
前面不是说了通过ServiceManager.geService()、onSrviceConnected()、或者其他方式可以
获得BinderProxy。ActivityManagerService正是使用ServiceManager.geService()来获得
BinderProxy,很容易让人觉得getService是获取到当初addService()注册的Binder。那么
ServiceManagerService的binder通信又是怎么获得BinderProxy的呢。
ActivityManagerService首先需要向ServiceManagerService注册:
public static void addService(String name, IBinder service){
//...
getIServiceManager.addService(name, service)
//..
}
getIServiceManager()获得的是一个IServiceManager,就是ServiceManagerNative.java中
的ServiceManagerProxy impliments IServiceManager.使用ServiceManagerNative.asInterface(IBinder)
静态方法创建并返回一个ServiceManagerProxy,而BinderProxy是ServiceManagerProxy的构造参数就,
因为要把消息传到binder驱动,是需要通过BinderProxy。
BinderProxy是一个final类,只有一个空参构造方法,本身没有绑定通信目标的信息,那么它是怎么知道他的
通信目标的呢?BinderProxy的mObject(long)成员保存着native对象的BpBinder的指针(对象地址),BpBinder
是知道对应的BBinder的,BpBinder和BBinder都是extends IBinder的,是两两对应,是通信通道的两端。
上面说的都客户端的,那么服务端呢,服务端是需要注册到Binder驱动的,通过ActivityServiceNative是通过
ServiceManagerNative#addService(IBinder)注册到Binder驱动的。java中的Binder对象并不是直接跟驱动
打交道的,Binder(该例中是ActivityManagerNative)的mOject:long成员持有一个native对象的指针JavaBBinderHolder,
JavaBBinderHolder对象又指向BBinder(JavaBBinder?),BBinder指向,打个圆圈可能跟垃圾回收有关。注册到
Binder驱动中的是BBinder。
当客户端请求时,从BBinder中传来请求到Binder中,启动Binder#execTransact(),其中会调用子类的onTransact(),
该例中就是AtivityManagerNative#onTransact(),会在里面根据请求参数调用客户端请求的那个方法。
那么服务端处理客户端请求是在哪个线程中执行呢?在zygote应用进程时,会创建一个处理binder请求的线程池,
所以服务端响应客户端binder请求是在服务端所在应用进程的binder线程中执行的。
ServiceManager的作用:
管理serivces,注册只是给个路径其他client,即其他进程方便获取到这个服务,注册到ServiceManager需要system或者root权限,又或者在allowed结构体中加入一个进程名称。而service和client的binder通信并不用通过ServiceManager的,ServiceManger也是能和binder通信的。
ActivityManagerService, PowerManagerService, WindowManagerService等服务是运行在system_server进程中的,每个服务都运行在不同线程中。照说通过binder驱动通信时,执行client访问的函数都是在binder线程池的线程中运行的,而没有一个binder线程对应一个服务,那么,怎样让属于某个服务的代码运行在服务所在线程呢,那就得把实际代码放在handler中,通过handler把消息从binder线程中传到服务线程中。Activity也是这么实现,WindowManagerService或者ActivityManagerService给ActivityThread发消息时,就是通过handler,使实际代码在主线程中执行。
binder线程池:
在android系统中,通过binder进行IPC时,服务端总是会起一些Binder线程来响应客户端的请求。如下面的这个设备上,system_process进程中就可以看到许多名为"Binder_X"的线程。在android的java app进程被创建起来时,它就会去建立一个线程池,来专门处理那些binder IPC事务。在frameworks/base/cmds/app_process/app_main.cpp中我们可以看到下面的这两个方法:
virtual void onStarted()
{
sp<ProcessState> proc = ProcessState::self();
ALOGV("App process: starting thread pool.\n");
proc->startThreadPool();
AndroidRuntime* ar = AndroidRuntime::getRuntime();
ar->callMain(mClassName, mClass, mArgC, mArgV);
IPCThreadState::self()->stopProcess();
}
virtual void onZygoteInit()
{
// Re-enable tracing now that we're no longer in Zygote.
atrace_set_tracing_enabled(true);
sp<ProcessState> proc = ProcessState::self();
ALOGV("App process: starting thread pool.\n");
proc->startThreadPool();
}
上面的proc->startThreadPool()这个调用创建的Binder线程池。在android binder的设计中,ProcessState被设计来直接与binder驱动进行通信。在之前分析ActivityManagerService的文章中,有提到ProcessState。任何一个线程只要执行了IPCThreadState::self()->joinThreadPool(),就可以把自己变成一个binder线程
最后总结
实现binder通信,并不一定要AIDL,AIDL只是简化了搭建binder通信通道的过程。只要知道以下关键点即可:
* 定义一个接口如:IMyService,IServiceManager,IActivityManager,extends IInterface(有一个asBinder()接口),这些接口都是提供给客户端调用的方法
* 定义一个类,实现IMyService和Binder。这个类是用于实现实际业务逻辑的。除了实现IMyService中的方法外,
最重要的是需要一个返回一个创建并返回一个Proxy给客户作为代理使用,还有实现onTransact(...)方法,这
个方法根据参数选择调用IMyService中定义的接口方法。
* 定义一个Proxy,实现IMyService,然后Proxy实现IMyService的接口的原则是,打包数据发送到Binder驱动,
让服务端(即上面说的那个类)的onTransact()知道怎么解析出方法参数和调用哪个方法。Proxy在创建的
时候需要传入一个BinderProxy,这样才能和Binder驱动通信并确定通信目标(服务端)