Android中进程间通信之Binder浅析

对于初级Android程序员来说Binder机制可是一个不容易理解和掌握的知识点,而Binder却在Android系统中有着不可或缺的作用,那么,究竟该如何理解掌握Binder呢,,,,,,众所周知Android系统是基于Linux内核开发的一款手机操作系统,而其采用的进程间通信方式确实Linux所没有的——Binder。那么问题来了:

1、Android中为什么要采用Binder机制来进行进程间通信。
2、Binder到底是什么。
3、Binder是如何来进行进程间通信的,换言之,我们在应用中应该怎么使用Binder来实现进程通信的。

下面我们将一层层解开Binder的神秘面纱。
一、Android为什么要采用Binder机制来进行进程间通信
Linux中进程间通信方式有:管道,system V IPC,socket等,Binder不是Linux所具有的。那么Android中什么地方采用这样的通信方式呢,举个简单的例子,如果我们想要实现一款手机音乐播放app,我们就得需要多媒体Server管理类,我们的app为Client端。但是Server管理类是单独运行在自己的进程中,虽然我们看不见,但它是确确实实的在运行中。这样我们该如何获取Server管理类给我提供的方便的方法呢,Binder就出来了……这就是Server-Client通讯模式。
Android中有大量采用这样CS模式的应用,而Linux支持这样通讯方式的只有Socket,由于socket是一套通用的网络通信方式,其传输效率低下切有很大的开销,比如socket的连接建立过程和中断连接过程都是有一定开销的,在手机这种内存资源比较稀缺的设备上,很明显Socket不适合作为这样的通讯方式。
从安全性方面来讲,Android作为一个开放式,拥有众多开发者的的平台,应用程序的来源广泛,确保智能终端的安全是非常重要的。终端用户不希望从网上下载的程序在不知情的情况下偷窥隐私数据,连接无线网络,长期操作底层设备导致电池很快耗尽等等。传统IPC没有任何安全措施,完全依赖上层协议来确保。首先传统IPC的接收方无法获得对方进程可靠的UID/PID(用户ID/进程ID),从而无法鉴别对方身份。Android为每个安装好的应用程序分配了自己的UID,故进程的UID是鉴别进程身份的重要标志。使用传统IPC只能由用户在数据包里填入UID/PID,但这样不可靠,容易被恶意程序利用。可靠的身份标记只有由IPC机制本身在内核中添加。其次传统IPC访问接入点是开放的,无法建立私有通道。比如命名管道的名称,system V的键值,socket的ip地址或文件名都是开放的,只要知道这些接入点的程序都可以和对端建立连接,不管怎样都无法阻止恶意程序通过猜测接收方地址获得连接。
二、Binder到底是什么
1、对于Android面向对象角度来说,Binder其实就是一个Java类,它实现了IBinder接口。
2、从Android的IPC角度来说,它是一种跨进程通信方式,Binder还可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder。
3、从Android Framework角度来说,Binder是ServiceManager连接各种Manager(ActivityManager、WindowManager,etc)和相应ManagerService的桥梁。
4、从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当你bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务。
上面简单的介绍了Binder,实质上,Binder通信框架定义了四个角色:Server、Client、Binder驱动、ServerManager。Binder驱动是运行在内核中的,而其他的是运行在用户层。
具体的Binder各个角色是如何工作的,请参考http://blog.csdn.net/universus/article/details/6211589。在此也多感谢博主提供了这么好的Binder解析分析。
三、Binder是如何来进行进程间通信的,换言之,我们在应用中应该怎么使用Binder来实现进程通信的。
通过上述的分析,相信大家现在对Binder也已经有啦一个初步的认识,下面我们就来学习一下在我们现实的应用中该如何使用Binder呢。上面说了,在Android应用层中,Binder是客户端和服务端进行通信的媒介,当你bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务。所以我们这里需要Android四大组件之Server组件和AIDL,至于AIDL是Binder通信必须有的吗,答案当然不是,我们完全可以不用AIDL来实现Binder通信。当然,这里我们先用AIDL来实现以下。
首先要明确一下AIDL是什么:AIDL是 Android Interface definition language的缩写,它是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口。说白了它就是一个接口。下面让我通过AIDL方式来创建一个例子:
具体步骤参考Google官方文档:

