android Binder

来源:互联网 发布:linux dhcp 作用 编辑:程序博客网 时间:2024/06/17 07:33

Binder 是什么? 顾名思义 Binder 英文意思 别针,回形针,在android中 主要用于进程间通信。android 中Binder 工作于Linux层面,属于一个驱动,只是该驱动不需要硬件,或者说其操作的硬件是基于一小段内存。从线程角度来讲,Binder 驱动代码运行在内核端,客户端程序调用Binder是通过系统调用完成的。

Binder 跨进程通信是如何完成的?
在Binder 主要提供了三个模块,即 服务端接口,Binder驱动,客户端接口 三者的关系可以看下图
这里写图片描述

下面我们来详细介绍Binder中 这三个模块的主要功能:

1:服务端:一个Binder 服务端其实就是一个Binder类对象,服务端内该Binder对象一旦创建,就会启动一个隐藏线程,而该线程的作用是和Binder驱动 进行消息的交互。当该隐藏线程收到消息之后就会执行Binder对象中的onTransact() 方法,根据该方法的传入参数执行不同的服务代码。

2:Binder驱动 : Binder驱动是客户端雨服务端进行通信的桥 ,Binder驱动中的mRemote对象实质上也是一个Binder对象,该对象是在服务端创建Binder对象时,同时创建的。而客户端要访问远程服务端时,都是通过Binder驱动中的mRemote对象进行访问的。而在Binder驱动中同样重载类transact() 方法

重载transact() 方法内容主要包括:

2.1:向服务端隐藏线程发送调用消息
2.2:挂起当前线程,等待服务端执行完成之后通知
2.3:接到服务端通知后,继续执行客户端线程,并返回执行结果

3:客户端:客户端想要访问远程服务,必须获取远程服务在Binder 对象中对应的mRemote对象。

注:对于应用程序开发来说,客户端似乎是直接调用了服务端Binder对象,而实质上是客户端调用了Binder驱动中的Binder对象,然后通过Binder驱动中的Binder对象与服务端的Binder进行通信,所以应用程序中是存在两个Binder对象,一个服务端Binder,一个Binder驱动中的Binder。

下面我们从两个方面对Binder 进行详细的分析,分别是客户端通过Binder调用系统服、自定义一个远程服务客户端通过Binder进行调用

1:客户端通过Binder调用系统服务;
在调用系统服务时我们通常使用 getSystemService(String str);方法去获取系统服务;

 InputManager inputManager = (InputManager)   getSystemService(Context.INPUT_SERVICE); inputManager.getInputDeviceIds();

下面我们从这段代码开始分析看客户端是如何获取到服务端Binder对象,以及通过该Binder对象客户端如何与服务端进行通信的。

public class Activity extends ContextThemeWrapper        implements LayoutInflater.Factory2,        Window.Callback, KeyEvent.Callback,        OnCreateContextMenuListener, ComponentCallbacks2,        Window.OnWindowDismissedCallback, WindowControllerCallback{ @Override    public Object getSystemService(@ServiceName @NonNull String name) {           ...        return super.getSystemService(name);    } }

通过上述代码我们可以看到实质上是调用了ContextThemeWrapper.getSystemService(name);方法;

public class ContextThemeWrapper extends ContextWrapper {    @Override    public Object getSystemService(String name) {        ...        return getBaseContext().getSystemService(name);    }}

而在ContextThemeWrapper中我们可以看出实际上是调用了ContextImpl中的getSystemService(name);方法

 @Override    public Object getSystemService(String name) {        return SystemServiceRegistry.getSystemService(this, name);    }

在该段代码中可以看出实质上是调用到了SystemServiceRegistry 类中的getSystemService(…)方法,

private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =            new HashMap<String, ServiceFetcher<?>>(); static abstract interface ServiceFetcher<T> {        T getService(ContextImpl ctx);    } public static Object getSystemService(ContextImpl ctx, String name) {        ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);        return fetcher != null ? fetcher.getService(ctx) : null;    }

