前一章说了下Binder机制的实现分析,这章就主要通过实际的例子来看看,具体的服务端创建。一般而言,同一个进程里的沟通称之为短程沟通,进程间的沟通称之为远程沟通,短程沟通的效率远高于远程沟通,但是Android提供的Binder机制就提供了高效率的远程沟通。
下面通过Activity与Service之间的沟通为例,列举出2种实现方式。
例子一:通过直接继承Binder实现
1. 实现Binder类
该类继承了Binder类,就必须实现onTransact方法,与客户端的transact方法相对应。其中参数code代表客户端调用码,决定在服务器端调用哪个方法,和transact端的保持一致,data是请求参数,由客户端设置,reply为返回参数,服务器端设置。Flags表示ipc通信方式。0表示双向,1表示单向。
2. 实现service类
Service相对简单,主要是初始化Binder对象,并且在onBind中返回。
3. Activity与service之间的沟通
首先需要创建ServiceConnection对象,并且在bind时返回Binder对象
其次,调用bindService绑定服务。
最后,在需要调用服务器端方法的地方,通过transact进行沟通,这里传递的参数必须是Parcel的。
由此可见,这种方法提供的沟通手段比较单一,只能通过transact进行沟通,如果服务器端的接口很多,这样调用起来不太方便,所以Android提供了一种更为简单的方式来实现-AIDL。
AIDL定义了Proxy/Stub封装了IBinder接口,提供了更加方便的新接口。
例子二:通过AIDL实现
1. 单一aidl文件接口
该接口文件以aidl结尾,中间封装了2个方法,该文件生成后,会在gen目录下自动生成对应的java文件,里面包含了具体的类别继承,反向呼叫等实现。
2. 实现Binder类
现在的实现继承接口改为了aidl类文件的Stub,此处需要实现接口中的2个接口方法
3. 实现service类
此处同例一保持一致
4. activity与service之间的沟通
首先,需要实现ServiceConnection对象,注意返回binder对象时与例一之间的差别。
其次,需要绑定服务,方法同例一
最后,具体调用服务器端接口
总结:
2种方法都可以实现跨进程通信,但是aidl实现相对简单,接口直接调用。
问题:
1.如果我们想保持和 Service 的通信,又不想让 Service 随着 Activity 退出而退出呢?你可以先 startService() 然后再 bindService() 。当你不需要绑定的时候就执行 unbindService() 方法,执行这个方法只会触发 Service 的 onUnbind() 而不会把这个 Service 销毁。这样就可以既保持和 Service 的通信,也不会随着 Activity 销毁而销毁了。
2.Activity has leaked ServiceConnection异常
这个异常是由于在启动应用时绑定了service,退出时没有接触绑定导致,可以在退出时调用unbindService()方法解决。
3.在先startService(),然后bindService()的情况下,在退出时
1).如果只调用unbindService(),在第一次会调用onUnbind,然后再次绑定时,通过start启动,再退出是不会调用onUnbind。
2).如果调用unbindService()和stopService,整个service会调用ondestory进行销毁。
3.在通过MediaPlayer播放音乐时,网上都说如果在activity中播放,在activity退出时,音乐播放会停止,这个纯粹是他妈的扯淡,我以前还对此坚信不疑。MediaPlayer播放音乐,会重新启动一个service进行播放,如果你的activity或service已经destory了,是不影响播放service的生命周期的。
例子源码见附件
本章介绍了Binder服务器的实现及举例说明了binder客户端与服务端的交互,下一章介绍下Service是如何绑定Binder的
下面通过Activity与Service之间的沟通为例,列举出2种实现方式。
例子一:通过直接继承Binder实现
1. 实现Binder类
public class MyBinder extends Binder {
private Context mContext;
public MyBinder(Context context){
mContext = context;
}
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
reply.writeString(data.readString()+" mp3");
switch(code){
case 1:
MyMediaPlayer.getIntance(mContext).play();
break;
case 2:
MyMediaPlayer.getIntance(mContext).stop();
break;
default:
break;
}
return true;
}
}
该类继承了Binder类,就必须实现onTransact方法,与客户端的transact方法相对应。其中参数code代表客户端调用码,决定在服务器端调用哪个方法,和transact端的保持一致,data是请求参数,由客户端设置,reply为返回参数,服务器端设置。Flags表示ipc通信方式。0表示双向,1表示单向。
2. 实现service类
public class MyService extends Service {
private Binder mBinder = null;
@Override
public IBinder onBind(Intent arg0) {
Log.e("XXX", "onBind");
return mBinder;
}
@Override
public void onCreate() {
super.onCreate();
mBinder = new MyBinder(this);
Log.e("XXX", "onCreate");
}
}
Service相对简单,主要是初始化Binder对象,并且在onBind中返回。
3. Activity与service之间的沟通
首先需要创建ServiceConnection对象,并且在bind时返回Binder对象
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e("XXX", "onServiceDisconnected");
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mIBinder = service;
Log.e("XXX", "onServiceConnected");
}
};
其次,调用bindService绑定服务。
Intent intent = new Intent("com.eric.ipc.binder");
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
最后,在需要调用服务器端方法的地方,通过transact进行沟通,这里传递的参数必须是Parcel的。
Parcel send_data = Parcel.obtain();
send_data.writeString("play");
Parcel reply_data = Parcel.obtain();
mIBinder.transact(1, send_data, reply_data, 0);
由此可见,这种方法提供的沟通手段比较单一,只能通过transact进行沟通,如果服务器端的接口很多,这样调用起来不太方便,所以Android提供了一种更为简单的方式来实现-AIDL。
AIDL定义了Proxy/Stub封装了IBinder接口,提供了更加方便的新接口。
例子二:通过AIDL实现
1. 单一aidl文件接口
package com.eric.ipc.aidl;
interface IMediaPlayer {
void play();
void stop();
}
该接口文件以aidl结尾,中间封装了2个方法,该文件生成后,会在gen目录下自动生成对应的java文件,里面包含了具体的类别继承,反向呼叫等实现。
2. 实现Binder类
public class MyBinder extends IMediaPlayer.Stub {
private Context mContext;
public MyBinder(Context context){
mContext = context;
}
@Override
public void play() throws RemoteException {
MyMediaPlayer.getIntance(mContext).play();
}
@Override
public void stop() throws RemoteException {
MyMediaPlayer.getIntance(mContext).stop();
}
}
现在的实现继承接口改为了aidl类文件的Stub,此处需要实现接口中的2个接口方法
3. 实现service类
此处同例一保持一致
4. activity与service之间的沟通
首先,需要实现ServiceConnection对象,注意返回binder对象时与例一之间的差别。
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e("XXX", "onServiceDisconnected");
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mIBinder = IMediaPlayer.Stub.asInterface(service);
Log.e("XXX", "onServiceConnected");
}
};
其次,需要绑定服务,方法同例一
最后,具体调用服务器端接口
mIBinder.play();
总结:
2种方法都可以实现跨进程通信,但是aidl实现相对简单,接口直接调用。
问题:
1.如果我们想保持和 Service 的通信,又不想让 Service 随着 Activity 退出而退出呢?你可以先 startService() 然后再 bindService() 。当你不需要绑定的时候就执行 unbindService() 方法,执行这个方法只会触发 Service 的 onUnbind() 而不会把这个 Service 销毁。这样就可以既保持和 Service 的通信,也不会随着 Activity 销毁而销毁了。
2.Activity has leaked ServiceConnection异常
这个异常是由于在启动应用时绑定了service,退出时没有接触绑定导致,可以在退出时调用unbindService()方法解决。
3.在先startService(),然后bindService()的情况下,在退出时
1).如果只调用unbindService(),在第一次会调用onUnbind,然后再次绑定时,通过start启动,再退出是不会调用onUnbind。
2).如果调用unbindService()和stopService,整个service会调用ondestory进行销毁。
3.在通过MediaPlayer播放音乐时,网上都说如果在activity中播放,在activity退出时,音乐播放会停止,这个纯粹是他妈的扯淡,我以前还对此坚信不疑。MediaPlayer播放音乐,会重新启动一个service进行播放,如果你的activity或service已经destory了,是不影响播放service的生命周期的。
例子源码见附件
本章介绍了Binder服务器的实现及举例说明了binder客户端与服务端的交互,下一章介绍下Service是如何绑定Binder的