Binder service入门—Framework binder service

2014/04/21

本文原创作者:Cloud Chou. 欢迎转载,请注明出处和本文链接

1.前言

上一篇介绍了natvie binder Service,本篇将介绍如何创建框架层binder service,并交给ServiceManager管理,客户端通过ServiceManager获取binder service的引用,然后测试binder service。

Binder service入门系列:

2.程序构成

Framework程序的开发必须要在源码开发环境下,本示例在vendor目录下建立了子目录shuame,然后把工程放在该目录下。

整个工程都可以在github上下载: https://github.com/cloudchou/FrameworkBinderTest

程序由服务端和客户端组成,服务端包括启动服务端的shell脚本bserver和放在framework目录的bserver.jar,客户端包括启动客户端的shell脚本bclient和放在framework目录的bclient.jar。

服务端 Android.mk如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
LOCAL_PATH:= $(call my-dir)
#生成framwork目录下的库bserver.jar
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_MODULE := bserver 
LOCAL_MODULE_TAGS := optional
include $(BUILD_JAVA_LIBRARY)
#生成system/bin目录下的shell脚本bserver 
include $(CLEAR_VARS)
LOCAL_MODULE := bserver
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(TARGET_OUT)/bin
LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
LOCAL_SRC_FILES := bserver
include $(BUILD_PREBUILT)

客户端Android.mk如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
LOCAL_PATH:= $(call my-dir)

#生成framwork目录下的库bclient.jar
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_MODULE := bclient 
LOCAL_MODULE_TAGS := optional
include $(BUILD_JAVA_LIBRARY)

#生成system/bin目录下的shell脚本bclient
include $(CLEAR_VARS)
LOCAL_MODULE := bclient
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(TARGET_OUT)/bin
LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
LOCAL_SRC_FILES := bclient
include $(BUILD_PREBUILT)

3.程序源码构成

程序源码构成如下图所示:

framework_fbs

整个程序由服务端和客户端两部分组成,分别放在bserver和bclient目录,顶层目录只有一个Android.mk,该Android.mk的内容如下所示:

1
2
#包含所有子目录的makefiles,这样在顶层目录就可以编译所有模块
include $(call all-subdir-makefiles)

ICloudManager:服务端和客户端共用同一个接口文件ICloudManager.java,这里通过软链接实现共用,该接口声明了binder service供外界调用的方法。

CloudManager:binder service实体类,接收客户端的调用,进行相关逻辑处理后返回结果给客户端

BServer: 创建CloudManage对象,并调用ServiceManager注册binder service

CloudManagerProxy: binder service引用类,其实是binder service在客户端的代理,客户端通过该类调用服务端的操作

BClient: 测试binder service的客户端,先通过ServiceManager获取binder对象,然后再利用它创建CloudManagerProxy对象,通过CloudManagerProxy对象调用服务端的操

实现Framework binder service的步骤

1) 定义binder service接口ICloudManager以及binder service的描述字符串,并为接口里的每个方法声明对应的操作常量:

1
2
3
4
5
6
7
8
9
10
11
12
public interface ICloudManager extends IInterface {
  //binder sevice的描述字符串
  static final java.lang.String DESCRIPTOR = "com.cloud.test.BServer";
  //操作
  public void print(String str) throws RemoteException ;
  public int add(int a, int b) throws RemoteException ;
  //操作常量
  static final int TRANSACTION_print = 
         (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
  static final int TRANSACTION_add = 
        (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}

声明binder service接口时必须从IInterface继承,IInterface接口如下所示:

1
2
3
4
5
6
7
8
9
public interface IInterface
{
    /**
     * Retrieve the Binder object associated with this interface.
     * You must use this instead of a plain cast, so that proxy objects
     * can return the correct result.
     */
    public IBinder asBinder();
}

IInterface声明了asBinder方法,用于转为Binder对象。

2) 实现binder service实体类CloudManager

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
63
64
public class CloudManager extends Binder implements ICloudManager {  
   public CloudManager() {
      //保存 binder service描述字符串
      this.attachInterface(this, DESCRIPTOR);
   }

   @Override
   public IBinder asBinder() {
      return this;
   }

