AIDL与Binder下篇_顾梁

这次我们来分析Binder对象。用来更好的去理解AIDL
Binder分为三部分,服务端,客户端,和Binder驱动。
首先讲下Binder驱动:
Binder它的作用是进行进程间通信的。既然它能提供进程间通信的功能。那么它应该是系统级别的全局性。
而Binder驱动存在于Android内核中,它能提供全局的服务。
Binder服务端:
一个Binder的服务端,就是一个Binder对象,当我们new 出一个Binder对象

那么它内部会起一个线程。用于接收Binder驱动发送的消息。并且Binder驱动 会创建了一个 mRemote对象。它也是个Binder对象。mRemote是当不同进程间通信的时候会使用到。
我们可以说。每个服务端的Binder对象。在Binder驱动中都会有一个对应的mRemote对象。当不同进程间访问的时候,客户端需要获得mRemote对象的引用。
Binder客户端:
这里我们首先说一下Binder的两个方法
transact(),和onTransact(),这两个方法是对应的。
而我们的客户端就是获得服务端的Binder对象引用(同进程),或者是mRemote的引用(不同进程)。最后然后去调用transact()方法。
这里我们测试下同进程间客户端获取的Service与服务端所创建的
Service是否是一个对象。
我们修改一下与提供计算的Service同进程中的
Activity中的代码(实际这个Activity就是客户端了)

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    protected void onResume() {
        super.onResume();
        //startService(new Intent(this,MyService.class));
    }
    ServiceConnection conn = new ServiceConnection() {
        public void onServiceDisconnected(ComponentName name) {
        }
        public void onServiceConnected(ComponentName name, IBinder service) {
            IJiaFa mService= Stub.asInterface(service);
            try {
                Log.i("TAG", "同进程间的mService="+mService);
                Log.i("TAG", "从服务端算出的结果是+"+mService.add(5, 10));
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    };
    public void onClick(View v){
        bindService(new Intent("com.example.jiafa"), conn, BIND_AUTO_CREATE);
    }
}

Service中的代码块(服务端)

public class MyService extends Service {
    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        mBinder binder=new mBinder();
        Log.i("TAG", "服务端的binder="+binder);
       return binder;
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }


    class mBinder extends IJiaFa.Stub{
        public int add(int x, int y) throws RemoteException {
            return (x+y)*10;
        }
    }

}

输出结果。
这里写图片描述
可以看到,确实是同一个对象
客户端实际就是获得了Binder对象的引用后。然后调用transact()方法
transact()方法需要三个参数

 public final boolean transact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException {
        if (false) Log.v("Binder", "Transact: " + code + " to " + this);
        if (data != null) {
            data.setDataPosition(0);
        }
        boolean r = onTransact(code, data, reply, flags);
        if (reply != null) {
            reply.setDataPosition(0);
        }
        return r;
    }

对应的 onTransact()方法前三个参数。

 protected boolean onTransact(int code, Parcel data, Parcel reply,int flags) throws RemoteException {
            //代码略
            }

当我们客户端调用transact()会有两种情况。如果服务端的Binder没有重写
transact()方法,那么走的逻辑就是transact()中调用onTransact()
还有一种情况就是 我们重写了transact(),而mRemote对象就是重写过的。重写的内容包括:
这里写图片描述
换句话说,当不同进程间通信。客户端获取mRemote对象的引用后。调用
transact()方法,会挂起当前客户端线程。并且把参数发送给了服务端。
服务端运行完后。会将客户端线程唤醒。所以当我们知道服务端的是耗时操作那么就不应该在UI线程去执行transact()方法。

以上我们就Binder对象的三个模块。
现在我们分析下transact()方法的三个参数
code:code一般是用来做方法标示的。当服务端有多个方法的时候,可以根据传入code的数字,服务端相应的提供方法。
data: 存放的是数据。客户端调用服务端的方法的时候,需要的数据就封装到里面。
reply:当服务端方法有返回值的时候,可以将数据写入进去。
onTransact()中的前三个参数是一一对应的。
不过需要注意的是:onTransact()的返回值为false的时候。客户端的请求会失败。

现在我们按自己的理论,来利用Binder进行进程间通信。不去利用AIDL
这里我们提出疑问。我们的客户端怎么去获得Binder对象的引用。
Android给我们提供的方式就是。当bindService()在
这里写图片描述
中。传递的service,就是系统给我们的Binder对象引用。
那么开始;
首先我们说下思路。
服务端的思路:
1.我们服务端的Binder对象,必须重写onTransact(),因为不重写就没办法去调用我们自己定义的方法了。
2.我们定义方法标示。
3.按一定的顺序从data中获取数据。
4.如果需要返回值,我们返回添加到reply中。
贴上代码:

public class MyService extends Service {
    final static int M_ADD=101;
    public MyService() {
    }
    @Override
    public IBinder onBind(Intent intent) {
        return new mBinder();
    }
class mBinder extends Binder{
    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        switch (code) {
        case M_ADD:
         int agr0=data.readInt();
         int agr1=data.readInt();
        reply.writeInt(add(agr0,agr1)); 
        return true;
        default:
            break;
        }
        return super.onTransact(code, data, reply, flags);
    }

}

public int add(int x,int y){
    return x*10+y*5;
}

}

客户端的思路:
1.获取Binder对象的引用,然后调用transact()方法。
2.我们需要准备transact()的三个参数。
贴上代码

public class MainActivity extends Activity {
    ServiceConnection conn;
    final static int  M_ADD=110;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        conn=new ServiceConnection() {
            @Override
            public void onServiceDisconnected(ComponentName name) {
            }
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Parcel  data= Parcel.obtain();
                Parcel  reply=Parcel.obtain();
                try {
                    data.writeInt(10);
                    data.writeInt(5);
                    service.transact(M_ADD, data, reply, 0);
                    Log.i("TAG", "获得的算术结果是="+reply.readInt());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        };

    }
    public void onClick(View v){
        bindService(new Intent("com.example.jiafa"), conn, BIND_AUTO_CREATE);
    }
}

好了,终于到出结果的时候了。运行—–
这里写图片描述
总结:我们可以看到不利用AIDL也可以完成进程间通信,
这里我们可以在回去看下系统帮助我们生产的代码。实际上一部分是客户端
使用的,一部分是服务端使用的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值