可以看出在SystemServiceRegistry 类内部通过Map将name与 ServiceFetcher 一一对应进行存放,然后通过Map的key - value 值先获取ServiceFetcher 然后通过getService()获取Object对象,而getService()方法实质上是 ServiceFetcher 抽象类中的一个抽象方法。
而ServiceFetcher抽象方法的具体实现是:

/**     * Override this class when the system service constructor needs a     * ContextImpl and should be cached and retained by that context.     */ static abstract class CachedServiceFetcher<T> implements ServiceFetcher<T> {      ...    }/**     * Override this class when the system service does not need a ContextImpl     * and should be cached and retained process-wide.     */static abstract class StaticServiceFetcher<T> implements ServiceFetcher<T> {...}/**     * Like StaticServiceFetcher, creates only one instance of the service per application, but when     * creating the service for the first time, passes it the application context of the creating     * application.     *     * TODO: Delete this once its only user (ConnectivityManager) is known to work well in the     * case where multiple application components each have their own ConnectivityManager object.     */static abstract class StaticApplicationContextServiceFetcher<T> implements ServiceFetcher<T> {...}

通过上述源码可以看出 getService(…)方法实质上是 执行了
CachedServiceFetcher 中的getService方法。

 private T mCachedInstance;        @Override        public final T getService(ContextImpl unused) {            synchronized (StaticServiceFetcher.this) {                if (mCachedInstance == null) {                    mCachedInstance = createService();                }                return mCachedInstance;            }        }        public abstract T createService();

可以看出在改方法中Object 对象是通过数组获取的,如果数组中没有该对象,则会创建一个Object对象,然后将该对象存放到数组中。

而在SystemServiceRegistry 类中我们可以看到内部有一个static 代码块在这段代码块中我们可以看到;

static{    ...    registerService(Context.INPUT_SERVICE, InputManager.class,                new StaticServiceFetcher<InputManager>() {            @Override            public InputManager createService() {                return InputManager.getInstance();            }});      ....        registerService(Context.LOCATION_SERVICE, LocationManager.class,                new CachedServiceFetcher<LocationManager>() {            @Override            public LocationManager createService(ContextImpl ctx) {                IBinder b = ServiceManager.getService(Context.LOCATION_SERVICE);                return new LocationManager(ctx, ILocationManager.Stub.asInterface(b));            }});}

内部执行了一些列的注册活动,

private static final HashMap<Class<?>, String> SYSTEM_SERVICE_NAMES =            new HashMap<Class<?>, String>();    private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =            new HashMap<String, ServiceFetcher<?>>(); private static <T> void registerService(String serviceName, Class<T> serviceClass,            ServiceFetcher<T> serviceFetcher) {        SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);        SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);    }

在注册方法中实质上是将’ key-value’ 数据存储到Map容器中;

下面我们单独看 INPUT_SERVICE 是如何存储到,在 registerService 过程中我们只需要关注

new StaticServiceFetcher<InputManager>() {            @Override            public InputManager createService() {                return InputManager.getInstance();            }}

而上述代码实质上是返回了一个InputManager对象,而在InputManager类中

 public static InputManager getInstance() {        synchronized (InputManager.class) {            if (sInstance == null) {                IBinder b = ServiceManager.getService(Context.INPUT_SERVICE);                sInstance = new InputManager(IInputManager.Stub.asInterface(b));            }            return sInstance;        }    }

我们可以看到 内部首先 通过ServiceManager.getService(…)方法获取一个IBinder对象,而改IBinder对象实质上是其他系统Service的Binder引用,

 public static IBinder getService(String name) {        try {            IBinder service = sCache.get(name);            if (service != null) {                return service;            } else {                return getIServiceManager().getService(name);            }        } catch (RemoteException e) {            Log.e(TAG, "error in getService", e);        }        return null;    }

然后通过 new 出一个InputManager 出入到参数为 IInputManager.Stub.asInterface(b),该参数实质上是一个IInputMethodManager的统一接口。

  private final IInputManager mIm;  private InputManager(IInputManager im) {        mIm = im;    }

在我们调用InputManager内部方法是可以看到实质上是调用了IInputManager中的方法

    public InputDevice getInputDevice(int id) {        synchronized (mInputDevicesLock) {           ...            InputDevice inputDevice = mInputDevices.valueAt(index);            ...            return inputDevice;        }    }

