4.10Android学习

一、今日学习内容

今日继续binder的学习

根据昨天学习的两个方法我们就可以实现Client和Server端的通信,接下来我们看看具体该怎么使用。 在Server接收到Client传来的消息(data)时,会对data进行验证data.enforceInterface(DESCRIPTOR),DESCRIPTOR是一个字符串类型的描述符,当data的描述符跟DESCRIPTOR相同时才能通过验证。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class Stub extends Binder {
    //描述符
    public static final java.lang.String DESCRIPTOR = "MyTestBinder";
    //code 方法描述符
    public static final int TRANSACTION_test0 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    public static final int TRANSACTION_test1 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
 
    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        switch (code) {
            case TRANSACTION_test0:
                //验证描述符
                data.enforceInterface(DESCRIPTOR);
                //执行方法
                test0();
                return true;
            case TRANSACTION_test1:
                //验证描述符
                data.enforceInterface(DESCRIPTOR);
                //执行方法
                test1(data, reply);
                return true;
        }
        return super.onTransact(code, data, reply, flags);
    }
 
    //无返回值
    private void test0() {
        Log.d("MyBinderServer", "test0");
    }
 
    //有返回值
    private void test1(Parcel data, Parcel reply) {
        Log.d("MyBinderServer", "test1");
        reply.writeInt(data.readInt() + 1);
    }
}

我们知道,要想实现Client和Server端的通信连接,就必须让client知道server端的地址,就跟Http请求,我们要知道服务端的ip和端口。Binder通信其实也是一样的,那么我们怎么让Client拿到Server的地址呢? 一种是跟Http请求一样,我们知道Http请求要把域名转换成ip和端口,这就是DNS,我们也需要一个Binder的DNS。安卓中也为我们提供了Binder的“DNS”那就是ServiceManager,ServiceManager中注册了所有系统服务(如MediaServer等),我们可以使用ServiceManager拿到远程的Binder地址,这种方式叫做有名Binder查找(有名Binder,如MediaServer等这些系统服务被注册的时候都是有名字的,比如,我们通过WINDOW_SERVICE这个名字就能拿到WindowManager)。但是问题是向ServiceManager注册服务的过程是系统进程实现的,我们的应用进程不能注册自己的Binder。 另一种就是利用有名的Binder来辅助传递匿名的Binder,也就是说如果有某个有名Binder服务它提供了传递Binder的方法,那么我们就可以通过这个Binder服务来传递我们的匿名Binder,我们查找到这个有名的Binder是不是就能拿到我们的匿名Binder。正好AMS其实提供了这样的功能,它通过Service.onBind把匿名的Binder封装在了Service里面供我们调用。

1
2
3
4
5
6
7
public class MyService extends Service {
     
    @Override
    public IBinder onBind(Intent intent) {
        return new Stub();
    }
}

我们使用binderService()来获取远程的Binder。

1
2
3
4
5
6
7
8
9
10
11
12
Intent serviceIntent = new Intent(this, MyService.class);
        bindService(serviceIntent, new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                //service可以理解为是远程Binder的地址,我们利用他跟远程通信,C++层会转换这个IBinder跟Binder进行通信
            }
 
            @Override
            public void onServiceDisconnected(ComponentName name) {
 
            }
        }, BIND_AUTO_CREATE);

获取到Binder之后我们补充好通信的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
bindService(serviceIntent, new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        Parcel data0 = Parcel.obtain();//请求参数
        Parcel reply0 = Parcel.obtain();//响应参数
        Parcel data1 = Parcel.obtain();
        Parcel reply1 = Parcel.obtain();
 
        //调用第一个方法
        try {
            //添加描述符
            data0.writeInterfaceToken(Stub.DESCRIPTOR);
            /*
             * 写入参数,要想传递多个int参数,顺序调用writeInt
             * data0.writeInt(10);
             * data0.writeInt(20);
             * 获取
             * int num10 = data0.readInt();
             * int num20 = data0.readInt();
             */
            data0.writeInt(10);
            //发起远程调用
            service.transact(Stub.TRANSACTION_test0, data0, reply0, 0);
            reply0.readException();
        } catch (RemoteException e) {
            e.printStackTrace();
        } finally {
            data0.recycle();
            reply0.recycle();
        }
 
        //调用第二个方法
        try {
            //添加描述符
            data1.writeInterfaceToken(Stub.DESCRIPTOR);
            data1.writeInt(10);
            //发起远程调用
            service.transact(Stub.TRANSACTION_test1, data1, reply1, 0);
            reply1.readException();
            //读取返回值
            int num = reply1.readInt();
            Log.d(TAG, "reply value: " + num);
        } catch (RemoteException e) {
            e.printStackTrace();
        } finally {
            data1.recycle();
            reply1.recycle();
        }
    }
 
    @Override
    public void onServiceDisconnected(ComponentName name) {
 
    }
}, BIND_AUTO_CREATE);

为了方便调用,我们写一个代理类来封装通信过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public class Proxy {
    //描述符
    public static final java.lang.String DESCRIPTOR = "MyTestBinder";
    //code 方法描述符
    public static final int TRANSACTION_test0 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    public static final int TRANSACTION_test1 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    private IBinder mRemote;
 
    public Proxy(IBinder remote) {
        this.mRemote = remote;
    }
 
    public void test1() {
        Parcel data0 = Parcel.obtain();//请求参数
        Parcel reply0 = Parcel.obtain();//响应参数
        //调用第一个方法
        try {
            //添加描述符
            data0.writeInterfaceToken(DESCRIPTOR);
            /*
             * 写入参数,要想传递多个int参数,顺序调用writeInt
             * data0.writeInt(10);
             * data0.writeInt(20);
             * 获取
             * int num10 = data0.readInt();
             * int num20 = data0.readInt();
             */
            data0.writeInt(10);
            //发起远程调用
            mRemote.transact(TRANSACTION_test0, data0, reply0, 0);
            reply0.readException();
        } catch (RemoteException e) {
            e.printStackTrace();
        } finally {
            data0.recycle();
            reply0.recycle();
        }
    }
 
    public int test2() {
        Parcel data1 = Parcel.obtain();
        Parcel reply1 = Parcel.obtain();
        //调用第二个方法
        int num = 0;
        try {
            //添加描述符
            data1.writeInterfaceToken(DESCRIPTOR);
            data1.writeInt(10);
            //发起远程调用
            mRemote.transact(TRANSACTION_test1, data1, reply1, 0);
            reply1.readException();
            //读取返回值
            num = reply1.readInt();
        } catch (RemoteException e) {
            e.printStackTrace();
        } finally {
            data1.recycle();
            reply1.recycle();
        }
        return num;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
bindService(serviceIntent, new ServiceConnection() {
           @Override
           public void onServiceConnected(ComponentName name, IBinder service) {
               Proxy proxy = new Proxy(service);
               proxy.test1();
               int i = proxy.test2();
           }
 
           @Override
           public void onServiceDisconnected(ComponentName name) {
 
           }
       }, BIND_AUTO_CREATE);

二、遇到的问题

暂无

三、明日计划

继续binder的学习

原文地址:https://www.cnblogs.com/zyljal/p/14909807.html