如果你的service仅被自己的应用使用并且不需跨进程工作,那么你可以实现你自己的Binder类使得你的客户端能直接使用service的公开接口方法.

注:这只在客户端和service位于同一应用和同一进程中时才能工作,其实大多数都是这种情况.例如,在一个音乐应用需要把它的activity绑定到它自己的播放音乐的后台service时,这种方式就会很好地工作.

下面是如何建立它:

  1. 在你的service中,创建一个Binder实例,提供以下三种功能之一:
  • Binder包含一些可供客户端调用的公开方法.
  • 返回当前的Service实例,它具有一些客户端可以调用的公开方法.
  • 或者,返回另一个类的实例,这个类具有客户端可调用的公开方法并托管于service.
  1. 在回调方法onBind()中返回这个Binder的实例.
  2. 在客户端,从回调方法onServiceConnected()中接收这个Binder并使用1中所述的公开方法调用绑定service.

注:service和客户端必须位于同一应用的理由是这样可以使客户端正确地转换返回的对象并调用它的公开方法.service和客户端必需要位于同一个进程中,因为这样就不必执行跨进程的封送处理了.

例如,这下面这个service提供让客户端通过一个Binder实现调用service中的方法的功能:

public class LocalService extends Service {
    // Binder given to clients
    private final IBinder mBinder = new LocalBinder();
    // Random number generator
    private final Random mGenerator = new Random();

    /**
     * 用于客户端Binder的类.因为我们我们知道这个
     * service永远运行在与客户端相同的进程中,所以我们不需要处理IPC.
     */
    public class LocalBinder extends Binder {
        LocalService getService() {
            // 返回本service的实例到客户端,于是客户端可以调用本service的公开方法
            return LocalService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    /**客户端所要调用的方法*/
    public int getRandomNumber() {
      return mGenerator.nextInt(100);
    }
}

类LocalBinder提供提供了getService()方法使得客户端能取得当前LocalService的实例.于是客户端就可以调用service中的公开方法了.比如,客户端可以调用service的getRandomNumber()方法.

下面是一个绑定到LocalService并且在按钮按下时调用getRandomNumber()的actvity的例子:

public class BindingActivity extends Activity {
    LocalService mService;
    boolean mBound = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        // 绑定到类LocalService的实例
        Intent intent = new Intent(this, LocalService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // 从service解除绑定
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }

    /** 当按钮按下时调用(在layout文件中定义的button并用android:onClick 属性指定响应到本方法) */
    public void onButtonClick(View v) {
        if (mBound) {
            // 调用LocalService的一个方法
            // 然而,如果这个调用中有挂起操作,那么这个请求应发
            // 生在另一个线程来避免拉低activity的性能.
            int num = mService.getRandomNumber();
            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
        }
    }

    /** 定义service绑定的回调,传给bindService() 的*/
    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            //我们已经绑定到了LocalService,把IBinder进行强制类型转换并且获取LocalService实例.
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
        }
    };
}

上面的例子展示了客户端如何使用一个ServiceConnection的实例和onServiceConnected()方法绑定到service.

注:上面的例子没有明确地从service解除绑定.但是所有的客户端都应该在合适的时候解除绑定(比如当activity暂停时).

基础知识

service是一个允许应用绑定然后进行交互的类Service的实现.要为service提供绑定,你必须实现onBind()回调方法.此方法返回一个IBinder对象,此对象定义了客户端可以用来与service交互的程序接口.

bindService()绑定到service.当它这样做时,它必须提供了一个ServiceConnection的实现.这个实现用于监视客户端与service的连接.bindService()方法会立即返回并且不会返回任何值,但当Android系统创建客户端与service之间的连接时,它调用ServiceConnection的onServiceConnected()来传送客户端用来与servcie通信的IBinder.

service.然而,系统只在第一个客户端绑定时才调用你的service的onBind()方法来接收IBinder.之后系统把同一个IBinder传给其它客户端,所以不会再调用onBind().