Bound Service

本文详细介绍了如何在Android应用中创建和绑定BoundService,包括使用继承Binder类、使用Messenger和使用AIDL的方式。重点阐述了BoundService的特点、创建步骤以及与客户端的交互过程。此外,还提供了示例代码帮助开发者更好地理解和实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一个Bound Service相当于cient-server中的server。Bound Service允许其它组件(比如activity)绑定到Service中,发送请求,接收响应,甚至进行内部进程通信。Bound Service是有在有其它组件绑定它时才运行,一旦没有组件绑定时,系统将会摧毁这个Bound Service。

这部分展示如何创建一个Bound Service,包括如何从其它app组件绑定一个Service。可以结合Service部分。

The Basics (基础)

Bound Service通过对Service的继承来允许其它app的组件能够绑定到这个Service并和这个Service交互。为了能够被其它组件绑定,你必须实现onBind()函数,在这个函数中返回一个IBinder对象,这个IBinder定义了编程接口用于client与Service交互。

一个client可以通过调用bindService()来绑定一个Service。当这样做后,这个client必须实现ServiceConnetion类,这个类监视与Service的连接。bindService被调用后马上返回,当系统创建client和Service的连接时,ServiceConnection中的onServiceConnected()函数被调用,来派发IBinder这个对象。

多个client可以同时连接到一个service。但是系统仅仅在第一次被绑定时调用onBind()函数火气IBinder。在接下来的绑定中,系统都是将相同的IBinder床给其它clients,不会在调用onBind()函数。

当最后一个client取消绑定时,系统将会销毁这个service(除非这个service是通过startService被启动)。

当实现bound service时,最重要的一点是定义onBind()返回的接口。有几种不同的方式定义这接口,下面将一一讨论。

Creating a Bound Service (创建一个bound Service)

当创建一个bound Service时,你必须提供一个IBinder接口,client可以通过这个接口来和Service交互,有三种定义IBinder的方式。

Extending the Binder class (继承Binder类)

如果你的Service仅仅在你的app可用,而且和client运行在同一个进程中,你应该通过继承Binder类来创建你的Binder。client接收到这个Binder后可以直接访问Binder或Service的公共函数。

这非常适合当你的Service仅仅是你app的一个后台worker。但是当你的Service可被其它app使用或者在不同的进程运行时,你即不能用上面的方法构造Binder。

Using a Messenger (使用Messenger)

如果你的接口需要工作在不同的进程中,你可以在这个Service中创建一个Messenger接口。这种情况下,Service将会定义一个Handler对象处理不同的Message对象。client就可以通过Message 对象来对Service发送命令,在client中也可以自己定义一个messager来接收Service发送的Message。

Using AIDL (使用AIDL)

AIDL(Android interface definition language android接口定义语言)将所有对象都分解成操作系统可以理解的原型然后marshall这些原型在多进程间通信。上面使用Messager就是基于AIDL的,指示使用了Messenger作为底层机构。正如前面提到,Messenger在一个单线程中创建一个队列用来保存所有client的请求。所以Service在每次只接收一个请求。如果你想让你的Service每次接收同时接收多个请求,可以直接使用AIDL。在这种情况下,你的Service必须支持多线程操作并保证安全。

可以创建一个.aidl文件定义相关的编程接口来直接使用AIDL。android SDK将会用这个文件来创建一个实现这个接口的抽象类来处理IPC,你可以在你的Service中继承这个抽象类。

注意:大多数app最好不要使用AIDL来创建bound Service,因为这需要多线程操作,实现起来复杂。因此本文档没有详细介绍AIDL。若你真想使用AIDL,可以详见AIDL部分。

Extending the Binder class (继承Binder类)

如果你的Service仅仅本地app可以使用,并且不用跨进程,那么你可以实现你自己的Binder类一边client可以通过这个Binder来访问你Service的公共函数。

注意:只有当client和Service处于同一个app和进程时,上面这个才能工作,比如,当一个音乐app需要一个activity绑定到在后台播放音乐的Service中。

下面展示设置:

1. 在你的Service中创建一个Binder实例:

包含client可以调用的公共函数。

返回当前Service实例,通过这个实例调用Service的公共函数。

返回这个Service拥有的其它activity实例,通过这个实例调用其它activity的公共函数。

2. 在onBind()函数中返回这个Binder实例

3. 在client的onServiceConnected()回调函数中获取这个Binder实例,使用这个实例来调用Service的公共函数。

