Binder总结分析_应用层

作者: wo883721 | 来源:发表于2017-12-10 14:28 被阅读65次

    Binder总结分析_native层

    Binder是Android系统提供的一种IPC机制(Inter-Process Communication,进程间通信)。

    进程是系统进行资源分配和调度的基本单位,进程的用户空间是互相独立的,那么两个进程之间的数据如何交互?一种简单的方式,采用c/s模式,分成客户端和服务端,客户端分送数据,服务端接收处理后,给出响应,这样就实现了数据的交互。

    Binder架构

    Binder就是一种c/s架构,分为客户端、Binder 驱动、服务端三个模块。

    1. 客户端通过 IBinder.transact() 方法将数据传递给Binder 驱动。
    2. Binder 驱动接收到数据,将这个数据传递给对应的服务端,并将当前线程(客户端线程)阻塞,等待服务端消息返回。
    3. 服务端接收到数据,并调用Binder.onTransact()方法进行相应处理,再将结果返回给Binder 驱动。
    4. Binder 驱动接收的数据,唤醒客户端线程,将结果返回给客户端。

    整个Binder框架远比这个复杂,应用层Binder其实都是基于native(C/C++)层的binder架构,Binder 驱动其实是在Linux的kernel层。这里我们只分析Binder在应用层的体现,而native层的binder架构在下一篇文章中介绍。

    上面的流程,有两个重要的方法 IBinder.transact、Binder.onTransact。

    public interface IBinder {
    
       public boolean transact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException;
    
    }
    

    注意这个方法是定义在IBinder接口中的,发送Binder请求。

    1. code:请求码,主要用于在Binder.onTransact方法中,标识客户端期望调用服务端的哪个函数。
    2. data:客户端请求数据都储存这个对象里面。
    3. reply:服务端返回的数据都储存在这个对象里。(当然要等服务端正确返回之后,这个对象里才有值)。
    4. flags:这是一个标志位,一种是双向,用常量 0 表示, 其含义是服务端执行完指定服务后会返回一定的数据;另一种是单向,用常量 1 表示,其含义是不返回 任何数据。
    public class Binder implements IBinder {
         protected boolean onTransact(int code, Parcel data, Parcel reply,
                int flags) throws RemoteException {
        }
    }
    

    这个方法在Binder里,根据客户端发送过来的数据,调用响应的方法处理后,将结果存储在reply里,然后返回给Binder驱动。

    注意onTransact方法中的data和reply和transact方法里的,并不是同一个对象,因为它们是在两个进程中的。其实过程是这个样子:

    1. 客户端通过transact方法将data数据赋值给自己进程的Binder驱动。
    2. 然后本进程的Binder驱动,会找到目标进程的Binder驱动,将数据重新复制一份传递给目标Binder驱动,然后目标进程的Binder驱动会调用服务端,并将数据传递给它。
    3. 服务端处理完数据后,将结果写入reply,然后在传给Binder驱动。

    因此我们要进行Binder调用,只要获取一个IBinder对象,调用它的transact方法,然后它就会调用到Binder实体的onTransact方法,返回对应的结果。

    但是这样做起来比较繁琐:

    1. 要保证transact和onTransact方法中code是对应的,这样你才能调用到onTransact正确的处理逻辑。
    2. 我们每次都要手动地将数据设置到Parcel中,然后在另一个方法从Parcel中按照放入顺序一一取出来,这样才能使用。

    AIDL (Android接口定义语言)

    那么有没有一种简单地方式,将这些繁琐的操作都封装起来呢?

    一种简单地方式就是定义一系列接口方法,每个接口方法对应一个code,按照方法中参数的顺序,将参数放入Parcel,然后在另一个方法中一一取出。这种方式就是aidl(Android Interface Definition Language)。

    package com.binder.bindertest;
    interface IMyService {
        void setName(String name);
        String getName();
    }
    

    简单定义了IMyService.aidl,下面看看工具帮我们生成的java类。

    package com.binder.bindertest;
    
    public interface IMyService extends android.os.IInterface {
        /**
         * Local-side IPC implementation stub class.
         */
        public static abstract class Stub extends android.os.Binder 
               implements com.binder.bindertest.IMyService {
            private static final java.lang.String DESCRIPTOR = 
                 "com.binder.bindertest.IMyService";
    
            /**
             * Construct the stub at attach it to the interface.
             */
            public Stub() {
                this.attachInterface(this, DESCRIPTOR);
            }
    
            /**
             * Cast an IBinder object into an com.binder.bindertest.IMyService 
             * interface,generating a proxy if needed.
             */
            public static com.binder.bindertest.IMyService asInterface(
                   android.os.IBinder obj) {
                if ((obj == null)) {
                    return null;
                }
                android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
                if (((iin != null) && (iin instanceof com.binder.bindertest.IMyService))) {
                    return ((com.binder.bindertest.IMyService) iin);
                }
                return new com.binder.bindertest.IMyService.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_setName: {
                        data.enforceInterface(DESCRIPTOR);
                        java.lang.String _arg0;
                        _arg0 = data.readString();
                        this.setName(_arg0);
                        reply.writeNoException();
                        return true;
                    }
                    case TRANSACTION_getName: {
                        data.enforceInterface(DESCRIPTOR);
                        java.lang.String _result = this.getName();
                        reply.writeNoException();
                        reply.writeString(_result);
                        return true;
                    }
                }
                return super.onTransact(code, data, reply, flags);
            }
    
            private static class Proxy implements com.binder.bindertest.IMyService {
                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 void setName(java.lang.String name) throws 
                        android.os.RemoteException {
                    android.os.Parcel _data = android.os.Parcel.obtain();
                    android.os.Parcel _reply = android.os.Parcel.obtain();
                    try {
                        _data.writeInterfaceToken(DESCRIPTOR);
                        _data.writeString(name);
                        mRemote.transact(Stub.TRANSACTION_setName, _data, _reply, 0);
                        _reply.readException();
                    } finally {
                        _reply.recycle();
                        _data.recycle();
                    }
                }
    
                @Override
                public java.lang.String getName() 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);
                        mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
                        _reply.readException();
                        _result = _reply.readString();
                    } finally {
                        _reply.recycle();
                        _data.recycle();
                    }
                    return _result;
                }
            }
    
            static final int TRANSACTION_setName = 
                    (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
            static final int TRANSACTION_getName = 
                   (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        }
    
        public void setName(java.lang.String name) throws android.os.RemoteException;
    
        public java.lang.String getName() throws android.os.RemoteException;
    }
    
    

    IMyService

    首先来看一下,IMyService继承了IInterface接口,但没有继承IBinder接口。

    public interface IMyService extends android.os.IInterface {
    }
    
    public interface IInterface
    {
        public IBinder asBinder();
    }
    

    这个接口非常简单,就是返回一个IBinder对象,因此它的子类中肯定有IBinder这个成员变量。所以IMyService是采用的组合的方式,通过IBinder这个成员属性的transact方法,来发送Binder请求的。

    代理类Proxy

    private static class Proxy implements com.binder.bindertest.IMyService {
                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 void setName(java.lang.String name) throws android.os.RemoteException {
                    android.os.Parcel _data = android.os.Parcel.obtain();
                    android.os.Parcel _reply = android.os.Parcel.obtain();
                    try {
                        _data.writeInterfaceToken(DESCRIPTOR);
                        _data.writeString(name);
                        mRemote.transact(Stub.TRANSACTION_setName, _data, _reply, 0);
                        _reply.readException();
                    } finally {
                        _reply.recycle();
                        _data.recycle();
                    }
                }
    
                @Override
                public java.lang.String getName() 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);
                        mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
                        _reply.readException();
                        _result = _reply.readString();
                    } finally {
                        _reply.recycle();
                        _data.recycle();
                    }
                    return _result;
                }
            }
    

    这个Proxy类有一个mRemote成员变量,通过它来进行Binder请求,它实现IMyService方法,只是将方法中的参数,存放到_data中,然后调用transact方法,最后再从_reply中取出结果值,返回给调用处。

    抽象类Stub

        public static abstract class Stub extends android.os.Binder implements 
                     com.binder.bindertest.IMyService {
         private static final java.lang.String DESCRIPTOR = 
                    "com.binder.bindertest.IMyService";
          public Stub() {
                this.attachInterface(this, DESCRIPTOR);
            }
    
     }
    

    继承自Binder类,说明它要实现onTransact方法,作为服务端处理客户端transact方法发送来的消息。它implements了IMyService接口,它并没有真正实现IMyService接口方法,而是由它子类去实现这些方法。
    DESCRIPTOR:这个常量作为Parcel的验证参数。

          public static com.binder.bindertest.IMyService asInterface(android.os.IBinder obj) {
                if ((obj == null)) {
                    return null;
                }
                android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
                if (((iin != null) && (iin instanceof com.binder.bindertest.IMyService))) 
               {
                    return ((com.binder.bindertest.IMyService) iin);
                }
                return new com.binder.bindertest.IMyService.Stub.Proxy(obj);
            }
    
            @Override
            public android.os.IBinder asBinder() {
                return this;
            }
    

    asInterface方法并不是Binder中的方法,它返回一个IMyService对象的实例,目前来看这个IMyService对象有两种类型的子实例:

    1. Stub这个抽样类子类的实例,它就是Binder实体,那么就不用通过Binder机制了,直接调用Binder实体对应方法了。
    2. Proxy这个类的实例。那么它就要调用IBinder的transact方法,通过Binder驱动,接着调用到Stub的onTransact方法,最后服务端实例对应的方法。
    @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_setName: {
                        data.enforceInterface(DESCRIPTOR);
                        java.lang.String _arg0;
                        _arg0 = data.readString();
                        this.setName(_arg0);
                        reply.writeNoException();
                        return true;
                    }
                    case TRANSACTION_getName: {
                        data.enforceInterface(DESCRIPTOR);
                        java.lang.String _result = this.getName();
                        reply.writeNoException();
                        reply.writeString(_result);
                        return true;
                    }
                }
                return super.onTransact(code, data, reply, flags);
            }
    

    根据code码,调用不同的方法,最后将结果写入reply中。

    通过aidl,我们就将Binder请求中一些繁琐的参数准备细节都封装起来了,我们只需要实现接口方法就行了。

    自定义远程service

    下面我们用一个自定义远程service的例子,来详细说明一下。

    package com.binder.bindertest;
    
    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    import android.os.Process;
    import android.os.RemoteException;
    import android.util.Log;
    
    public class MyService extends Service {
        public MyService() {
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            return binder;
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            Log.e("zhang", "onCreate   pid=="+ Process.myPid());
        }
    
        private IBinder binder = new IMyService.Stub() {
    
            private String name;
            @Override
            public void setName(String name) throws RemoteException {
                this.name = name;
                Log.e("zhang", "setName  name==  "+name+"   pid=="+ Process.myPid());
            }
    
            @Override
            public String getName() throws RemoteException {
                Log.e("zhang", "getName  name==  "+name+"   pid=="+ Process.myPid());
                return name;
            }
        };
    }
    
    

    自定义一个MyService,在onBind方法中返回一个IMyService.Stub对象。它是Binder子类,实现了IMyService接口的方法。

    package com.binder.bindertest;
    
    import android.content.ComponentName;
    import android.content.Intent;
    import android.content.ServiceConnection;
    import android.os.IBinder;
    import android.os.Process;
    import android.os.RemoteException;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    
    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    bindService();
                }
            });
        }
    
        private void bindService() {
            Intent intent = new Intent(this, MyService.class);
            bindService(intent, connection, BIND_AUTO_CREATE);
        }
    
        private ServiceConnection connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.e("zhang", "onServiceConnected start pid=="+ Process.myPid());
                IMyService myService = IMyService.Stub.asInterface(service);
                try {
                    myService.setName("zhang");
                    String result = myService.getName();
                    Log.e("zhang", "onServiceConnected result=="+result+"  pid=="+ Process.myPid());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
    
            }
        };
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            unbindService(connection);
        }
    }
    
    

    通过bindService方法启动并绑定自定义的Service,然后在ServiceConnection的onServiceConnected获取我们想要的IBinder对象。

    主要这个IBinder对象,根据所在进程不同,值不一样。

    1. 如果MainActivity和MyService在同一个进程,那么这个IBinder对象就是MyService onBind方法返回的那个IBinder实例。
    2. 如果MainActivity和MyService不在同一个进程,那么这个IBinder对象就是返回的那个IBinder实例代理对象。
      所以调用asInterface方法获取的结果也是不一样的。
        <service
                android:name=".MyService"
                android:process=":remote"
                >
                <intent-filter>
                    <action android:name="com.binder.bindertest.MyService" />
                </intent-filter>
            </service>
    

    最后注意在AndroidManifest.xml文件中将<service 修改成上面方式。
    这样当点击按钮时,会发现MainActivity和MyService中调用Process.myPid()方法获取的pid是不一样的,但是它们实现了数据交互。

    各种系统服务

    在android中各种系统服务就是通过Binder机制进行信息交互的。来看看它们是怎么实现的。就以InputMethodManager为例:

    InputMethodManager

    public static InputMethodManager getInstance() {
            synchronized (InputMethodManager.class) {
                if (sInstance == null) {
                    try {
                        sInstance = new InputMethodManager(Looper.getMainLooper());
                    } catch (ServiceNotFoundException e) {
                        throw new IllegalStateException(e);
                    }
                }
                return sInstance;
            }
        }
    

    调用InputMethodManager构造函数方法。

    InputMethodManager(Looper looper) throws ServiceNotFoundException {
            this(IInputMethodManager.Stub.asInterface(
                    ServiceManager.getServiceOrThrow(Context.INPUT_METHOD_SERVICE)), looper);
        }
    

    它也有个IInputMethodManager接口,定义了对InputMethodManager操作一系列方法,通过IBinder对象向InputMethodManager的服务端进行请求,而它的IBinder对象是通过ServiceManager获取。

    ServiceManager

    public static IBinder getServiceOrThrow(String name) throws ServiceNotFoundException {
            final IBinder binder = getService(name);
            if (binder != null) {
                return binder;
            } else {
                throw new ServiceNotFoundException(name);
            }
        }
    

    调用getService方法,如果得到的binder对象是空,就抛出异常。

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

    先从缓存sCache中获取,如果获取不到,那么就要通过IServiceManager对象来获取了。

    public interface IServiceManager extends IInterface
    {
        public IBinder getService(String name) throws RemoteException;
       
        public IBinder checkService(String name) throws RemoteException;
    
        public void addService(String name, IBinder service, boolean allowIsolated) 
          throws RemoteException;
    
        public String[] listServices() throws RemoteException;
    
        public void setPermissionController(IPermissionController controller)
                throws RemoteException;
        
        static final String descriptor = "android.os.IServiceManager";
    }
    
    1. IServiceManager是一个接口,继承了IInterface(就相当于我们自定义的IMyService),
    2. ServiceManagerNative类继承了Binder实现了IServiceManager(就相当于Stub)
    3. ServiceManagerProxy实现了IServiceManager(就相当于Proxy)。这两个类都在ServiceManagerNative.java文件中。
    private static IServiceManager getIServiceManager() {
            if (sServiceManager != null) {
                return sServiceManager;
            }
            // Find the service manager
            sServiceManager = ServiceManagerNative
                    .asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
            return sServiceManager;
        }
    

    可以看出IServiceManager中所需要的远程代理IBinder实例,是由BinderInternal.getContextObject()方法返回的,而这个方法是通过jni从native端获取的。

    小结

    由此可以得出,每个系统服务Service,都有一个IXXX接口(例如InputMethodManager对应IInputMethodManager),这个接口定义了操作本服务一系列方法,这个接口的实例中有一个IBinder属性,通过它来调用远程Service真正实现的方法。而IBinder这个实例是通过IServiceManager获取的。
    而IServiceManager它对应的实例ServiceManagerProxy也是一个代理对象。它也是通过Binder机制,从服务端ServiceManager中获取这个IBinder对象的。而服务端ServiceManager是在native层实现的,下一章我们会说明的。

    相关文章

      网友评论

        本文标题:Binder总结分析_应用层

        本文链接:https://www.haomeiwen.com/subject/kviuixtx.html