Android Binder Analysis(2)

Android Binder Analysis(2)

@(数据库系统)[Binder, AIDL, Messenger]
前文中,我们简单介绍了Binder相关的OS 基础知识,从LinuxAndroid以及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,HandlerMessenger的基础,Messenger可与客户端共享一个IBinder,从而使得客户端能利用Message对象向服务发送命令,此外客户端还可以定义自己的Messenger,以方便服务器回传数据。这是执行IPC的最简单方式,Messenger会在单一线程中创建所有请求队列。
  • AIDLAIDL执行将对象分解成数据流的工作,操作系统可以识别这些数据流并将它们传递到各进程中,执行IPC过程,AIDL用于服务器需要多线程访问的情况,其不同于Messenger,服务一次只接收一个请求,在AIDL中,服务可以同时执行多个请求,在这种情况下,服务必须具备多线程处理能力,并采用线程安全式设计(如果你不确定你的服务器必须支持并发操作,请不要使用AIDL,尽量用MessengerExtends 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接口中的关键APItransact()函数,它的默认实现在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,

  1. 编写服务
    新建一个Service
  2. 继承Binder类,并在服务onBind()方法中返回该类对象,如下图所示(Service中编写代码):

    这里写图片描述

  3. 编写客户端,连接服务
    在启动服务的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中,从而得到服务器MessengerBinder对象,使用该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.编写服务,并创建HandlerMessenger变量,Messenger变量由Handler构造而来,在ServiceonBind方法中返回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.MainActivitybindService方式启动服务并在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,敬请期待。

最新文章更新在微信公众号上,欢迎关注获取详情:
这里写图片描述


感谢阅读这份博文。转载请注明出处:小海窝

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值