笔记 - 安卓中的Binder之使用①(二)

引言

上一篇中我们aidl和其对应的java代码都生成了,我们也分析了,那具体的使用还没有讲,接下来就来见一下针对我们的IBookManager,该怎么写对应的客户端和服务端。

梗概

《Android开发艺术》是直接在一个app中开启两个进程,并且各个阶段不可控,我这里进行了稍稍的改造,创建了两个app,并且各个阶段都用Button来控制,可以更好的理解。

  • 服务端
    服务端我们新建了一个项目,叫做AIDLService,并创建一个Service来监听客户端的请求,然后要创建上一篇讲的aidl文件,把暴露给客户端使用的接口声明出来,并且在Service中实现这个接口。
  • 客户端
    客户端我们也新建一个项目,叫做AIDLClient,我们需要绑定到服务端的Service,然后将返回的Binder对象转换为AIDL接口所属的类型,这样就可以调用接口的方法了。

注意:aidl的包结构在服务端和客户端都要保持一致,否则运行会出错

服务端实现

1. Activity

服务端的Activity作用不大,我只是在布局文件中用了一个TextView来标明这个App的身份是服务端,因为是创建出来的默认实现,我这里就不摆出代码了。

2. AIDL

aidl的实现基本跟上一篇的一模一样,只是项目结构改变了,并且为了方便打印log,我在在Book这个类中添加了get和set,并且重写了toString()这个方法。项目结构如下
服务端AIDL文件结构
这里再提醒一下
注意aidl的包名结构!
注意aidl的包名结构!!
注意aidl的包名结构!!!
重要的事情说三遍,包名不一定跟我一样,但是客户端和服务都的aidl包名一定要一致。

3. Service

服务端最重要的一个部分就是这个Service了,它来负责监听客户端发来的请求,并给出相应处理。这里理清一下我们的业务,我们要维护一个图书的列表,并提供两个功能,一个是添加图书,一个是提供所有图书的列表信息。因为作为服务端,我们需要一个实现了aidl接口文件中声明方法的Bidner。

  • 图书列表
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();

因为我们的Binder会运行在Binder线程池中,这里使用了CopyOnWriteArrayList这个数据结构,它支持并发读写。

 @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "AIDL Service is created");
        //图书列表中默认添加有两本书
        mBookList.add(new Book(1, "Android"));
        mBookList.add(new Book(2, "Ios"));
    }

另外我们在onCreate()函数中打印一句log并添加两个默认的图书到列表中

  • Binder
private Binder mBinder = new IBookManager.Stub() {
        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;//返回图书列表
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);//添加图书
        }
    };

我们创建一个IBookManager.Stub()的Binder,并实现里面两个方法:getBookList()、addBook()

 @Override
    public IBinder onBind(Intent intent) {
        return mBinder;//返回Binder对象
    }

在onBind()方法中,我们返回我们的binder实例。

整个Service的代码如下,注意要在清单文件中注册Service,并设置exported和enabled都为“true"

//服务端的Service实现
public class BookManagerService extends Service {

    private static final String TAG = "BookManagerService";

    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();//这个数据结构支持并发读/写

    //在服务端中创建Binder对象并实现接口中的方法
    private Binder mBinder = new IBookManager.Stub() {
        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;//返回图书列表
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);//添加图书
        }
    };

    public BookManagerService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "AIDL Service is created");
        //图书列表中默认添加有两本书
        mBookList.add(new Book(1, "Android"));
        mBookList.add(new Book(2, "Ios"));
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;//返回Binder对象
    }
}

客户端

客户端我们主要的逻辑都在Activity中,AIDL文件也跟服务端的一模一样。

1. AIDL

AIDL文件跟服务端的AIDL文件是一模一样的,结构如下
客户端AIDL文件结构
这里再再提醒一下
注意aidl的包名结构!
注意aidl的包名结构!!
注意aidl的包名结构!!!
很重要的事情说六遍,包名不一定跟我一样,但是客户端和服务都的aidl包名一定要一致。

2. Activity

我们在Activity中主要有三个部分:绑定服务端Service、获取图书列表和添加图书。分别对应三个按钮

  • 绑定服务器Service

先创建一个ServiceConnection,作用是获取服务端的binder

 IBookManager bookManager;

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            bookManager = IBookManager.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };

设置按钮的点击事件进行绑定,不要忘了在onDestroy中解除绑定

btn_bindService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setComponent(new ComponentName("com.example.aidlservice", "com.example.aidlservice.BookManagerService"));
                bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
            }
        });

这里我们使用了Intent,并设置了ComonentName,注意两个参数,一个是我们服务端app的包名,一个是我们服务端Service的包名.类名。就像你要打电话到小明家找小明,你要先知道小明家的电话,拨通之后再声明你要找的是小明,不是小明的妈妈,也不是小明的爸爸。

  • 获取图书列表
 btn_getBookList.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    List<Book> list = bookManager.getBookList();
                    Log.i(TAG, "query book list, list type: " + list.getClass().getCanonicalName());
                    Log.i(TAG, "query book list, list content: " + list.toString());
                }catch (RemoteException e){
                    e.printStackTrace();
                }
            }
        });

我们这里通过获取到的binder也就是bookManger的getBookList来获取图书列表,并答应出日志

  • 添加图书
btn_addBook.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    Book book = new Book(3, "Android开发艺术探索");
                    bookManager.addBook(book);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });

我们这里通过获取到的binder也就是bookManger的addBook来添加图书

  • 整个Activity的代码如下
public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    private Button btn_getBookList;
    private Button btn_addBook;
    private Button btn_bindService;

    IBookManager bookManager;

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            bookManager = IBookManager.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btn_getBookList = findViewById(R.id.btn_getBookList);
        btn_addBook = findViewById(R.id.btn_addBook);
        btn_bindService = findViewById(R.id.btn_bindService);

        btn_bindService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setComponent(new ComponentName("com.example.aidlservice", "com.example.aidlservice.BookManagerService"));
                bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
            }
        });

        btn_getBookList.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    List<Book> list = bookManager.getBookList();
                    Log.i(TAG, "query book list, list type: " + list.getClass().getCanonicalName());
                    Log.i(TAG, "query book list, list content: " + list.toString());
                }catch (RemoteException e){
                    e.printStackTrace();
                }
            }
        });

        btn_addBook.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    Book book = new Book(3, "Android开发艺术探索");
                    bookManager.addBook(book);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(mConnection);
    }
}

Binder演示

先安装AIDLService,再安装AIDLClient,都启动一下,然后我们再AIDLClient中进行操作并查看答应出来的log

  • 我们先绑定Service,点击按钮,查看日志,我们可以看到之前预埋的log已经出来了,说明绑定成功
    绑定Service的log
  • 获取图书列表,点击按钮,查看日志,我们看到了初始化的图书列表信息
    获取图书列表的log
  • 添加图书,点击添加,再点击获取图书列表信息,我们看到现在多了一本图书
    添加图书log

到这里,我们的实例就算结束啦,全部代码我会传到github上,需要自取

下一篇我们讲一下Binder怎么注册回调

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值