  //静态方法 转为ICloudManager接口对象
   public static com.cloud.test.ICloudManager asInterface(
         android.os.IBinder obj) {
      if ((obj == null)) {
         return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin != null) && 
          (iin instanceof com.cloud.test.ICloudManager))) {
         return ((com.cloud.test.ICloudManager) iin);
      }
      return null;
   }

  //接收客户端的调用,根据不同的操作码进行不同的处理,然后返回结果给客户端
   @Override
   protected boolean onTransact(int code, Parcel data, 
                            Parcel reply, int flags)
         throws RemoteException {
      switch (code) {
      case INTERFACE_TRANSACTION: {
         reply.writeString(DESCRIPTOR);
         return true;
      }
      case TRANSACTION_print: {
         data.enforceInterface(DESCRIPTOR);
         String str = data.readString();
         print(str);
         reply.writeNoException();
         return true;
      }
      case TRANSACTION_add: {
         data.enforceInterface(DESCRIPTOR);
         int a = data.readInt();
         int b = data.readInt();
         int c = add(a, b);
         reply.writeNoException();
         reply.writeInt(c);
         return true;
      }
      }
      return super.onTransact(code, data, reply, flags);
   }

   @Override
   public void print(String str) {
      System.out.println(str);
   }

   @Override
   public int add(int a, int b) {
      return a + b;
   }

注意必须从Binder继承,并实现ICloudManager接口

3) 实现服务端 BServer,创建CloudManager对象,并通过ServiceManager注册服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class BServer {
   public static void main(String[] args) {
      System.out.println("Cloud Manager Service Starts");
        //准备Looper
      Looper.prepareMainLooper();         
      android.os.Process.setThreadPriority(
                android.os.Process.THREAD_PRIORITY_FOREGROUND);
        //注册binder service
      ServiceManager.addService("CloudService", new CloudManager());
        //进入Looper死循环,
      Looper.loop();
   }

}

4) 实现服务端脚本bserver

1
2
3
4
base=/system
export CLASSPATH=$base/framework/bserver.jar
#调用app_process启动BServer
exec app_process $base/bin com.cloud.test.BServer "$@"

5) 实现binder service引用类CloudManagerProxy

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
public class CloudManagerProxy implements ICloudManager {
   private android.os.IBinder mRemote;
    //保存通过ServiceManager查询服务得到的IBinder对象
   public CloudManagerProxy(android.os.IBinder remote) {
      mRemote = remote;
   }

   public java.lang.String getInterfaceDescriptor() {
      return DESCRIPTOR;
   }
 
   @Override
   public void print(String str) throws RemoteException {
      android.os.Parcel _data = android.os.Parcel.obtain();
      android.os.Parcel _reply = android.os.Parcel.obtain();
      try {
         _data.writeInterfaceToken(DESCRIPTOR);
         _data.writeString(str);
         mRemote.transact(TRANSACTION_print, _data, _reply, 0);
         _reply.readException();
      } finally {
         _reply.recycle();
         _data.recycle();
      }
   }

   @Override
   public int add(int a, int b) throws RemoteException {
      android.os.Parcel _data = android.os.Parcel.obtain();
      android.os.Parcel _reply = android.os.Parcel.obtain();
      int result=0;
      try {
         _data.writeInterfaceToken(DESCRIPTOR);
         _data.writeInt(a);
         _data.writeInt(b);
         mRemote.transact(TRANSACTION_add, _data, _reply, 0);
         _reply.readException();
         result=_reply.readInt();
      } finally {
         _reply.recycle();
         _data.recycle();
      }
      return result;
   }

   @Override
   public IBinder asBinder() {
      return mRemote;
   }

} 

接口方法的实现实际上是通过调用IBinder对象的transact方法将调用参数传递给服务端,然后读取服务端返回的结果,再返回给接口方法的调用者。

6) 实现客户端BClient

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class BClient { 

   /** 
    * 
    * @param args
    *            The command-line arguments
    * @throws RemoteException
    */
   public static void main(String[] args) throws RemoteException {
      System.out.println("==========Client starts===========");
       //通过ServiceManager查询服务获得IBinder对象
      IBinder binder = ServiceManager.getService("CloudService");
        //创建CloudManagerProxy对象
      ICloudManager manager = new CloudManagerProxy(binder);
        //通过CloudManagerProxy对象调用接口的方法
      manager.print("Hello world");
      int result = manager.add(2, 3);
      System.out.println("manager.add(2, 3)=" + result);
   }
}

7) 实现客户端脚本bclient

1
2
3
4
base=/system
export CLASSPATH=$base/framework/bclient.jar
#调用app_process启动Bclient
exec app_process $base/bin com.cloud.test.BClient "$@"

4.测试

编译时若提示错误,缺少framwork之类的jar,可先用mka framework编译framework,编译好之后再使用mm进行模块编译。

编译好之后将相关文件上传至手机并修改权限:

adb push bserver /system/bin
adb push bclient /system/bin
adb push bserver.jar /system/framework
adb push bclient.jar /system/framework
adb shell chmod 755 /system/bin/bserver
adb shell chmod 755 /system/bin/bclient
```

测试:

framework_test

¥打赏5毛

取消

感谢您的支持,我会继续努力的!

扫码支持
赏个5毛,支持我把

打开支付宝扫一扫,即可进行扫码打赏哦

本篇目录