Android--Service之绑定服务交互

原文链接 http://www.cnblogs.com/plokmju/p/android_ServiceCommunication.html

前言

  开篇名义,这篇博客介绍一下Android下使用绑定服务进行时数据交互的几种方法。关于Android下Service的内容,前面两篇博客已经介绍了,不清楚的可以移步过去先看看:Android--Service之基础Android--Service之提高

  在前面的博客中已经介绍到了,对于Service组件而言,它只有在绑定模式下才可以与客户端进行时交互,这里讲解几个方法进行绑定服务与客户端间的交互方法:

  1. 使用IBinder接口
  2. 使用Messenger类
  3. 使用AIDL

  虽然根据官方文档给出了三个方法,其中AIDL涉及的内容超出本博客内容范围,以后有机会在另外介绍,本篇博客只介绍1、2两种方式的数据交互。

 

使用IBinder接口

  如果看了之前关于Service博客的人,应该对IBinder接口有所了解,这里简单介绍一下IBinder。

  IBinder是远程对象的基本接口,是为高性能而设计的轻量级远程调用机制的核心部分。但它不仅用于远程调用,也可以用于进程内调用。这个接口定义了与远程对象交互的协议,一般不直接实现这个接口,而是从它的实现类Binder中继承。

  通过IBinder进行服务的交互一般有两种方式,一种方式是使用IBinder.transact()方法向远端的IBinder对象发送一个发出调用,会回调远端的Binder.onTransact()方法,这个方法传递的数据是Parcel。Parcel是一种缓冲区,除了数据外还有有一些描述它内容的元素,如果查看源码的话会发现,Parcel本质上是一个Serialize,只是它在内存中完成了序列化和反序列化,利用的是连续的内存空间,因此效率会更高,并且AIDL的数据也是通过Parcel来交互的。另外一种方法就是抛弃IBinder中原生的方法,使用自定义的接口方法进行数据交互,这也是Android官方推荐绑定服务的一种数据交互方式。当然,不管是使用transact()给远程服务交互,还是使用自定义的接口交互,都是同步执行的,直到远程服务执行完并返回结果才会继续向下执行。

  其他关于适应IBinder服务的内容,在博客Android--Service之基础中已经讲解过了,这里不再累述。下面使用一个例子来演示一下使用自定义接口与服务进行交互的例子。

  服务:IBinderSer.java

package cn.bgxt.servicebinddatedemo;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;

public class IBinderSer extends Service {
    private final String TAG="main";
    private final int MULTIPLE=1024;    
    public  final IBinder mBinder=new LocalBinder();
    
    public class LocalBinder extends Binder{
        // 在Binder中定义一个自定义的接口用于数据交互
        // 这里直接把当前的服务传回给宿主
        public IBinderSer getService(){
            return IBinderSer.this;
        }                
    }
    
    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "The service is binding!");
        // 绑定服务,把当前服务的IBinder对象的引用传递给宿主
        return mBinder;
    }
    
    public int getMultipleNum(int num){
        // 定义一个方法 用于数据交互
        return MULTIPLE*num;
    }
}

 调用服务的Activity:IBinderActivity.java

package cn.bgxt.servicebinddatedemo;

import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Messenger;
import android.os.Parcel;
import android.os.RemoteException;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class IBinderActivity extends Activity {
    private Button btnStart, btnInvoke, btnStop;
    IBinderSer mService=null;
    private ServiceConnection mConnection = new ServiceConnection() {

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

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 获取服务上的IBinder对象,调用IBinder对象中定义的自定义方法,获取Service对象
            IBinderSer.LocalBinder binder=(IBinderSer.LocalBinder)service;
            mService=binder.getService();
        }
    };
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_service);
        btnStart = (Button) findViewById(R.id.btnStartSer);
        btnInvoke = (Button) findViewById(R.id.btnInvokeMethod);
        btnStop = (Button) findViewById(R.id.btnStopSer);
        
        btnStart.setOnClickListener(onclick);
        btnInvoke.setOnClickListener(onclick);
        btnStop.setOnClickListener(onclick);
    }

    View.OnClickListener onclick = new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            switch (v.getId()) {
            case R.id.btnStartSer:
                Toast.makeText(getApplicationContext(), "绑定服务成功", Toast.LENGTH_SHORT).show();
                bindService(new Intent(IBinderActivity.this,IBinderSer.class),mConnection,Service.BIND_AUTO_CREATE);                
                break;
            case R.id.btnInvokeMethod:
                if(mService==null){
                    Toast.makeText(getApplicationContext(), "请先绑定服务", Toast.LENGTH_SHORT).show();
                    return;
                }
                // 调用绑定服务上的方法,进行数据交互
                int iResult=mService.getMultipleNum(10);
                Toast.makeText(getApplicationContext(), "服务计算结果为:"+iResult, Toast.LENGTH_SHORT).show();
                break;
            case R.id.btnStopSer:
                Toast.makeText(getApplicationContext(), "服务解除绑定", Toast.LENGTH_SHORT).show();
                unbindService(mConnection);
                mService=null;
                break;
            default:
                break;
            }
        }
    };
}

 执行结果:

 

 