通过上述代码的调用我们可以看到实质上是 通过InputManager中的IBinder对象 获取了远程的IInputManager接口对象,调用了远程的方法。

至此 我们可以看出系统服务中是如何通过Binder进行远程调用的。

总结:系统的各种服务的获取实质上 是先通过ServiceManager对象获取到不同Manager对应的IBinder对想,然后通过IBinder对象去获取远程接口,然后通过该远程接口调用远程方法去实现客户端的需求。

下面我们以图示的方式说明系统内部是如何跨进程调用的:

这里写图片描述

----------------------------
2:下面我们来看一下如何通过Binder来自定义跨进程通信:
关于自定义跨进程通信 android提供了统一的方法--aidl,下面我们就以aidl方式来讲解通过Binder 如何实现的跨进程通信。

先来看一段aidl如何实现跨进程通信的实例:

1:定义aidl

interface IKernelInterface {    /**     * Demonstrates some basic types that you can use as parameters     * and return values in AIDL.     */    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,            double aDouble, String aString);    //自定义远程服务中的方法     void test();}

2:实现一个Service 并在该Service中实现aidl中自定义的方法,并将Binder对象通过 onBind 方法返回;

public class KernelService extends Service {    private IBinder iBinder = new IKernelInterface.Stub() {        @Override        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {        }        @Override        public void test() throws RemoteException {            Log.d("TAG","aidl");        }    }.asBinder();    @Override    public void onCreate() {        super.onCreate();    }    @Nullable    @Override    public IBinder onBind(Intent intent) {        return iBinder;    }}

3:客户端 通bindService启动Service 并实现ServiceConnection接口,在该接口中获取其它进程中的接口,然后通过该接口调用内部方法。

  Intent intent = new Intent(this,KernelService.class);        bindService(intent,new KernelConnection(), Context.BIND_AUTO_CREATE); class KernelConnection implements ServiceConnection{        //IBinder 获取Binder驱动中的Remoter对象。        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            IKernelInterface iKernelInterface = IKernelInterface.Stub.asInterface(service);            try {                iKernelInterface.test();            } catch (RemoteException e) {                e.printStackTrace();            }        }        @Override        public void onServiceDisconnected(ComponentName name) {        }    }

以上就是aidl的实现步骤,通过以上步骤我们可以实现跨进程通信。

下面我们通过源码来分析上述代码是如何实现跨进程通信的。
在文章的开始我们提到跨进程通信实质上是客户端获取到Binder驱动中的Binder对象,然后调用 transact(…)方法实现跨进程通信的。

在aidl生成的文件中我们可以看到 Binder驱动中transact(…) 方法实质上系统已经帮我们实现了。在我们调用test() 方法是内部实质上已经调用到了该方法

  @Override            public void test() throws android.os.RemoteException {                android.os.Parcel _data = android.os.Parcel.obtain();                android.os.Parcel _reply = android.os.Parcel.obtain();                try {                    _data.writeInterfaceToken(DESCRIPTOR);                    mRemote.transact(Stub.TRANSACTION_test, _data, _reply, 0);                    _reply.readException();                } finally {                    _reply.recycle();                    _data.recycle();                }            }

所以我们并不需要在aidl中关注如何 获取到mRemote对象,并调用了transact 方法,因为这些系统已经帮我们实现了,我们现在所关注的是 客户端如何去调用test方法。

按照我们的编程思想,我们只需要在某个类中实现这个接口,并在客户端调用该接口中的方法即可。

实际上aidl中的实现也是这样实现的,首先在Service中 定义一个IBinder对象,而该对象实质上实现是

    public static abstract class Stub extends android.os.Binder implements IKernelInterface 

并在Service中实现 IKernelInterface接口中的方法。

到这里很多读者认为在Service中已经实现了该接口方法,客户端只需要获取到Service对象的实例,然后调用 IKernelInterface 中的方法不就可以了吗?

针对这个问题 我们可能忽略了一个问题,就是 Service很可能不在我们当前这个进程,既然不在我们当前的进程,我们又如何去获取该对象的实例呢。

针对这个问题 android 系统为我们提供了很好的方法。
因为我们接口的实现是通过Service实现的,android系统为我们提供了 bindService方法来获取到远程到Binder对象,然后通过该Binder对象,我们可以得到 我们需要的接口

在aidl文件中我们可以看到该方法

 public static com.liuluchao.kernelaidl.IKernelInterface asInterface(android.os.IBinder obj) {            if ((obj == null)) {                return null;            }            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);            if (((iin != null) && (iin instanceof com.liuluchao.kernelaidl.IKernelInterface))) {                return ((com.liuluchao.kernelaidl.IKernelInterface) iin);            }            return new com.liuluchao.kernelaidl.IKernelInterface.Stub.Proxy(obj);        }

