Android Binder Analysis(2)
@(数据库系统)[Binder, AIDL, Messenger]
前文中,我们简单介绍了Binder
相关的OS
基础知识,从Linux
,Android
以及Component communication
等角度详细阐述了跨进程通信在OS
内部的必要性,随后简单粗暴地提出了Binder
的概念,在本节中我们将围绕Binding Service
,从Binder
的应用场景入手,简单介绍其使用方法。
我们知道服务的启动方式有两种,一种是bindService()
,另一种是startService()
,在大多数情况下,我们请求后台服务执行某一项工作,往往需要得到其反馈。例如在播放音乐的后台服务中(Service
内含MediaPlayer
对象的实现方式),我们需要暂停或者继续播放音乐都需要向Service
发出指令,也就意味着该播放音乐的服务与其调用者之间存在着某种通信绑定机制,使得二者之间可以相互交换信息,甚至于调用内部公开方法。对Service
有所了解的朋友应该知道,以startService()
方式启动的服务,就是一篇脱缰的野马,启动后则与调用者不再产生联系,直到应用某处调用stopService()
方法终止服务,自然实现不了服务和调用者产生依赖,剩下的就只能是bindService()
这种启动方式了,bindService()
的参数原型如下:
bindService(android.content.Intent,android.content.ServiceConnection,int)
从上面可以看出,使用bindService()
方式启动服务时,必须提供一个ServiceConnection
对象,该ServiceConnection
会监听与服务的连接,在连接成功后会调用ServiceConnection#onServiceConnected()
方法,向客户端传递与服务通信的IBinder
对象。在绑定服务中,多个调用者可同时连接一个服务,不过只有在第一个调用者建立绑定关系时,系统才会调用服务的onBind()
方法,随后无需再次调用onBind()
方法,直接返回第一次的IBinder
对象即可,当最后一个客户端与服务取消绑定时,系统才会销毁服务(除非startService()
也启动了该服务)。至于什么是IBinder
对象,我们在使用过程中再做简介。
实现绑定服务一般有如下三种方式:
- Extends Binder Class:如果服务是应用内部服务,并且在与客户端相同的进程中运行,可以通过继承
Binder
类来实现绑定服务,在onBind()
方法中返回一个该实现类的实例,客户端接收到Binder
后,可利用它直接访问Binder
乃至Service
中的公有方法; - Messenger:如果应用服务需要跨进程工作,那么可以考虑用
Messenger
为服务创建接口,服务可以按照Message
定义不同的Handler
,Handler
是Messenger
的基础,Messenger
可与客户端共享一个IBinder
,从而使得客户端能利用Message
对象向服务发送命令,此外客户端还可以定义自己的Messenger
,以方便服务器回传数据。这是执行IPC
的最简单方式,Messenger
会在单一线程中创建所有请求队列。 - AIDL:
AIDL
执行将对象分解成数据流的工作,操作系统可以识别这些数据流并将它们传递到各进程中,执行IPC
过程,AIDL
用于服务器需要多线程访问的情况,其不同于Messenger
,服务一次只接收一个请求,在AIDL
中,服务可以同时执行多个请求,在这种情况下,服务必须具备多线程处理能力,并采用线程安全式设计(如果你不确定你的服务器必须支持并发操作,请不要使用AIDL
,尽量用Messenger
或Extends Binder Class
代替)。
大家一定要注意这三种方式的适用场合哦。
好了,说了这么多,我知道你们想要我JUST SHOW U THE FUCKING CODE
,接下来一起进入撸码环节吧。
Extends Binder Class
以继承Binder
类实现绑定服务的过程是三种方式中最简单的一种,适用于同进程内的Service
&Activty
交互。接下来就让我们一起来敲个Demo
吧。
ServiceConnection
在Android官网上是这样介绍ServiceConnection
的:
Interface for monitoring the state of an application service.
由上述英文可以看出,ServiceConnection
是用于监听应用服务的接口,其内部包含三个回调函数:
onBindingDied(ComponentName name)
onServiceConnected(ComponentName name, IBinder service)
onServiceDisconnected(ComponentName name)
其中我们一般只需要重写最后两个函数即可,这里可以看到当Service绑定操作完成时,会返回一个IBinder对象,这里的IBinder是一个接口,其中定义了远程对象所拥有的基本方法,Android官网是这样描述IBinder接口的:
Base interface for a remotable object, the core part of a lightweight remote procedure call mechanism designed for high performance when performing in-process and cross-process calls. This interface describes the abstract protocol for interacting with a remotable object. Do not implement this interface directly, instead extend from Binder.
上面这段英文的意思是,IBinder
是一个远程对象所要实现的基础接口,是高性能轻量级远程过程调用机制的主要组成部分,通常用于实现进程内部或者跨进程通信。在这个接口中描述了与一个远程对象交互的抽象协议。我们采用继承Binder
类的方式来进行进程通信,而不是直接实现该接口,Binder
正是这个接口的实现类。
对于Binder
接口,我们需要知道如下几点:
Binder
接口中的关键API
是transact()
函数,它的默认实现在Binder
类中,这种事务型API
整体上来说是一种同步操作,一次transact()
调用的结束完全依赖于Binder.onTransact()
是否结束,这种行为是调用进程内部对象的期望行为并且基础IPC机制也保证了在跨进程通信时其表现为同样的行为- 在
transact()
函数中传递的数据必须是Parcel
类型的,实体类必须实现Parcelable
接口 - 系统在每一个进程中都维护了一个
transact
的线程池,这些线程用来执行来自于其他进程的IPC
请求,例如当进程A向进程B发出一个IPC
请求时,A的请求线程会阻塞在transact
部分并发送IPC
请求到进程B,进程B的下一个可用线程接收进程A的请求,调用onTransact
函数并发送执行结果到进程A,进程A中的线程接收到返回的结果后被唤醒继续执行。实质上,其他进程就像一个你所拥有的额外线程一样,但是这种线程并不需要在自身进程创建执行 Binder
系统也支持递归跨进程调用
如上描述请大家仔细理解,实质上已经简单描述了一次跨进程通信的基本过程,在后面的源码解析部分中,我们会对这四点有更加深刻清晰的理解。
Binder
对于Binder,这里就不做更多详细介绍了,其官网描述与IBinder接口描述相似。
Extends Binder Class Code Demo
现在让我们来编写一个继承Binder类实现绑定服务的demo,
- 编写服务
新建一个Service
类 继承
Binder
类,并在服务onBind()
方法中返回该类对象,如下图所示(Service
中编写代码):编写客户端,连接服务
在启动服务的Activity
中创建ServiceConnection
类对象,并使用bindService
方式启动该服务,在onServiceConnected
回调中,获取LocalBinder
对象,从而调用LocalBinder
,乃至于Service
中的共有方法,详细代码如下:
private LocalBinder mLocalBinder;
private PlayMusicService mPlayMusicService;
private TextView mTextViewPlay,mTextViewStop,mTextViewIsPlay;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextViewPlay = (TextView)findViewById(R.id.start_music);
mTextViewStop = (TextView)findViewById(R.id.stop_music);
mTextViewIsPlay = (TextView)findViewById(R.id.is_music_play);
}
@Override
protected void onStart() {
super.onStart();
if (!mBound){
Intent intent = new Intent(this,PlayMusicService.class);
bindService(intent,mServiceConnection, Context.BIND_AUTO_CREATE);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mBound){
unbindService(mServiceConnection);
mBound = false;
}
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.start_music:
processStartMusic();
break;
case R.id.stop_music:
processStopMusic();
break;
}
}
public void processStartMusic(){
Log.e(TAG,"call Server method playMusic(),music start play!");
if (mLocalBinder.isMusicPlay()){
Log.e(TAG,"call Server method playMusic(),music has already play!");
}else{
mPlayMusicService.playMusic();
}
}
public void processStopMusic(){
Log.e(TAG,"call Server method playMusic(),music start play!");
mPlayMusicService.stopMusic();
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mBound = true;
Log.e(TAG,"call Server method Connect server");
mTextViewPlay.setOnClickListener(MainActivity.this);
mTextViewStop.setOnClickListener(MainActivity.this);
mLocalBinder = (LocalBinder) iBinder;
mPlayMusicService = mLocalBinder.getPlayMusicService();
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mBound = false;
}
};
运行结果如下:
从上述描述及编码过程中,我们可以看出继承Binder
这种实现方式关键在于如何正确的定义Binder
类所持有的方法或变量以满足不同的场景需求,这个Demo
的大概框架如下图所示:
Messenger
Messenger
这种实现方式与我们普通的Handler
通信机制略有相似,具体调用过程如下图所示:
在Service
类中使用Handler
构造了一个Messenger
对象,Messenger
自身持有Binder
,在onBind
函数中返回Messenger
对象所持有的Binder
,当客户端使用bindService
方式启动服务后,服务启动成功回调至onServiceConnected
中,从而得到服务器Messenger
的Binder
对象,使用该Binder
对象构造客户端的Messenger
,利用客户端Messenger
向服务sendMessage
即可完成客户端与服务的交互。
Messenger
方式是对AIDL
方式的一种简化封装,其唯一的局限性在于服务中只支持单线程操作。
Messenger Class
Reference to a Handler, which others can use to send messages to it. This allows for the implementation of message-based communication across processes, by creating a Messenger pointing to a Handler in one process, and handing that Messenger to another process.
Note: the implementation underneath is just a simple wrapper around a Binder that is used to perform the communication. This means semantically you should treat it as such: this class does not impact process lifecycle management (you must be using some higher-level component to tell the system that your process needs to continue running), the connection will break if your process goes away for any reason, etc.
上面是Android
官网对Messenger
这个类的描述,该类持有一个Handler
类的引用,其他类可以使用该类向Messenger
发消息,通过将该Messenger
指向一个同进程的Handler
并暴露给其他进程实现了基于消息的跨进程通信方式。
注意这种实现方式只是对Binder
的一种封装,也就是说这种交互方式并不会影响进程的生命周期,在开发过程中,你仍然应该关注进程是否中止。
Messenger IPC Demo
在上面我们已经给出了Messenger
方法实现IPC的函数调用过程,接下来我们就编写一个Demo
来实践一下:
1.编写服务,并创建Handler
,Messenger
变量,Messenger
变量由Handler
构造而来,在Service
的onBind
方法中返回Messenger
内部持有的Binder
class IncomingHandler extends Handler{
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case MSG_SAY_HELLO:
Toast.makeText(getApplicationContext(), "Hello Messenger!", Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}
final Messenger mMessenger = new Messenger(new IncomingHandler());
public MessengerService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
Toast.makeText(getApplicationContext(), "Service Binding", Toast.LENGTH_SHORT).show();
return mMessenger.getBinder();
}
2.MainActivity
以bindService
方式启动服务并在onServiceConnected
回调中获取服务的Messenger#Binder
,构造客户端Messenger
,代码如下:
Messenger mService = null;
boolean mBound;
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mService = new Messenger(iBinder);
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mService = null;
mBound = false;
}
};
3.客户端Messenger
发送消息到服务进行通信
findViewById(R.id.say_hello).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
processSayHello();
}
});
该部分代码较简单,请大家自行运行demo
查看效果。
从上述描述和编码过程中我们可以看到Messenger
方式的大概处理框架如下:
下一篇我们将讲解AIDL
部分Demo
,包括同步AIDL
和异步回调AIDL
,敬请期待。
最新文章更新在微信公众号上,欢迎关注获取详情:
感谢阅读这份博文。转载请注明出处:小海窝