IPC利用纯Binder通信(非aidl)

Posted 從前以後

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IPC利用纯Binder通信(非aidl)相关的知识,希望对你有一定的参考价值。

android中,当两个类都在同一个进程里执行时,两者之间的沟通,只要采取一般的函数调用(Function Call)就行了,既快速又方便。一旦两个类分别在不同的进程里执行时,两者之间的沟通,就不能采取一般的函数调用途径了。只好采取IPC沟通途径。 Android框架的IPC沟通仰赖单一的IBinder接口。此时Client端调用IBinder接口的transact()函数,透过IPC机制而调用到远方(Remote)的onTransact()函数。此时的onTransact()函数是Binder类中的函数,之所以transact()能执行到远方的onTransact(),原因是因为Binder实现了IBinder接口,Binder类复写的transact()函数内部调用了自己定义的onTransact()函数。 下面不用aidl,纯Binder来实现一次进程间的通信。
服务端实现:
首先定义一个接口:
接口定义两个方法,加法和减法。

public interface IEthan 
int  getAdd(int i,int j);
int  getminus(int i,int j);

下面是主角MyBinder类

public class MyBinder extends Binder implements IEthan 
    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException 
        switch (code) //code为客户端Transact发送过来的code
        case 0:
            int ii[] =  0, 0 ;//此处数组长度必须和客户端发来的数组长度一致,不然报错
            data.readIntArray(ii);//获取从客户端发来的int数组,对应的getAdd中的I和j
            int add = getAdd(ii[0], ii[1]);
            Log.e("ethan", "两数相加结果"+add);
            break;
        case 1:
            int jj[] =  0, 0 ;//此处数组长度必须和客户端发来的数组长度一致,不然报错
            data.readIntArray(jj);//获取从客户端发来的int数组,对应的getminus中的I和j
            int minus = getminus(jj[0],jj[1]);
            Log.e("ethan", "两数相减结果"+minus);
            break;
        
        return false;
    
    @Override
    public int getAdd(int i, int j) 
        return i+j;
    
    @Override
    public int getminus(int i, int j) 
        return i-j;
    

MyBinder继承了Binder类同时实现了IEthan接口中的方法。同时在onTransact中得到客户端发过来的数据,并且解析,然后调用实现getAdd()和getminus();为什么要在onTransact执行方法,因为客户端拿到的是一个IBinder接口对象,会调用IBinder中的Transact()方法,由于Transact()方法被IBinder的实现类重写了,根据多态,客户端接口对象会调用Binder(实现类)中的Transact()方法,而Binder类中的Transact()内部又会调用onTransact(),因此最终会执行到onTransact()方法中。
下面就是服务端服务类了

public class MyService extends Service 
    MyBinder mybinder = new MyBinder();
    @Override
    public IBinder onBind(Intent intent) 
        Log.e("ethan", "Service binder==" + mybinder);
        return mybinder;
    
    @Override
    public void onCreate() 
        super.onCreate();
        Log.e("ethan", "service on create");
    
@Override
public int onStartCommand(Intent intent, int flags, int startId) 
    Log.e("ethan", "service  onStartCommand");
    return super.onStartCommand(intent, flags, startId);

服务端代码很简单,就是实例化一个MyBinder对象,然后在onBind方法中将这个对象返回出去就行了。

客户端代码

public class MainActivity extends Activity 
    private IBinder myBinder;
    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button=(Button) findViewById(R.id.button);
        button.setOnClickListener(new OnClickListener() 
            @Override
            public void onClick(View v) 
                Intent intent=new Intent("com.example.test.MyService");
                bindService(intent, myConnection, Service.BIND_AUTO_CREATE);
            
        );
    
ServiceConnection myConnection=new ServiceConnection() 
    @Override
    public void onServiceDisconnected(ComponentName name) 
    
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) 
                Log.e("ethan", "service=="+service);
                myBinder=service;
                Parcel data1 = Parcel.obtain();
                int ii[]=3,5;
                data1.writeIntArray(ii);
                Parcel data2 = Parcel.obtain();
                Parcel data3 = Parcel.obtain();
                int jj[]=5,3;
                data3.writeIntArray(jj);
                Parcel data4 = Parcel.obtain();
        try 
            myBinder.transact(0, data1,data2 , 1);
            myBinder.transact(1, data3,data4 , 1);
         catch (RemoteException e) 
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        
    
;

客户端代码很简单,定义了一个Button,点击的时候会执行bindService(intent, myConnection, Service.BIND_AUTO_CREATE); 其中intent是隐式启动Service的意图(隐式要给服务在AndroidManifest.xml中注册Action哈),myConnection 是ServiceConnection 接口的一个对象,该对象在MainActivity 初始化时就实例化。
此时所有的工作就做好了,可以愉快的事件进程间通信了。

03-24 04:41:11.363: E/ethan(16525): service==android.os.BinderProxy@526fc6f0
03-24 04:41:11.363: E/ethan(16168): 两数相加结果8
03-24 04:41:11.363: E/ethan(16168): 两数相减结果2

notes:
1,上述onServiceConnected(ComponentName name, IBinder service)方法中service参数其实就是服务端MyService中onBind方法中返回的MyBinder对象的代理对象。上述service==android.os.BinderProxy@526fc6f0可以看出。
2,虽然是service是代理对象,但是类型是Ibinder接口类型,也就是service是一个接口对象。
3,service 调用IBinder中的transact(),而IBinder被Binder实现并被服务端的MyBinder继承,因此执行MyBinder中的transact()方法,而transact()方法已被父类Binder重写调用onTransact(),最终会执行到MyBinder中的onTransact()。
4,myBinder.transact(0, data1,data2 , 1);中第一个参数对应服务端onTransact()的第一个参数,表示方法的Code。第二,第三是Parcel类型对象,该对象分别封装了一个int类型数组,也就是被用来相加和相减的俩个数字。
ps:aidl其实是系统自动对自己定义的接口(例如上述的IEthan)进行了一个封装。aidl中服务端需要实例化一个.stub对象,其实就是一个Binder对象,然后在service的onBinder方法中返回这个对象。客户端在onServiceConnected方法中对service进行强制类型转换成对应的接口对象,调用接口中的方法。其实就是在客户端进行了一次借口封装,然后接收onServiceConnected中的service。其后调用的接口种方法,本质还是通过transact来传送的,只不过是对其中的参数(例如Parcel对象)进行了打包而已。

有时间再写写Binder原理。

以上是关于IPC利用纯Binder通信(非aidl)的主要内容,如果未能解决你的问题,请参考以下文章

谈谈Android Binder机制及AIDL使用

Android IPC机制:浅谈Binder的使用

Android基础——初学者必知的AIDL在应用层上的Binder机制

Android跨进程通信Binder机制与AIDL实例

Android跨进程通信Binder机制与AIDL实例

AIDL跨进程通信