注意:之所以要求Service和client在同一个app中是为了client能够投射返回的对象并正确的调用它的API。另一个要求是client和Service必须在同一个进程中,因为这个技术不执行任何marshalling与进程间。

比如,下面的client可以通过返回的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();

    /**
     * Class used for the client Binder.  Because we know this service always
     * runs in the same process as its clients, we don't need to deal with IPC.
     */
    public class LocalBinder extends Binder {
        LocalService getService() {
            // Return this instance of LocalService so clients can call public methods
            return LocalService.this;
        }
    }

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

    /** method for clients */
    public int getRandomNumber() {
      return mGenerator.nextInt(100);
    }
}
在LocalBinder中提供getService()函数一边clients用来获取当前的LocalService实例。这样client就可以调用Service中的公共函数。例如:client可以调用serviced的getRandomNumber()。

下面的activity绑定到LocalService中,当单击一个按钮时调用Service中的getRandomNumber()函数。

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();
        // Bind to LocalService
        Intent intent = new Intent(this, LocalService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }

    /** Called when a button is clicked (the button in the layout file attaches to
      * this method with the android:onClick attribute) */
    public void onButtonClick(View v) {
        if (mBound) {
            // Call a method from the LocalService.
            // However, if this call were something that might hang, then this request should
            // occur in a separate thread to avoid slowing down the activity performance.
            int num = mService.getRandomNumber();
            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
        }
    }

    /** Defines callbacks for service binding, passed to bindService() */
    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            // We've bound to LocalService, cast the IBinder and get LocalService instance
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
        }
    };
}
上面例子展示client通过实现ServiceConnection来绑定到Service中。下部分将提供更多关于绑定到service的信息。

注意:上面例子中没有显式的解除去service的绑定,但是每个activity都会在一段时间后解除绑定(比如当activity暂停)。

Using a Messenger (使用Messenger)

如果你的service需要跟远程进程相互通信,那么你可以使用Messenger作为你service的提供的接口。这能让你在不实用AIDL的情况下进行内部进程通信(IPC)。

下面总结如何使用Messenger:

1. 在service中实现一个Handler,用来接收client的每次调用。

2. 使用Handler来创建Messenger对象(是Handler的一个引用)

3. Messenger创建一个IBinder,用于service在onBind()函数中返回给client。

4. client使用IBinder来实例化一个Messenger,用于发送Message对象到service。

5. 在service的handleMessage()函数中接收每个Message。

在这种方式下,client没有调用service的函数,而是通过发送message对象到service中。

下面是一个使用Messenger接口的Service:

public class MessengerService extends Service {
    /** Command to the service to display a message */
    static final int MSG_SAY_HELLO = 1;

    /**
     * Handler of incoming messages from clients.
     */
    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);
            }
        }
    }

    /**
     * Target we publish for clients to send messages to IncomingHandler.
     */
    final Messenger mMessenger = new Messenger(new IncomingHandler());

    /**
     * When binding to the service, we return an interface to our messenger
     * for sending messages to the service.
     */
    @Override
    public IBinder onBind(Intent intent) {
        Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
        return mMessenger.getBinder();
    }
}

service在Handler里面的handleMessage()函数根据Message的类别来处理消息。

client需要做的是根据从service中返回的IBinder构建一个Messenger对象,通过send()发送消息。下面的例子有一个Activity绑定到上面的service中,并发送一个MSG_SAY_HELLO的消息。

public class ActivityMessenger extends Activity {
    /** Messenger for communicating with the service. */
    Messenger mService = null;

    /** Flag indicating whether we have called bind on the service. */
    boolean mBound;

    /**
     * Class for interacting with the main interface of the service.
     */
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            // This is called when the connection with the service has been
            // established, giving us the object we can use to
            // interact with the service.  We are communicating with the
            // service using a Messenger, so here we get a client-side
            // representation of that from the raw IBinder object.
            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;
        // Create and send a message to the service, using a supported 'what' value
        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();
        // Bind to the service
        bindService(new Intent(this, MessengerService.class), mConnection,
            Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }
}

注意到上面的例子并没有展示service如何回到client,如果你想service能够回到client,那么你必须在client中创建一个Messenger。当client的onServiceConnected()回调函数被调用后,它发送一个包含client的Messenger的消息到service中。

你可以在例子MessengerService.java(service)和MessengerServiceActivities.java(client)中看到如何实现来回发送消息。

Binding to a Service (绑定一个Service)

app的组件(client)可以通过调用bindService()函数绑定一个Service,之后android系统捡回调用Service的onBind()函数,在这个函数中返回给client一个IBinder,用来与Service交互。