通过asInterface 方法我们可以得到 IKernelInterface 接口,得到该接口之后,我们就可以在客户端调用接口中的方法,这样就实现了跨进程通信。

在上述过程中还存在一个问题就是,我们如何在客户端获取到 远程服务为我们提供的Binde对象?

针对这个问题我们可以通过bindService方法的源码进行分析:

bindService在执行过程中实质上是执行了ContextImpl.bindService(…)方法

 @Override    public boolean bindService(Intent service, ServiceConnection conn,            int flags) {        warnIfCallingFromSystemProcess();        return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),                Process.myUserHandle());    }

可以看出内部直接调用了bindServiceCommon方法 ,

private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler            handler, UserHandle user) {   ....   if (mPackageInfo != null) {            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);        } else {            throw new RuntimeException("Not supported in system context");        }...int res = ActivityManagerNative.getDefault().bindService(                mMainThread.getApplicationThread(), getActivityToken(), service,                service.resolveTypeIfNeeded(getContentResolver()),                sd, flags, getOpPackageName(), user.getIdentifier());}

在该方法中我们只需要关注上述代码即可, 通过上述代码 我们看到实质上是去执行了 ActivityManagerService中的bindService方法

public int bindService(IApplicationThread caller, IBinder token, Intent service,            String resolvedType, IServiceConnection connection, int flags, String callingPackage,            int userId) throws TransactionTooLargeException {    ...        synchronized(this) {            return mServices.bindServiceLocked(caller, token, service,                    resolvedType, connection, flags, callingPackage, userId);        }    }

其内部实质上是调用了ActiveServices.bindServiceLocked(…) 方法

final IServiceConnection conn; int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,            String resolvedType, final IServiceConnection connection, int flags,            String callingPackage, final int userId) throws TransactionTooLargeException{     ....       AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);            ConnectionRecord c = new ConnectionRecord(b, activity,                    connection, flags, clientLabel, clientIntent);    ... c.conn.connected(s.name, b.intent.binder); ...

这段代码很长我们只看我们需要关注的点。
1:通过外部传入的Service 获取到AppBindRecord 对象。
通过AppBindRecord.intent 获取IntentBindRecord 对象;在IntentBindRecord 对象中获取IBinder对象

final class IntentBindRecord {   ...    IBinder binder;    }

这样就获取到了外部Service中的IBinder对象;

2:c.conn 该对象为 IServiceConnection 而IServiceConnection接口中有几个抽象方法

private static class InnerConnection extends IServiceConnection.Stub {            ....            public void connected(ComponentName name, IBinder service) throws RemoteException {              ...            }        }

通过上述代码我们可以看出 在 ActiveServices的 bindServiceLocked 方法中将IBinder对象回调到了外部。
而Service的onBind方法在Service启动时会执行该方法,并将改方法 得到的IBinder对象保存到 IntentBindRecord 中,具体到详细执行过程 读者可以看

http://blog.csdn.net/liuluchao0543/article/details/52434095

该文章就可以了解。

至此我们 通过aidl实现进程间通信,读者应该有了更深一步的了解。

下面是视图对aidl进程间进一步说明:
这里写图片描述

ok 至此进程间的通信已经讲解完了,内容中如存在不正确的地方,还请各位看官指出来。

0 0
原创粉丝点击