安卓Binder解析

本文详细解析了Android系统中Binder作为进程间通信(IPC)方式的工作原理。从定义aidl文件开始,介绍了如何创建Service并模拟跨进程调用,强调了Binder、Stub、Proxy类的角色与功能,以及它们在进程间通信中的作用。最后通过示例代码展示了Binder调用流程,帮助理解Binder如何实现跨进程通信。
摘要由CSDN通过智能技术生成

Binder是Android系统进程间通信(IPC)方式之一,他的使用方法如下
先定义一个aidl文件

package com.example.binderdemo;

interface IBinderTest {
    void setName(String name);
    String getName();
}

接着我们写一个service,里面实现具体的功能

public class BinderService extends Service {

    private String name;

    public BinderService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return mBinderTest;
    }

    IBinderTest.Stub mBinderTest = new IBinderTest.Stub() {
        @Override
        public void setName(String name) throws RemoteException {
            Log.e("xw", "BinderService---setName---" + name);
            BinderService.this.name = name;
        }

        @Override
        public String getName() throws RemoteException {
            Log.e("xw", "BinderService---getName---" + name);
            return name;
        }
    };
}

再写一个应用跨进程太费劲,我们就在一个应用中来模拟跨进程调用,要使我们的service运行在另一个进程也简单,在manifest的对应service中添加process一项

<service
            android:name=".BinderService"
            android:process=":test" />

试验下,在activity中开启service

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        startService(new Intent(this, BinderService.class));
    }

运行应用,在ddms中可以看到的确开启了两个进程
这里写图片描述
接着我们利用bindservice来进行跨进程调用service中的方法

public class MainActivity extends AppCompatActivity {

    IBinderTest mTestBinder;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(this, BinderService.class);
        bindService(intent, conn, BIND_AUTO_CREATE);
        initButton();
    }

    private void initButton() {
        findViewById(R.id.bt_set).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    mTestBinder.setName("xw");
                    Log.e("xw", "MainActivity---setName---xw");
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
        findViewById(R.id.bt_get).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    Log.e("xw", "MainActivity---getName---" + mTestBinder.getName());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    private ServiceConnection conn = new ServiceConnection() {
        /** 获取服务对象时的操作 */
        public void onServiceConnected(ComponentName name, IBinder service) {
            // TODO Auto-generated method stub
            mTestBinder = IBinderTest.Stub.asInterface(service);
        }

        /** 无法获取到服务对象时的操作 */
        public void onServiceDisconnected(ComponentName name) {
            // TODO Auto-generated method stub

        }
    };

}

很简单,acticity中有两个按钮,一个设置,一个获取
这里写图片描述

点击setName按钮时log信息

这里写图片描述

点击getName按钮时log信息

这里写图片描述

可以看到,的确实现了跨进程调用的功能,那么接下来开始分析到底binder是如何工作的

首先我们应该了解aidl并不是必须的,它的功能就是快速的生成用来进程间通信的文件,也就是下面

package com.example.binderdemo;

public interface IBinderTest extends android.os.IInterface
{
    public static abstract class Stub extends android.os.Binder implements com.example.binderdemo.IBinderTest{
        ......
        private static class Proxy implements com.example.binderdemo.IBinderTest
        {
             ......
        }
    }
        ......
}

代码很多,但是他们都是分属于上面的一个接口,两个类,我们分别抽出来看,首先是IBinderTest

public interface IBinderTest extends android.os.IInterface {

    public void setName(java.lang.String name) throws android.os.RemoteException;
    public java.lang.String getName() throws android.os.RemoteException;
}

其实就是继承了IInterface 的一个接口,里面有两个等待实现的方法,也就是我们在aidl文件中定义的方法,接着看Stub

 public static abstract class Stub extends android.os.Binder implements com.example.binderdemo.IBinderTest
    {
        private static final java.lang.String DESCRIPTOR = 

        public Stub()
        {
            this.attachInterface(this, DESCRIPTOR);
        }

        public static com.example.binderdemo.IBinderTest asInterface(android.os.IBinder obj)
        {
            if ((obj==null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin!=null)&&(iin instanceof com.example.binderdemo.IBinderTest))) {
                return ((com.example.binderdemo.IBinderTest)iin);
            }
            return new com.example.binderdemo.IBinderTest.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);
        }
        static final int TRANSACTION_setName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

一个继承了binder实现了IBinderTest接口的抽象类,他的几个方法要着重介绍下
asInterface(android.os.IBinder obj):他是在Client端调用,用以返回一个IBinderTest对象。而且根据传入的参数,他会返回两类对象,一个就是我们实现的具体功能的类,一类则是下面要讲的第三个类Proxy。关于按什么规则返回等下再说
onTransact:我们利用binder进行进程间通信时,其实我们并没有获取到Server端的binder对象,我们获取到的只是binder驱动里与Server端的binder对象对应的一个binder代理对象。真正调用的时候binder驱动就像有一个密码箱,我们将我们需要传递的数据以及调用方法的信息放进密码箱后由binder驱动交给Server端的binder对象,因为binder类是由自己定义的,所以我们当然可以打开密码箱,取出所需要的信息。onTransact相当于打开密码箱的过程,而与之相对应的加密过程则在Proxy类中
最后看看Proxy类