绑定到Service是异步的。bindService()函数马上返回,但不是马上返回一个IBinder给client。为了拿到IBinder,client必须创建一个ServiceConnection的实例并将这个传入bindService中。在ServiceConnection中含有一个回调函数,系统将通过这个回调函数返回从service的IBinder。

注意:只有Activity, service, ContentProvider可以绑定到一个service中,broadcast receiver不能帮到到service中。

所以为了绑定到一个service中,你必须:

1. 实现ServiceConnection

在这个类的实现中必须覆盖两个回调函数

onServiceConnected():系统调用这个函数来返回从service过来的IBinder。

onServiceDisconnected():当与service的连接中断时(比如当service发生故障或者被销毁时),系统会调用这个函数。但是当client解除绑定时,系统不会调用这个函 数。

2. 在client中调用bindService(),在函数参数中传入ServiceConnection的实例。

3. 当系统调用你的onServiceConnected()回调函数时,你可以调用Service中国的函数。

4. 调用unbindService()函数来解除绑定,当你的client被销毁时,会自动解除与service的绑定,但通常当你已经完成和service交互你最好解除绑定,或者当你的Activity被暂停时,你也最好解除绑定,因为这样对应的service在无人使用时可以被销毁。

上面的例子通过返回一个继承自Binder的IBinder到client中,通过这个IBinder获取service的实例,这样就可以通过这个实例来调用service的公共函数:

LocalService mService;
private ServiceConnection mConnection = new ServiceConnection() {
    // Called when the connection with the service is established
    public void onServiceConnected(ComponentName className, IBinder service) {
        // Because we have bound to an explicit
        // service that is running in our own process, we can
        // cast its IBinder to a concrete class and directly access it.
        LocalBinder binder = (LocalBinder) service;
        mService = binder.getService();
        mBound = true;
    }

    // Called when the connection with the service disconnects unexpectedly
    public void onServiceDisconnected(ComponentName className) {
        Log.e(TAG, "onServiceDisconnected");
        mBound = false;
    }
};

在bindService()函数中传入一个ServiceConnection的实例

Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
在bindService的第一个参数是Intent对象,显式的指定了要绑定的service。

第二个参数是ServiceConnection对象。

第三个参数是一个标识,可以有BIND_AUTO_CREATE, BIND_DEBUG_UNBIND 和BIND_NOT_FOREGROUND等参数。

Addtional notes (注意事项)

以下是绑定到service的注意事项:

1. 你应该一直捕获DeadObjectException错误,这个是当连接中断是被抛出。

2. Objects are reference counted across processes.

3. 可以根据你的需要在合适的地方进行绑定service和解除绑定。比如当你需要在你的Activity是可见的时候与service交互,那么你可以在onStart()中进行绑定,在onStop()中解除绑定,如果你希望当你的client被停止时还能接收service的回答,那么你可以在onCreate()中绑定service,在onDestroy()中解除绑定。这表面你希望在client的整个生命过程中使用service,如果这个service实在其它的进程中,那么你将提高这个进程的权重,系统更有可能会销毁这个进程。

注意:在Activity的onResume()和onPause()中国你不要绑定和解除绑定到一个service,因为这两个函数发生在状态转换的时候,在这两个函数中执行的代码应尽量少。另外当在一个app中有多个Activity绑定到一个service中,那么当发生两个Activity的状态转换时,系统将有可能由于一个Activity解除绑定而销毁service,而下个Activity有可能还要绑定这个service(这样就销毁了又重建),没必要。

更多信息详见ApiDemos中的RemoteService.java

Managing the Lifecycle of a Bound Service (管理Bound Service的生命周期)

当一个Service没有client绑定时,系统将会销毁这个Service(除非这个Service是通过onStartCommand()启动的)。这样对于bound Service你就不用去管理它的生命周期了。

但是如果你实现了onStartCommand()函数,那么你必须显式的停止这个Service,因为这个Service是被started的(而不是通过被绑定而创建)。在这种启动模式下,这个Service将会一直运行,直到自身调用stopSelf()或者其它的app组件调用stopService()而停止。

如果你的Service是被started而且接受绑定,那么当系统调用onUnbind()函数时,你可以在这个函数中返回true表示这个Service的onRebind()函数可以在下次client绑定Service时被调用(而不是调用onBind()函数)。在onRebind()中返回void,但是client仍然会在onServiceConnection()中接收到一个IBinder对象,下图显示了这个逻辑过程:



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值