使用Messenger类

  除了使用IBinder之外,还可以使用Messenger,那么先来聊聊什么是Messenger。

  Messenger引用了一个Handler独享,可以使用Messenger.send(Message msg)方法跨进程向服务发送消息,只需要在服务中使用Handler创建一个Messenger,宿主持有这个Messenger就可以与服务进行通信。之前介绍的handler+Message的通信方式不同,那都是在同一个进程中的,从工作线程持有一个主线程的Handler对象,从而向主线程发送消息,这里不了解的可以看看之前的博客:Android--多线程之Handler。而上面介绍过了,Android可以使用IBinder实现跨进程通信,并且也将Handler与IBinder结合起来实现跨进程发送消息。

  当然这里提一下,Messenger管理的是一个消息队列,它会依据消息进入的先后次序予以执行,所以也不需要把服务设计为线程安全是。

   实现Messenger实现进程通信,主要有以下几点注意:

  1. 在服务中实现一个Handler类,并实例化它,在handleMessage()方法中接收客户端的请求。
  2. 在服务中使用这个Handler对象创建一个Messenger对象。
  3. 使用Messenger对象的getBinder()方法返回一个IBinder对象作为onBind()的返回值返回给客户端。
  4. 在客户端使用IBinder实例化一个Messenger对象,并使用它向服务端发送信息。

  下面通过一个简单的例子来演示一下利用Messenger在服务与客户端进行的通信。

  服务:MessengerSer.java

package cn.bgxt.servicebinddatedemo;

import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.util.Log;
import android.widget.Toast;

public class MessengerSer extends Service {
    private final String TAG="main";
    static final int MSG_SAY_HELLO = 1;

    public class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MSG_SAY_HELLO:
                Toast.makeText(getApplicationContext(), "Service say hello!",
                        Toast.LENGTH_SHORT).show();
                Log.i(TAG, "Service say hello!");
                break;
            default:
                super.handleMessage(msg);
            }
        }
    }

    IncomingHandler incomingHandler=new IncomingHandler();
     final Messenger mMessenger=new Messenger(new IncomingHandler());
     
    @Override
    public IBinder onBind(Intent arg0) {
        return mMessenger.getBinder();
    }

}


服务绑定的Activity:MessengerActivity.java

package cn.bgxt.servicebinddatedemo;


import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MessengerActivity extends Activity {
    private Button btnStart, btnInvoke, btnStop;
    private Messenger mService = null;

    private ServiceConnection mConnection = new ServiceConnection() {

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

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 使用服务端的IBinder对象实例化一个Messenger对象
            mService = new Messenger(service);
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_service);
        btnStart = (Button) findViewById(R.id.btnStartSer);
        btnInvoke = (Button) findViewById(R.id.btnInvokeMethod);
        btnStop = (Button) findViewById(R.id.btnStopSer);
        
        btnStart.setOnClickListener(onclick);
        btnInvoke.setOnClickListener(onclick);
        btnStop.setOnClickListener(onclick);
    }

    View.OnClickListener onclick = new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            switch (v.getId()) {
            case R.id.btnStartSer:
                Toast.makeText(getApplicationContext(), "绑定服务成功", Toast.LENGTH_SHORT).show();
                bindService(new Intent(getApplicationContext(),MessengerSer.class), mConnection, Service.BIND_AUTO_CREATE);
                break;
            case R.id.btnInvokeMethod:
                if(mService==null){
                    Toast.makeText(getApplicationContext(), "请先绑定服务",Toast.LENGTH_SHORT).show();
                    return ;
                }
                // 实例化一个Message对象
                Message msg=Message.obtain(null, MessengerSer.MSG_SAY_HELLO, 0, 0);
                try{
                    // 把Message独享传递给服务端处理
                    mService.send(msg);
                }
                catch(RemoteException e){
                    e.printStackTrace();
                }
                break;
            case R.id.btnStopSer:
                Toast.makeText(getApplicationContext(), "服务解除绑定", Toast.LENGTH_SHORT).show();
                unbindService(mConnection);
                mService=null;
                break;
            default:
                break;
            }

        }
    };
}

执行结果:

 

 

使用AIDL

  AIDL(Android Interface Definition Language),它可以实现跨进程间的通信。之前讲到的Messenger实现跨进程通信,其实也是基于AIDL作为底层结构。但是正如上面提到的,Messenger创建的一个消息队列是在一个单独的线程中,所以服务一次仅处理一个请求,然而,如果想要服务同时处理多个请求,就需要使用到AIDL,但是这种情况下就要考虑多线程和线程安全的问题了。这个不在本篇博客的范畴内,以后有机会在细细讲解。

 

作者: 承香墨影
更多内容,请阅读本人新书: 《Android深入浅出》
欢迎转载,但还请尊重劳动果实,保留此段声明并注明原文链接。



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值