2.2 Bound Services - TomeOkin/Learning-Notes GitHub Wiki

Bound Services

当 Service 需要与其他组件(Activity、Service 或者 Content Provider,不包括 Broadcast Receiver)进行交互,并且不需要长时间在后台运行的时候,一般会使用 Bound Services,也即使用 bindService() 与 Service 建立关联,这种方式成为 client-server 模式。Service 可以与其他组件运行在相同的进程或者允许在不同的进程(使用 android:process 配置)。在相同进程运行时,也即在本地进程运行,以故,也称该服务为本地服务。

Bound Services 的基本使用

有三种方式可以实现一个 Bound Services:

(1)提供一个 Binder 实例作为客户端与服务器交互的中介:只支持进程内交互。

首先,Service 需要重写 onBind() 方法,返回一个实现 IBinder 接口的对象。只有第一个客户端 bind 到 Service 时才会调用 onBind() 方法,后续的访问都会返回相同的 Binder。

public class LocalService extends Service {
    private final IBinder mBinder = new LocalBinder();
    private final Random mGenerator = new Random();

    public class LocalBinder extends Binder {
        LocalService getService() {
            return LocalService.this;
        }
    }

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

    public int getRandomNumber() {
      return mGenerator.nextInt(100);
    }
}

客户端通过 bindService() 与 Service 建立关联。bindService() 除了需要一个 Intent 外,还需要一个 ServiceConnection 实例,并且要设置 bind flags。其中,ServiceConnection 提供了两个回调方法:onServiceConnected()onServiceDisconnected()。客户端通过 onServiceConnected() 获得 Service 返回的 Binder 实例,当 Service 异常结束时,客户端会通过 onServiceDisconnected() 接收到通知。bind flags 一般设置为 Context.BIND_AUTO_CREATE,指示如果 Service 没有运行,则创建一个新的,另外也可以是 BIND_DEBUG_UNBINDBIND_NOT_FOREGROUND 或者 0(none)。 bindService() 同时也会有一个返回值,本意是标识服务是否存在,但实际上,即使传入的 flag 为 0,并且服务没有在运行,也会返回 true(see Issue 41085)。

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();

        Intent intent = new Intent(this, LocalService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();

        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }

    public void onButtonClick(View v) {
        if (mBound) {
            int num = mService.getRandomNumber();
            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
        }
    }

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            mBound = true;
        }

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

需要说明的是,一般选择在 onCreate 里建立与 Service 的关联,在 onDestroy() 里解除关联,避免频繁地创建和销毁服务。

(2)使用 Messager:Messager 是一种最简单的 IPC(inter-process communication,进程间通信) 实现,所有接收到的请求都会放入队列中依次传送给 Service 执行,只在单个线程中运行。使用 Messager 时,Service 需要创建一个 Handler 用于响应客户端的请求。Messager 则对 Handler 进行封装,客户端可以通过 Messager 发送 Message 给 Service 实现交互。如果客户端也提供了 Messager, 这样 Service 就可以发送消息给客户端(双向交互参见 MessengerServiceMessengerServiceActivities,取自 SDK Sample)。

public class MessengerService extends Service {
    static final int MSG_SAY_HELLO = 1;

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

    final Messenger mMessenger = new Messenger(new IncomingHandler());

    @Override
    public IBinder onBind(Intent intent) {
        Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
        return mMessenger.getBinder();
    }
}
public class ActivityMessenger extends Activity {
    Messenger mService = null;
    boolean mBound;

    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            mService = new Messenger(service);
            mBound = true;
        }

        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected -- that is, its process crashed.
            mService = null;
            mBound = false;
        }
    };

    public void sayHello(View v) {
        if (!mBound) return;

        Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
        try {
            mService.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

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

    @Override
    protected void onStart() {
        super.onStart();

        bindService(new Intent(this, MessengerService.class), mConnection,
            Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();

        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }
}

(3)使用 AIDL:AIDL (Android Interface Definition Language) 提供了一种方式进行 IPC 通信。Messager 也是基于 AIDL 实现的。一般情况下,如果 Service 不需要同时处理多个客户端请求,则直接使用 Messager 即可。使用 AIDL 时,需要编写相关的 .aidl 文件来定义编程接口,SDK 则会根据该文件自动生成需要的抽象类,实现需要的接口并提供 IPC 支持。

onRebind()

当所有的客户端都解除与 Service 的绑定后,会执行 onUnbind() 方法,如果 Service 希望重新启动 Service 的时候,进行一些操作,区别于第一次启动 Service,可以通过 onUnbind() 返回 true,这样,下一次有客户端绑定 Service 时就会执行 onRebind() 而不是 onBind()

service binding tree lifecycle

参考

Bound Services 官方文档
《The Busy Coders Guide to Android Development》