文档章节

[翻译]Android Bound Services

WolfCS
 WolfCS
发布于 2014/03/23 11:04
字数 4665
阅读 107
收藏 4

一个bound service是一个client-server接口中的server端。一个bound service允许应用组件(比如activities)bind到它,发送请求,接收响应,甚至是执行进程间通信(IPC)。一个bound service在典型情况下,只有在它服务于另一个应用组件时才存活,而不是在后台无限期的运行。

这份文档向您说明了要如何创建bound service,包括在其他的应用组件中如何bind到service。然而,你也应该参考Services文档来大体地了解关于services的额外信息,比如如何在service中传送通知,设置service在前台运行,等等。

基本概念

一个bound service是一个Service类的实现,它允许其它应用bind到它并与它交互。为了给一个service提供binding功能,你必须实现onBind()回调方法。这个方法返回一个IBinder对象,该对象则定义了客户端可以用来与service进行交互的编程接口。

Binding到一个Started Service

如同在Services文档中讨论的那样,你可以创建一个service,既可以被started,也可以被bound。即,service可以通过调用startService()被started,从而允许service无限期的运行,也可以允许一个客户端通过调用bindService()来bind到service

如果你确实允许你的service被started和bound,则当service已经被started时,系统不会在所有的客户端都unbind时销毁那个service。相反,你必须通过调用stopSelf()stopService()来显式地停止那个service。

尽管你通常应该实现onBind()onStartCommand()之一,但有时需要同时实现两者。比如,一个音乐播放器可能会发现同时允许它的service无限期的运行及提供binding是很有用的。以这种方式,一个activity可以start service来播放一些音乐,而在用户离开了应用后音乐也可以继续播放。然后,在用户回到应用时,activity可以bind到service来恢复对回放的控制。

请确保阅读了关于管理一个Bound Service的生命周期的部分,来了解更多关于给一个started service添加binding功能时service生命周期的信息。


一个客户端可以通过调用bindService()来bind到service。当它bind时,它必须提供一个ServiceConnection的实现,其者监视与service之间的连接。bindService()将立即返回,并且没有返回值,但当Android系统创建客户端和service之间的连接时,它会调用ServiceConnectiononServiceConnected(),来传送IBinder给客户端用于和service进行通信。

然而,系统只有在第一个客户端binds时才会去调用你的service的onBind()来获取IBinder。随后系统传送相同的IBinder给其它bind的客户端,而不再次调用onBind()

当最后一个客户端从service unbinds时,系统将销毁service(除非service也通过startService()被started了)。

当你实现你的bound service时,最重要的部分是定义你的onBind()回调方法返回的接口。你可以用一些不同的方法来定义你的service的IBinder接口,下面的小节将讨论每种技术。

创建一个Bound Service

当创建一个提供binding功能的service时,你必须提供一个客户端可以用于与service端交互的提供了编程接口的IBinder。有三种方法你可以用来定义这样的接口:

  • 扩展Binder类

    如果你的service是你自己的应用私有的,并且与客户端运行在相同的进程中(很常见的情况),你应该通过扩展Binder类来创建你的接口,并在onBind()方法中返回一个它的实例。客户端接收到Binder,然后就可以使用它来直接访问Binder实现或Service的可用的public方法。

    当你的service只是你自己的应用的一个后台工作者时,这是首选的技术。不使用这种方式来创建你的接口的仅有的理由就是,你的service被其他应用或跨越不同的进程被用到了。

  • 使用一个Messenger

    如果你需要你的接口跨进程工作,你可以借助于一个Messenger来为你的service创建一个接口。用这种方式,service定义了一个Handler,来响应不同类型的Message对象。这个Handler是一个Messenger的基础,然后后者可以与客户端共享一个IBinder,允许客户端使用Message对象给service发送命令。此外,客户端可以定义一个它自己的Messenger以使service可以发送消息回去。

    这是执行进程间通信(IPC)最简单的方式,因为Messenger把所有的请求入队到一个单独的线程,因此你不需要为线程安全而外设计你的service。

  • 使用AIDL

    AIDL (Android Interface Definition Language)执行所有将对象分解成操作系统可以理解的元语的工作,并在进程之间处理它们来执行IPC。前面的一种技术,即使用一个Messenger,实际上是以AIDL为它的底层结构的。如上面提到的,Messenger在一个单独的线程中创建一个所有的客户端请求的队列,以使service在某一时刻只接收一个请求。如果,然而,你想要你的service并行的处理多个请求,那么你可以直接使用AIDL。在这种情况下,你的service必须具有多线程的能力,并且要被构建的是线程安全的。

    要直接使用AIDL,你必须创建一个定义了编程接口的.aidl文件。Android SDK工具使用这个文件来产生一个实现了接口并处理IPC的抽象类,随后你可以在你的service中扩展这个抽象类。

