Service详解_绑定服务实现

作者: 一笑小先生 | 来源:发表于2019-01-09 14:58 被阅读2次

    本篇文章主要讲解Service绑定服务的实现方式以及三种绑定方法。

    1. Service绑定服务

    绑定服务是Service的另一种使用方式,当Service处于绑定状态时,其代表着客户端-服务器接口中的服务器。当其他组件(如 Activity)绑定到服务时(有时我们可能需要从Activity组建中去调用Service中的方法,此时Activity以绑定的方式开启Service后,我们就可以轻松地方法到Service中的指定方法),组件(如Activity)可以向Service(也就是服务端)发送请求,或者调用Service(服务端)的方法,此时被绑定的Service(服务端)会接收信息并响应,甚至可以通过绑定服务进行执行进程间通信 (即IPC)。

    与启动服务不同的是绑定服务的生命周期通常只在为其他应用组件(如Activity)服务时处于活动状态,不会无限期在后台运行,也就是说宿主(如Activity)解除绑定后,绑定服务就会被销毁。那么在提供绑定的服务时,该如何实现呢?实际上我们必须提供一个 IBinder接口的实现类,该类用以提供客户端用来与服务进行交互的编程接口,该接口可以通过三种方法定义接口:

    • 扩展 Binder 类
    • 使用 Messenger
    • 使用 AIDL

    下面👇,我们将对这三种方式进行详细的介绍:

    1.1 扩展Binder类

    如果服务是提供给自有应用专用的,并且Service(服务端)与客户端相同的进程中运行(常见情况),则应通过扩展 Binder 类并从 onBind() 返回它的一个实例来创建接口。客户端收到 Binder 后,可利用它直接访问 Binder 实现中以及Service 中可用的公共方法。如果我们的服务只是自有应用的后台工作线程,则优先采用这种方法。 不采用该方式创建接口的唯一原因是,服务被其他应用或不同的进程调用。

    其使用开发步骤如下

    1. 创建BindService服务端,继承自Service并在类中,创建一个实现IBinder 接口的实例对象并提供公共方法给客户端调用
    2. 从 onBind() 回调方法返回此 Binder 实例。
    3. 在客户端中,从 onServiceConnected() 回调方法接收 Binder,并使用提供的方法调用绑定服务

    注意:此方式只有在客户端和服务位于同一应用和进程内才有效,如对于需要将 Activity 绑定到在后台播放音乐的自有服务的音乐应用,此方式非常有效。

    以下是一个扩展 Binder 类的实例,先看看Service端的实现:

    class LocalService : Service() {
    
        companion object {
            const val TAG = "LocalService"
        }
    
        private lateinit var thread: Thread
        private var quit = false
        private var count = 0
    
        override fun onBind(intent: Intent?): IBinder? = LocalBinder()
    
        inner class LocalBinder : Binder() {
            // 声明一个方法,getService。(提供给客户端调用)
            // 返回当前对象LocalService,这样我们就可在客户端端调用Service的公共方法了
            val service: LocalService = this@LocalService
        }
    
        override fun onCreate() {
            super.onCreate()
            Log.i(TAG, "Service is invoke Created")
            thread = Thread(Runnable {
                while (!quit) {
                    try {
                        Thread.sleep(1000)
                    } catch (e: Exception) {
                        e.printStackTrace()
                    }
                    count++
                }
            })
            thread.start()
        }
    
        fun getCount(): Int = count
    
        override fun onUnbind(intent: Intent?): Boolean {
            Log.i(TAG, "Service is invoke onUnbind")
            return super.onUnbind(intent)
        }
    
        override fun onDestroy() {
            super.onDestroy()
            this.quit = true
            Log.i(TAG, "Service is invoke onDestroy")
        }
    }
    

    LocalService类继承自Service,在该类中创建了一个LocalBinder继承自Binder类,LocalBinder中声明了一个getService方法,客户端可访问该方法获取LocalService对象的实例,只要客户端获取到LocalService对象的实例就可调用LocalService服务端的公共方法,如getCount方法,值得注意的是,我们在onBind方法中返回了binder对象,该对象便是LocalBinder的具体实例,而binder对象最终会返回给客户端,客户端通过返回的binder对象便可以与服务端实现交互。接着看看客户端BindActivity的实现:

    class BindActivity : AppCompatActivity() {
    
        private lateinit var conn: ServiceConnection
        private var localService: LocalService? = null
    
        companion object {
            const val TAG = "BindActivity"
        }
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_bind)
    
            conn = object : ServiceConnection {
                override fun onServiceDisconnected(name: ComponentName?) {
                    localService = null
                }
    
                override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
                    Log.d(TAG, "绑定成功调用:onServiceConnected")
                    localService = (service as LocalService.LocalBinder).service
                }
    
            }
    
            val intent = Intent(this, LocalService::class.java)
    
            bindServiceBtn.setOnClickListener {
                Log.d(TAG, "绑定调用:bindService")
                bindService(intent, conn, Context.BIND_AUTO_CREATE)
            }
    
            unBindServiceBtn.setOnClickListener {
                Log.d(TAG, "解除绑定调用:unbindService")
                localService?.let {
                    localService = null
                    unbindService(conn)
                }
            }
    
            getCountBtn.setOnClickListener {
                Log.i(TAG, localService?.getCount().toString())
            }
        }
    }
    

    在客户端中我们创建了一个ServiceConnection对象,该代表与服务的连接,它只有两个方法, onServiceConnected和onServiceDisconnected,其含义如下:

    • onServiceConnected(ComponentName name, IBinder service)
      系统会调用该方法以传递服务的 onBind() 方法返回的 IBinder。其中service便是服务端返回的IBinder实现类对象,通过该对象我们便可以调用获取LocalService实例对象,进而调用服务端的公共方法。而ComponentName是一个封装了组件(Activity, Service, BroadcastReceiver, or ContentProvider)信息的类,如包名,组件描述等信息,较少使用该参数。
    • onServiceDisconnected(ComponentName name)
      Android 系统会在与服务的连接意外中断时(例如当服务崩溃或被终止时)调用该方法。

    注意:当客户端取消绑定时,系统“绝对不会”调用该方法。

    在onServiceConnected()被回调前,我们还需先把当前Activity绑定到服务LocalService上,绑定服务是通过通过bindService()方法,解绑服务则使用unbindService()方法,这两个方法解析如下:

    • bindService(Intent service, ServiceConnection conn, int flags)
      该方法执行绑定服务操作,其中Intent是我们要绑定的服务(也就是LocalService)的意图,而ServiceConnection代表与服务的连接,它只有两个方法,前面已分析过,flags则是指定绑定时是否自动创建Service。0代表不自动创建、BIND_AUTO_CREATE则代表自动创建。
    • unbindService(ServiceConnection conn)
      该方法执行解除绑定的操作。

    Activity通过bindService()绑定到LocalService后,ServiceConnection#onServiceConnected()便会被回调并可以获取到LocalService实例对象mService,之后我们就可以调用LocalService服务端的公共方法了,最后还需要在清单文件中声明该Service。

    运行程序,点击绑定服务并多次点击绑定服务接着多次调用LocalService中的getCount()获取数据,最后调用解除绑定的方法移除服务,其结果如下:

    01-08 15:30:18.059 24842-24842/com.wangyy.service I/LocalService: Service is invoke onCreate
    01-08 15:30:18.059 24842-24842/com.wangyy.service I/LocalService: Service is invoke onBind
    01-08 15:30:18.060 24842-24842/com.wangyy.service I/BindActivity: 绑定成功调用:onServiceConnected
    01-08 15:30:21.123 24842-24842/com.wangyy.service I/BindActivity: 3
    01-08 15:30:22.384 24842-24842/com.wangyy.service I/BindActivity: 4
    01-08 15:30:25.607 24842-24842/com.wangyy.service I/BindActivity: 7
    01-08 15:30:28.159 24842-24842/com.wangyy.service I/LocalService: Service is invoke onUnbind
    01-08 15:30:28.160 24842-24842/com.wangyy.service I/LocalService: Service is invoke onDestroy
    

    通过Log可知,当我们第一次点击绑定服务时,LocalService服务端的onCreate()、onBind方法会依次被调用,此时客户端的ServiceConnection#onServiceConnected()被调用并返回LocalBinder对象,接着调用LocalBinder#getService方法返回LocalService实例对象,此时客户端便持有了LocalService的实例对象,也就可以任意调用LocalService类中的声明公共方法了。

    值得注意的是,我们多次调用bindService方法绑定LocalService服务端,而LocalService得onBind方法只调用了一次,那就是在第一次调用bindService时才会回调onBind方法。接着我们点击获取服务端的数据,从Log中看出我们点击了3次通过getCount()获取了服务端的3个不同数据,最后点击解除绑定,此时LocalService的onUnBind、onDestroy方法依次被回调,并且多次绑定只需一次解绑即可。此情景也就说明了绑定状态下的Service生命周期方法的调用依次为onCreate()、onBind、onUnBind、onDestroy。ok~,以上便是同一应用同一进程中客户端与服务端的绑定回调方式。

    1.2 使用 Messenger

    Messenger可以翻译为信使,通过它可以在不同的进程中共传递Message对象(Handler中的Messager,因此 Handler 是 Messenger 的基础),在Message中可以存放我们需要传递的数据,然后在进程间传递。如果需要让接口跨不同的进程工作,则可使用 Messenger 为服务创建接口,客户端就可利用 Message 对象向服务发送命令。同时客户端也可定义自有 Messenger,以便服务回传消息。这是执行进程间通信 (IPC) 的最简单方法,因为 Messenger 会在单一线程中创建包含所有请求的队列,也就是说Messenger是以串行的方式处理客户端发来的消息,这样我们就不必对服务进行线程安全设计了。

    Messenger 使用的主要步骤:

    1. 服务实现一个 Handler,由其接收来自客户端的每个调用的回调
    2. Handler 用于创建 Messenger 对象(对 Handler 的引用)
    3. Messenger 创建一个 IBinder,服务通过 onBind() 使其返回客户端
    4. 客户端使用 IBinder 将 Messenger(引用服务的 Handler)实例化,然后使用Messenger将 Message 对象发送给服务
    5. 服务在其 Handler 中(在 handleMessage() 方法中)接收每个 Message

    以下是一个使用 Messenger 接口的简单服务示例,服务端进程实现如下:

    class MessengerService : Service() {
    
        companion object {
            const val MSG_SAY_HELLO = 1
            const val TAG = "MessengerService"
        }
    
        /**
         * 创建Messenger并传入Handler实例对象
         */
        private val messenger: Messenger = Messenger(IncomingHandler())
    
        /**
         * 用于接收从客户端传递过来的数据
         */
        class IncomingHandler : Handler() {
            override fun handleMessage(msg: Message) {
                when (msg.what) {
                    MSG_SAY_HELLO -> {
                        Log.i(TAG, "thanks,Service had receiver message from client!")
                    }
                    else -> {
                        super.handleMessage(msg)
                    }
                }
            }
        }
    
        /**
         * 当绑定Service时,该方法被调用,将通过mMessenger返回一个实现
         * IBinder接口的实例对象
         */
        override fun onBind(intent: Intent?): IBinder? {
            Log.i(TAG, "Service is invoke onBind")
            return messenger.binder
        }
    
        override fun onCreate() {
            super.onCreate()
            Log.i(TAG, "Service is invoke onCreate")
        }
    
        override fun onUnbind(intent: Intent?): Boolean {
            Log.i(TAG, "Service is invoke onUnbind")
            return super.onUnbind(intent)
        }
    
        override fun onDestroy() {
            super.onDestroy()
            Log.i(TAG, "Service is invoke onDestroy")
        }
    }
    

    首先我们同样需要创建一个服务类MessengerService继承自Service,同时创建一个继承自Handler的IncomingHandler对象来接收客户端进程发送过来的消息并通过其handleMessage(Message msg)进行消息处理。接着通过IncomingHandler对象创建一个Messenger对象,该对象是与客户端交互的特殊对象,然后在Service的onBind中返回这个Messenger对象的底层Binder即可。下面看看客户端进程的实现:

    class MessengerActivity : AppCompatActivity() {
    
        private lateinit var conn: ServiceConnection
        private var mService: Messenger? = null
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_messenger)
    
            conn = object : ServiceConnection {
                override fun onServiceDisconnected(name: ComponentName?) {
                    mService = null
                }
    
                override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
                    mService = Messenger(service)
                }
    
            }
    
            val intent = Intent(this, MessengerService::class.java)
    
            bindServiceBtn.setOnClickListener {
                bindService(intent, conn, Context.BIND_AUTO_CREATE)
            }
    
            unBindServiceBtn.setOnClickListener {
                mService?.let {
                    mService = null
                    unbindService(conn)
                }
            }
    
            sendMsgBtn.setOnClickListener {
                sayHello()
            }
        }
    
        private fun sayHello() {
            mService?.let { messenger ->
                val msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0)
                try {
                    messenger.send(msg)
                } catch (e: Exception) {
                    e.printStackTrace()
                }
    
            }
        }
    }
    

    在客户端进程中,我们需要创建一个ServiceConnection对象,该对象代表与服务端的链接,当调用bindService方法将当前Activity绑定到MessengerService时,onServiceConnected方法被调用,利用服务端传递给来的底层Binder对象构造出与服务端交互的Messenger对象,接着创建与服务交互的消息实体Message,将要发生的信息封装在Message中并通过Messenger实例对象发送给服务端。关于ServiceConnection、bindService方法、unbindService方法,前面已分析过,这里就不重复了,最后我们需要在清单文件声明Service和Activity,由于要测试不同进程的交互,则需要将Service放在单独的进程中,因此Service声明如下:

    <service
        android:name=".MessengerService"
        android:process=":remote" />
    

    其中android:process=":remote"代表该Service在单独的进程中创建

    接着多次点击绑定服务,然后发送信息给服务端,最后解除绑定,Log打印如下:

    01-08 18:04:13.753 26092-26092/com.wangyy.service:remote I/MessengerService: Service is invoke onCreate
    01-08 18:04:13.754 26092-26092/com.wangyy.service:remote I/MessengerService: Service is invoke onBind
    01-08 18:04:21.214 26092-26092/com.wangyy.service:remote I/MessengerService: thanks,Service had receiver message from client!
    01-08 18:04:21.429 26092-26092/com.wangyy.service:remote I/MessengerService: thanks,Service had receiver message from client!
    01-08 18:04:23.069 26092-26092/com.wangyy.service:remote I/MessengerService: Service is invoke onUnbind
    01-08 18:04:23.071 26092-26092/com.wangyy.service:remote I/MessengerService: Service is invoke onDestroy
    

    通过上述例子可知Service服务端确实收到了客户端发送的信息,而且在Messenger中进行数据传递必须将数据封装到Message中,因为Message和Messenger都实现了Parcelable接口,可以轻松跨进程传递数据。

    以上的例子演示了如何在服务端解释客户端发送的消息,但有时候我们可能还需要服务端能回应客户端,这时便需要提供双向消息传递了,下面就来实现一个简单服务端与客户端双向消息传递的简单例子。

    先来看看服务端的修改,在服务端,我们只需修改IncomingHandler,收到消息后,给客户端回复一条信息。

    class IncomingHandler : Handler() {
            override fun handleMessage(msg: Message) {
                when (msg.what) {
                    MSG_SAY_HELLO -> {
                        Log.i(TAG, "thanks,Service had receiver message from client!")
                        //回复客户端信息,该对象由客户端传递过来
                        val client = msg.replyTo
                        //获取回复信息的消息实体
                        val replyMsg = Message.obtain(null, MSG_SAY_HELLO)
                        val bundle = Bundle()
                        bundle.putString("reply", "ok~,I had receiver message from you! ")
                        replyMsg.data = bundle
                        try {
                            client.send(replyMsg)
                        } catch (e: Exception) {
                            e.printStackTrace()
                        }
    
                    }
                    else -> {
                        super.handleMessage(msg)
                    }
                }
            }
        }
    

    接着修改客户端,为了接收服务端的回复,客户端也需要一个接收消息的Messenger和Handler,其实现如下:

     private val mReceiverReplyMsg = Messenger(ReceiverReplyMsgHandler())
    
        class ReceiverReplyMsgHandler : Handler() {
            override fun handleMessage(msg: Message) {
                when (msg.what) {
                    MessengerService.MSG_SAY_HELLO -> {
                        Log.i(TAG, "receiver message from service:" + msg.data.getString("reply"))
                    }
                    else -> {
                        super.handleMessage(msg)
                    }
                }
    
            }
        }
    

    除了添加以上代码,还需要在发送信息时把接收服务器端的回复的Messenger通过Message的replyTo参数传递给服务端,以便作为同学桥梁,代码如下:

    private fun sayHello() {
            mService?.let { messenger ->
                val msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0)
                msg.replyTo = mReceiverReplyMsg
                try {
                    messenger.send(msg)
                } catch (e: Exception) {
                    e.printStackTrace()
                }
    
            }
        }
    

    ok~,到此服务端与客户端双向消息传递的简单例子修改完成,我们运行一下代码,看看Log打印,如下:

    2019-01-08 21:46:13.498 4259-4259/com.wangyy.service:remote I/MessengerService: Service is invoke onCreate
    2019-01-08 21:46:13.500 4259-4259/com.wangyy.service:remote I/MessengerService: Service is invoke onBind
    2019-01-08 21:46:50.758 4259-4259/com.wangyy.service:remote I/MessengerService: thanks,Service had receiver message from client!
    2019-01-08 21:46:50.782 3736-3736/com.wangyy.service I/MessengerActivity: receiver message from service:ok~,I had receiver message from you! 
    2019-01-08 21:52:11.365 4259-4259/com.wangyy.service:remote I/MessengerService: Service is invoke onUnbind
    2019-01-08 21:52:11.367 4259-4259/com.wangyy.service:remote I/MessengerService: Service is invoke onDestroy
    

    由Log可知,服务端和客户端确实各自收到了信息,到此我们就把采用Messenge进行跨进程通信的方式分析完了,最后为了辅助大家理解,这里提供一张通过Messenge方式进行进程间通信的原理图:


    messenger_process.png

    1.3 使用 AIDL

    由于Messenger是以串行的方式处理客户端发来的消息,如果当前有大量消息同时发送到Service(服务端),Service仍然只能一个个处理,这也就是Messenger跨进程通信的缺点了,因此如果有大量并发请求,Messenger就会显得力不从心了,这时AIDL(Android 接口定义语言)就派上用场了, 但实际上Messenger 的跨进程方式其底层实现 就是AIDL,只不过android系统帮我们封装成透明的Messenger罢了 。因此,如果我们想让服务同时处理多个请求,则应该使用 AIDL。 在此情况下,服务必须具备多线程处理能力,并采用线程安全式设计。使用AIDL必须创建一个定义编程接口的 .aidl 文件。Android SDK 工具利用该文件生成一个实现接口并处理 IPC 的抽象类,随后可在服务内对其进行扩展。

    这部分主要是跨进程通信的内容,在此不过多的讲解,后面会有专门的文章讲述AIDL。

    关于绑定服务的注意点
    1. 多个客户端可同时连接到一个服务。不过,只有在第一个客户端绑定时,系统才会调用服务的 onBind() 方法来检索 IBinder。系统随后无需再次调用 onBind(),便可将同一 IBinder 传递至任何其他绑定的客户端。当最后一个客户端取消与服务的绑定时,系统会将服务销毁(除非 startService() 也启动了该服务)。
    2. 通常情况下我们应该在客户端生命周期(如Activity的生命周期)的引入 (bring-up) 和退出 (tear-down) 时刻设置绑定和取消绑定操作,以便控制绑定状态下的Service,一般有以下两种情况:
    • 如果只需要在 Activity 可见时与服务交互,则应在 onStart() 期间绑定,在 onStop() 期间取消绑定。
    • 如果希望 Activity 在后台停止运行状态下仍可接收响应,则可在 onCreate() 期间绑定,在 onDestroy() 期间取消绑定。
    1. 通常情况下(注意),切勿在 Activity 的 onResume() 和 onPause() 期间绑定和取消绑定,因为每一次生命周期转换都会发生这些回调,这样反复绑定与解绑是不合理的。此外,如果应用内的多个 Activity 绑定到同一服务,并且其中两个 Activity 之间发生了转换,则如果当前 Activity 在下一次绑定(恢复期间)之前取消绑定(暂停期间),系统可能会销毁服务并重建服务,因此服务的绑定不应该发生在 Activity 的 onResume() 和 onPause()中。
    2. 我们应该始终捕获 DeadObjectException DeadObjectException 异常,该异常是在连接中断时引发的,表示调用的对象已死亡,也就是Service对象已销毁,这是远程方法引发的唯一异常,DeadObjectException继承自RemoteException,因此我们也可以捕获RemoteException异常。
    3. 应用组件(客户端)可通过调用 bindService() 绑定到服务,Android 系统随后调用服务的 onBind() 方法,该方法返回用于与服务交互的 IBinder,而该绑定是异步执行的。

    相关文章

      网友评论

        本文标题:Service详解_绑定服务实现

        本文链接:https://www.haomeiwen.com/subject/brcgrqtx.html