private static class Proxy implements com.example.binderdemo.IBinderTest
        {
            private android.os.IBinder mRemote;
            Proxy(android.os.IBinder remote)
            {
                mRemote = remote;
            }
            ......
            @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(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(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);
        }

在setName和getName中的

android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);

就是将我们的数据按照指定顺序放入

mRemote.transact(TRANSACTION_getName, _data, _reply, 0);
mRemote.transact(TRANSACTION_setName, _data, _reply, 0);

transact的第一个参数告诉Server端的binder应该调用哪个方法

总结说来,IBinderTest 是定义需要实现哪些方法的接口;Stub在Server端和client端都有作用,在Server端负责实现具体业务以及接收包裹,调用指定的方法,在client端则是用asInterface来返回指定的IBinderTest对象;Proxy则是在client端产生作用,负责打包,并将包裹扔给binder驱动

分析了以上三个的各自的功能,那么接下来看看到底他们是如何工作的

来到Client端,再看看代码

bindService(intent, conn, BIND_AUTO_CREATE);

private ServiceConnection conn = new ServiceConnection() {
        /** 获取服务对象时的操作 */
        public void onServiceConnected(ComponentName name, IBinder service) {
            // TODO Auto-generated method stub
            mTestBinder = IBinderTest.Stub.asInterface(service);
        }

        /** 无法获取到服务对象时的操作 */
        public void onServiceDisconnected(ComponentName name) {
            // TODO Auto-generated method stub

        }
    };

bindService完成后,系统会回调ServiceConnection的onServiceConnected方法,然后传给我们的参数中第二个参数为IBinder service,我们也是通过将这个参数转换为我们需要的binder对象来调用Server端binder的方法,那么这个IBinder service我们想当然的认为就是我们需要的实现了具体逻辑的binder对象,其实并不是的,我们来看看他到底是个什么玩意

public void onServiceConnected(ComponentName name, IBinder service) {
            // TODO Auto-generated method stub
            Log.e("MainActivity", service.getClass().getName());
            mTestBinder = IBinderTest.Stub.asInterface(service);
        }

看看log
这里写图片描述

这是个BinderProxy类,瞅瞅他的代码

final class BinderProxy implements IBinder {
    public native boolean pingBinder();
    public native boolean isBinderAlive();

    public IInterface queryLocalInterface(String descriptor) {
        return null;
    }

    public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
        if (Binder.isTracingEnabled()) { Binder.getTransactionTracker().addTrace(); }
        return transactNative(code, data, reply, flags);
    }

    public native String getInterfaceDescriptor() throws RemoteException;
    public native boolean transactNative(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException;
    public native void linkToDeath(DeathRecipient recipient, int flags)
            throws RemoteException;
    public native boolean unlinkToDeath(DeathRecipient recipient, int flags);

    ......

}

很好,一堆native方法,基本是不懂的,不过还好有一个queryLocalInterface方法,我们就需要这个,接着回去看我们的ServiceConnection

IBinderTest mTestBinder = IBinderTest.Stub.asInterface(service);

回顾下这个静态方法做了什么

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

在里面调用了IBinder obj的queryLocalInterface方法,我们的obj是BinderProxy类,往上翻翻看BinderProxy的queryLocalInterface返回了什么,是个null,所以asInterface方法会返回一个Proxy类,而那个IBinder obj作为了Proxy的一个构造参数。
那么现在我们Client端持有了一个Proxy对象,对象里面存储着binder驱动传递给我们的一个BinderProxy对象,而且这个BinderProxy对象在binder驱动中肯定和Server端的binder有着对应关系
最后我们看看调用方法走的什么流程,比如说设置名字

mTestBinder.setName("xw");

mTestBinder是一个Proxy

@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();
                }
            }

就是将我们的数据打包,然后调用mRemote的transact方法

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

mRemote就是binder驱动给我们的BinderProxy对象,看看BinderProxy的transact方法

public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
        if (Binder.isTracingEnabled()) { Binder.getTransactionTracker().addTrace(); }
        return transactNative(code, data, reply, flags);
    }

public native boolean transactNative(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException;

很忧伤。。。又调到了native方法里,不过我们也可以猜测,之后应该是binder驱动做了一些工作,然后将我们的参数传递给Server端,调用binder对象的onTransact方法

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);
        }

然后Server端的binder会根据code值去选择具体的处理方法,实现了跨进程调用的功能

Demo下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值