引言
上一篇中我们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包名一定要一致。
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包名一定要一致。
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已经出来了,说明绑定成功
- 获取图书列表,点击按钮,查看日志,我们看到了初始化的图书列表信息
- 添加图书,点击添加,再点击获取图书列表信息,我们看到现在多了一本图书
到这里,我们的实例就算结束啦,全部代码我会传到github上,需要自取
下一篇我们讲一下Binder怎么注册回调