注意:大多数应用不应该使用AIDL来创建一个bound service,因为它可能需要多线程能力,并可能导致一个更复杂的实现。因此,AIDL对于大多数应用都是不合适的,并且这份文档中不讨论如何使用它来实现你的service。如果你确认你需要直接使用AIDL,请参考AIDL文档。

扩展Binder类

如果你的service只被本地应用用到了,并且不需要跨进程工作,那么你可以实现你自己的Binder类,来为你的客户端提供直接访问service中的public方法的能力。

注意:这种方法只有在客户端和service在相同的应用和进程中时才有效,当然这种情况很常见。例如,对于一个音乐应用,需要bind一个activity到它自己的在后台播放音乐的service,就很有效。

这里是如何设置它:

  1. 在你的service中创建一个下述类型之一的Binder实例:

    (1). 包含了客户端可以调用的public方法

    (2). 返回当前的Service实例,其中具有客户端可以调用的public方法

    (3). 或者,返回另一个类的实例,其中寄宿了service,具有客户端可以调用的public方法。

  2. onBind()回调方法中返回这个Binder实例。

  3. 在客户端中,从onServiceConnected()回调方法接收这个Binder,并使用提供的方法来调用bound service。

注意:service和客户端必须位于相同的应用中的理由是,这样客户端可以强制类型转换返回的对象,并适当地调用它的APIs。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();
    /**
     * 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()来获取当前的LocalService实例。这将允许客户端调用service中的public方法。比如,客户端可以调用service的getRandomNumber()。

这里是一个activity,它bind到LocalService,并在button被点击时调用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;
        }
    };
}

上面的例子演示了客户端如何使用一个ServiceConnection实现和onServiceConnected()回调bind到service。下一节将提供更多关于binding到service的这一过程的信息。

注意:上面的例子没有显式地从service unbind,但所有的客户端都应该在一个适当的时间点 (比如当activity pause时) unbind。

.更多示例代码,请参考ApiDemos中的LocalService.java类和LocalServiceActivities.java类。

使用一个Messenger

与AIDL的比较

当你需要执行IPC时,使用一个Messenger来实现你的接口比通过AIDL来实现它要简单,因为Messenger将所有的调用入队到service,然而,一个纯粹的AIDL接口发送并发的请求给service,这将必须要处理多线程。

对于大多数应用,service不需要执行多线程,因此使用一个Messenger允许service每次只处理一个调用。如果你的service一定要是多线程的,那么你应该使用AIDL来定义你的接口。


如果你需要你的service与远端进程通信,那么你可以使用一个Messenger来为你的service提供接口。这项技术允许你在不需要使用AIDL的情况下执行进程间通信(IPC)。

这里是一个如何使用一个Messenger的总结:

以这种方式,客户端不调用service的"方法"。而是,客户端传送"消息" (Message 对象),service在它的Handler中接收消息。

这里有一个简单的使用了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();
    }
}

注意,Handler中的handleMessage()方法正是service接收传入的Message并根据what成员决定做什么的地方。

一个客户端所需要做的就是创建一个基于service返回的IBinderMessenger,并使用send()来发送一个消息。比如,这里是一个简单的activity,它binds到service,并传送MSG_SAY_HELLO消息给service:

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可以如何响应客户端。如果你想要service响应,则你也需要在客户端创建一个Messenger。然后当客户端接收到onServiceConnected()回调时,它发送一个Message 给service,其中在send()方法的replyTo参数中包含了客户端的Messenger

你可以参考示例MessengerService.java (service)和MessengerServiceActivities.java (client)来看一下如何提供两路消息的例子。

Binding到一个Service

应用组件(客户端)可以通过调用bindService()来bind到一个service。然后,Android系统调用service的onBind()方法,它会返回一个IBinder用于与service交互。

binding是异步的。bindService()会立即返回,而不给客户端返回IBinder。要接收IBinder,客户端必须创建一个ServiceConnection的实例,并把它传递给bindService()ServiceConnection包含一个回调方法,系统可以调用它来传送IBinder

注意:只有activities,services,和content providers可以bind到一个service——你不能在一个broadcast receiver中bind到一个service。

因而,要在客户端中bind到一个series,你必须:

  1. 实现ServiceConnection.

    你的实现必须覆写两个回调方法:

    onServiceConnected()

        系统调用这个方法来传送service的onBind()方法返回的IBinder

    onServiceDisconnected()

        当与service的连接意外断开时,Android系统会调用它。比如当service发生了crash或已       经被杀掉时。当client unbinds时则不会被调到。

  2. 调用bindService(),并传入ServiceConnection的实现。

  3. 当系统调用了你的onServiceConnected()回调时,你就可以开始调用service了,使用接口所定义的方法。

  4. 要从service断开连接,则调用unbindService()

    当你的客户端被销毁时,它将从service unbind,但你应该总是在你完成了与service的交互或者你的activity pause时unbind,以使得service可以在它没有被用到时关闭。(下面有更多关于bind和unbind的合适的时间的讨论。)

例如,下面的代码片段把客户端与上面通过扩展Binder类创建的service连接起来,它所必须做的一切就是把返回的IBinder强制类型转换到LocalService类,请向LocalService实例发起请求:

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;
    }
};

借助于这个ServiceConnection,客户端可以通过把它传递给bindService()来bind到一个service。比如:

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

补充说明

这里是关于binding到一个service的一些重要的说明:

  • 你应该总是捕获DeadObjectException一场,当连接破坏时它会被抛出。这是远程方法抛出的仅有的异常。

  • 对象是在进程间引用计数的。

  • 你通常应该在客户端的生命周期中对应的bring-up和tear-down的时刻成对的调用binding和unbinding。比如:

    如果你只需要在你的activity可见时与service交互,则你应该在onStart()中bind,并在onStop()unbind。

    如果你想要你的activity即使已经被stopped了,也要在后台接收响应,则你可以在onCreate()中bind,并在onDestroy()中unbind。注意,这意味着你的activity需要在它运行的所有时间都使用service(即使是在后台),因此如果service在另一个进程,则你将增加那个进程的weight,并且它变得更可能被系统杀掉。

注意:通常你不应该在你的activity的onResume()onPause()中bind和unbind,因为这些回调在每一个生命周期事务中都会发生,你应该把这些事务中发生的处理量降到最低。如果你的应用中的多个activities bind到相同的service,并且在这些activities中的两个之间有一个事务,则在当前的activity unbinds (在pause中)之后,下一个binds (在resume中) 之前可能会发生service的销毁和重建。(这种activities如何协调他们的生命周期的activity事务,在Activities文档中有描述)。

更多演示如何bind到一个service的示例代码,可以参考ApiDemos中的RemoteService.java类。

管理一个Bound Service的生命周期

当所有的客户端都从service unbound时,Android系统会销毁它(除非它也同时通过onStartCommand()被started了)。因此,你不需要管理你的service的生命周期,如果它是纯粹的bound service的话——Android系统会基于它是否被bound到了客户端来为你管理它。

然而,如果你选择实现onStartCommand()回调方法,则你需要显式的来停止service,因为现在service被认为是被started的了。在这种情况下,service会一直运行,直到service通过stopSelf()来终止它自己,或者另一个组件调用stopService(),而不管它是否被bound到任何的客户端。

此外,如果你的service是被started的,并接受binding,那么当系统调用了你的onUnbind()方法,如果你想要在下次客户端binds到service时接收一个对onRebind()的调用(而不是接收一个对onBind()的调用),你可以选择返回true。onRebind()返回void,但是客户端仍然在它的onServiceConnected()回调中接收IBinder。下面的图1描绘了这种生命周期的逻辑。

图1. The lifecycle for a service that is started and also allows binding.

更多关于一个started的service的生命周期的信息,请参考Services文档。

Done。

© 著作权归作者所有

共有 人打赏支持
WolfCS
粉丝 80
博文 147
码字总数 505184
作品 4
杭州
高级程序员
私信 提问
Activity和Service交互

As discussed in the Services document, you can create a service that is both started and bound. That is, the service can be started by calling , which allows the service to run ......

会飞的蝌蚪
2014/05/30
0
0
adb install 深入分析

adb install 也是用的pm命令去安装的,所以开始是在pm.java中。 frameworksbaseservicesjavacomandroidserverpm 1、调用pm程序开始安装 得用Pm安装时,一般是shell运行一个pm命令,并传送相应...

九洲北研
2013/03/30
0
0
Android包管理机制(三)PMS处理APK的安装

相关文章 Android包管理机制系列 前言 在上一篇文章Android包管理机制(二)PackageInstaller安装APK中,我们学习了PackageInstaller是如何安装APK的,最后会将APK的信息交由PMS处理。那么P...

刘望舒
2018/07/13
0
0
Android service的使用

Services 一个Service是一个应用组件,它可以在后台执行耗时长的操作,而不提供用户接口。另一个应用组件可以启动一个service,然后它将在后台持续运行,即使用户切换到了另一个应用。此外,...

WolfCS
2014/03/13
0
0
没有AI芯片,你的手机也能实现离线神经网络机器翻译

     编者按:去年10月份,微软跟华为合作,首先在具有AI芯片的Mate 10手机上实现了AI驱动型离线翻译功能。现在,Microsoft Translator将这一功能扩展到所有安卓、iOS和亚马逊Fire设备,...

微软亚洲研究院
2018/04/19
0
0

没有更多内容

加载失败,请刷新页面

加载更多

MySQL查询执行

当我们希望MySQL能够以更高的性能运行查询时,最好的办法就是弄清楚MySQL是如何优化和执行查询的。一旦理解了这一点,很多查询优化工作实际上就是遵循一些原则让优化器能够按照预想的合理方式...

Linux就该这么学
8分钟前
0
0
爱可生开源社区官网正式发布啦!

近期大事记 2018/12/31 DBLE年度报告发版 2019/01/07 DBLE 2.18.12.0 新版发布,修复 issue 60+ 2019/01/09 DBLE 2.18.12.0 Release Notes 详细解读 2019/01/15 DBLE Logo 首发 + DBLE 团队迎......

爱可生
16分钟前
0
0
【分布式缓存系列】Redis实现分布式锁的正确姿势

一、前言   在我们日常工作中,除了Spring和Mybatis外,用到最多无外乎分布式缓存框架——Redis。但是很多工作很多年的朋友对Redis还处于一个最基础的使用和认识。所以我就像把自己对分布式...

编辑之路
27分钟前
0
0
3.x 在Unix系统上面启动守护进程

12.14 在Unix系统上面启动守护进程 问题 你想编写一个作为一个在Unix或类Unix系统上面运行的守护进程运行的程序。 解决方案 创建一个正确的守护进程需要一个精确的系统调用序列以及对于细节的...

dragon_tech
27分钟前
0
0
ES6中的class

class Point {constructor(x, y, z) {this.x = x;this.y = y;this.z = z;}toString() {return `${this.x},${this.y}`}get prop() {return `获取的是get${this.z...

chinahufei
30分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部