1、Include the .aidl file in the project src/ directory.
2、Declare an instance of the IBinder interface (generated based on the AIDL).
3、Implement ServiceConnection.
4、Call Context.bindService(), passing in your ServiceConnection implementation.
5、In your implementation of onServiceConnected(), you will receive an IBinder instance (called
service). Call YourInterfaceName.Stub.asInterface((IBinder)service) to cast the returned parameter to YourInterface type.
6、Call the methods
that you defined on your interface. You should always trap
DeadObjectException exceptions, which are thrown when the connection
has broken; this will be the only exception thrown by remote methods.
7、To disconnect, call Context.unbindService() with the instance of your interface.

我们挑重点的几个步骤说一下,一些前提准备工作,相信难不倒大家~
aidl源文件:

package com.android.lf.aidltestproject;
interface ITest{
  int testIntAdd(int a,int b);
  String testString(String str);
}

若是在Eclipse下,sdk 工具会自动帮我们编程生成接口文件放在gen/目录下。

Client源码:

/**
 * Client端
 * @author lf
 * @date 2015-11-18
 */
public class MainActivity extends Activity{

    private ITest iTest;

    private ServiceConnection serviceConnection = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
            iTest = null;
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iTest = ITest.Stub.asInterface(service);
            Toast.makeText(MainActivity.this, "绑定Service成功", Toast.LENGTH_LONG).show();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_layout);
    }

    /**
     * 绑定Service
     * @param view
     */
    public void onBinder(View view){
        Intent service = new Intent(this,MyService.class);
        bindService(service, serviceConnection, BIND_AUTO_CREATE);
    }

    /**
     * 调用testIntAdd(int a,int b)方法
     * @param view
     */
    public void onTestIntAdd(View view){
        try {
            if (iTest!= null) {
                int result =  iTest.testIntAdd(10, 10);
                Toast.makeText(this, "result is -----> " + result, Toast.LENGTH_LONG).show();
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    /**
     * 调用testString(String str)方法
     * @param view
     */
    public void onTestString(View view){
        try {
            if (iTest!=null) {
                String str = iTest.testString("Hello !!");
                Toast.makeText(this, str, Toast.LENGTH_LONG).show();
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    /**
     * 解除绑定Service 调用一次即可,不可重复调用,否则会报错
     * @param view
     */
    public void unBinder(View view) {
        unbindService(serviceConnection);
    }

}

Server源码:

public class MyService extends Service{

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return myBinder;
    }


    private final ITest.Stub myBinder = new ITest.Stub() {

        @Override
        public String testString(String str) throws RemoteException {
            return str;
        }

        @Override
        public int testIntAdd(int a, int b) throws RemoteException {
            return a + b;
        }
    };
}

在清单文件中配置Service:

<application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@android:style/Theme.Holo.Light.NoActionBar" >

        <activity android:name="com.android.lf.aidltestproject.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <service android:name="com.android.lf.aidltestproject.MyService"
            android:process=":remote">
        </service>
    </application>

好,大致的源码就到此为止,大家快速浏览一遍,没什么难度,接下来我们重点分析一下sdk 工具为为我们生成AIDL的接口文件,这样才会明白我们为什么不用AIDL也可以进行Binder通信。
先看源码:

package com.android.lf.aidltestproject;

public interface ITest extends android.os.IInterface {
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements
            com.android.lf.aidltestproject.ITest {
        private static final java.lang.String DESCRIPTOR = "com.android.lf.aidltestproject.ITest";

        /** Construct the stub at attach it to the interface. */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.android.lf.aidltestproject.ITest
         * interface, generating a proxy if needed.
         */
        public static com.android.lf.aidltestproject.ITest asInterface(
                android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.android.lf.aidltestproject.ITest))) {
                return ((com.android.lf.aidltestproject.ITest) iin);
            }
            return new com.android.lf.aidltestproject.ITest.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_testIntAdd: {
                data.enforceInterface(DESCRIPTOR);
                int _arg0;
                _arg0 = data.readInt();
                int _arg1;
                _arg1 = data.readInt();
                int _result = this.testIntAdd(_arg0, _arg1);
                reply.writeNoException();
                reply.writeInt(_result);
                return true;
            }
            case TRANSACTION_testString: {
                data.enforceInterface(DESCRIPTOR);
                java.lang.String _arg0;
                _arg0 = data.readString();
                java.lang.String _result = this.testString(_arg0);
                reply.writeNoException();
                reply.writeString(_result);
                return true;
            }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements
                com.android.lf.aidltestproject.ITest {
            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 int testIntAdd(int a, int b)
                    throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(a);
                    _data.writeInt(b);
                    mRemote.transact(Stub.TRANSACTION_testIntAdd, _data,
                            _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public java.lang.String testString(java.lang.String str)
                    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(str);
                    mRemote.transact(Stub.TRANSACTION_testString, _data,
                            _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_testIntAdd = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_testString = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public int testIntAdd(int a, int b) throws android.os.RemoteException;

    public java.lang.String testString(java.lang.String str)
            throws android.os.RemoteException;
}

先分析服务端代码:

public static abstract class Stub extends android.os.Binder implements
            com.android.lf.aidltestproject.ITest

我们的Service中的myBinder就是Stub的实例,而Stub继承自Binder类实现了ITest接口,所以我们可以把服务端的myBinder看做为Binder类的一个实例。
接下来看:

@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_testIntAdd: {
                data.enforceInterface(DESCRIPTOR);
                int _arg0;
                _arg0 = data.readInt();
                int _arg1;
                _arg1 = data.readInt();
                int _result = this.testIntAdd(_arg0, _arg1);
                reply.writeNoException();
                reply.writeInt(_result);
                return true;
            }
            case TRANSACTION_testString: {
                data.enforceInterface(DESCRIPTOR);
                java.lang.String _arg0;
                _arg0 = data.readString();
                java.lang.String _result = this.testString(_arg0);
                reply.writeNoException();
                reply.writeString(_result);
                return true;
            }
            }
            return super.onTransact(code, data, reply, flags);
        }

可以看到onTransact有四个参数

code , data ,replay , flags

code 是一个整形的唯一标识,用于区分执行哪个方法,客户端会传递此参数,告诉服务端执行哪个方法

data客户端传递过来的参数

replay服务器返回回去的值

flags标明是否有返回值,0为有(双向),1为没有(单向)

我们仔细看case TRANSACTION_min中的代码

data.enforceInterface(DESCRIPTOR);与客户端的writeInterfaceToken对用,标识远程服务的名称

int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();

接下来分别读取了客户端传入的两个参数

int _result = this.testIntAdd(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);

然后执行this.testIntAdd(_arg0, _arg1);,即我们实现的testIntAdd方法;返回result由reply写回。testString(String str)与之相似,大家可以试着分析一下。
这样,我们就是简单分析了一下服务端的源码。下面来继续看客户端源码:

private ServiceConnection serviceConnection = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
            iTest = null;
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iTest = ITest.Stub.asInterface(service);
        }
    };

首先我们在Activity中创建了一个ServiceConnection 对象,然后通过bindService方法启动并绑定后台服务,即Service,在Service中的onBinder方法返回一个IBinder实例。即myBinder对象。可以看到在onServiceConnected方法中有这样

iTest = ITest.Stub.asInterface(service);

一段代码,我们跟进去看看,来到了sdk 工具为我们创建的AIDL接口中,

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

可以看到返回了一个Proxy代理对象,那么Proxy代理又是什么鬼,继续跟进去,发现它是Stub内部的一个静态类,该类里面有一个IBinder对象,即们Remote,此对象在构造方法中被赋初值。

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

结合前面的代,我们可以发现remote就是public void onServiceConnected(ComponentName name, IBinder service)中的service,,,,,,是不是忽然明白什么了,对,Proxy就是把service对象给我们封装了,我们在Activity最终拿到的ITest实例对象就是Proxy对象,Proxy把内部通信细节给封装起来了。这样我们在Activity中调用ITest的testIntAdd方法或者testString方法的时候就是通过Binder内部转换,最终来到Service中的testIntAdd方法或者testString方法。这样通过Binder我们就可以实现两个不同进程之间的通信了。
这里有点绕,大家还得多多理解才行。我们简单的总结一下:

客户端(Activity)拿到后台服务的IBinder对象,通过AIDL通信模型,经过一系列序列化,反序列化最终调用了Service的内部。

好了,有点累了,我们简单了分析了一下Binder的通信方法,初次写博客,有什么不足之处,望大家多多谅解!后续我将继续写一篇,不用AIDL我们也可以实现Binder通信的文章。欢迎大家指正。
参考:
http://blog.csdn.net/lmj623565791/article/details/38461079
http://blog.csdn.net/singwhatiwanna/article/details/19756201

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值