Android Service

[TOC] 在Android中 Service 是一个可以在后台执行长时间操作而不提供用户界面的应用组件。Service可以有其他应用组件启动,而且当用户切换到其他应用,Service仍将在后台运行。此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信(IPC)。例如,Service可以处理网络事务、播放音乐,执行文件I/O或与内容提供程序交互,而所有这一切均可在后台运行。

注意:

  1. Service是在其托管进程的主进程中运行,它既不创建自己的线程,也不在单独的进程中运行(除非另行指定)。这意味着,==在Service中执行任何耗时操作都应该在Service内创建新线程来完成这项操作。== 通过使用单独的线程,可以降低发生“应用无响应”(ANR)错误的风险。
  2. 任何组件(如Activity)都可以控制同一个Service,而系统也只会创建一个对应的Service实例。

Service服务基本上可以分为两种形式:

1. 启动: 当应用组件(如Activity)通过调用 startService() 启动Service时,Service即处于“启动”状态。一旦启动,服务即可在后台无限期运行,即使启动服务的组件已经被销毁也不受影响。已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。例如,它可能通过网络下载或上传文件。操作完成后,服务自动停止运行。

2. 绑定: 当应用组件(如Activity)通过调用 bindService() 绑定到Service时,Service即处于“绑定”状态。绑定Service提供一个客户端-服务器接口,允许组件和Service进行交互、发送请求、获取结果,甚至是利用进程间通信(IPC)跨进程执行这些操作。仅当有应用组件绑定时,绑定Service才会运行。多个组件可以同时绑定到同一个Service,但全部取消绑定后,该Service就会被销毁。

这是两种形式的Service的生命周期:

**注意:**Service是可以同时以这两种方式运行,也就是说,它就可以是启动Service(启动后无限运行),也允许绑定(随绑定组件一起销毁)。问题在于Service是否实现了一组方法: onStartCommand()(允许组件启动Service) 和 onBind() (允许绑定Service)。

1.Service的基本应用

创建Service,必须是Service的子类。 如下:

open class BaseService :Service(){
    val TAG = "hugo"
    override fun onCreate() {
        super.onCreate()
        LogUtil.i(TAG,"onCreate-----------")
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        LogUtil.i(TAG,"onStartCommand-----------")
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onDestroy() {
        super.onDestroy()
        LogUtil.i(TAG,"onDestroy-----------")
    }
    override fun onBind(intent: Intent?): IBinder? {
        LogUtil.i(TAG,"onBind-----------")
        return null
    }


    override fun onUnbind(intent: Intent?): Boolean {
        LogUtil.i(TAG,"onUnbind-----------")
        return super.onUnbind(intent)
    }
}

复制代码

然后根据需要重写一些回调方法:

  • onStartCommand() :当另一个组件(如Activity)通过调用 startService() 请求启动服务时,系统就会调用此方法。一旦执行此方法,Service就会启动并可在后台无限期运行。所以如果实现这个方法,在完成服务任务后,需要手动通过调用 stopSelf() stopService() 方法来停止服务。(如果Service只想提供绑定服务,则可以不用实现此方法。)
    注意: 该方法的返回值是用于描述系统应该如何在服务终止的情况下继续运行服务,该方法返回的值必须是以下常量之一:

1. START_NOT_STICKY:如果系统在 onStartCommand() 返回后终止服务,则除非有Intent要传递,否则系统==不会==重建服务。这是最安全的选项,可以避免在不必要时应用能够轻松重启所有未完成作业的服务。

2. START_STICKY:如果系统在 onStratCommand()返回后终止服务,则会==重建服务==并调用onStartCommand(),但==不会==重新传递最后一个Intent。相反,除非有Intent要启动服务(在这种情况下,将传递这些Intent),否则系统会通过一个空Intent调用onStartCommand()。这个适用于不执行命令、但无限期运行并等待作业的媒体播放器(或类似服务)。 3. START_REDELIVER_INTENT:如果系统在 onStratCommand()返回后终止服务,则会==重建服务==,并通过传递给服务的最后一个Intent调用onStartCommand()。任何挂起的Intent均依次传递。这适用于主动执行应该立即恢复的作用(例如下载文件)的服务。

  • onBind() :当另一个组件想通过调用 bindService() 与Service绑定时,系统将调用此方法。这个方法实现中必须返回 IBinder 提供一个接口,供客户端用来和Service进行通信。(如果不希望允许绑定,则应返回null。)
    注意: 该方法只有在第一个组件通过调用 bindService() 与Service绑定时,系统才会调用,而组件和以及有其他组件绑定过的Service绑定时,系统是不会在调用该方法。
  • onUnBind() :当通过 bindService() 绑定服务后,一旦该服务与所有组件之间的绑定全部取消后,系统会调用该方法。
  • onRebind() : 该方法在调用font color=0099ff> onUnBind() 方法是返回 true时在重新绑定时,系统会调用该方法。
  • onCreate() :首次创建Service时,系统将调用此方法来进行一些一次性设置程序(在调用 onStartCommand() onBind() 方法之前)。如果Service已经在运行,则不会调用此方法。
  • onDestroy():当Service不在使用被销毁时,系统将调用此方法。应该实现这个方法用来清理Service用到的所有资源(如线程、注册的侦听器、接收器等)。这是Service接收的最后一个调用。
  • onConfigurationChanged() :该方法是在配置发生改变时调用,如屏幕旋转时,系统就会回调该方法。

如果组件通过调用 startService() 启动服务(这会导致对 onStartCommand() 的调用),则服务将会一直运行,知道服务使用 stopSelf() 自行停止运行,或由其他组件通过调用 stopService() 停止它运行为止。

如果组件通过调用 bindService() 来创建绑定服务(且未调用 onStartServic() ),则服务只会在该组件与其绑定时运行。一旦该服务与所有的组件之间的绑定全部取消,系统便会销毁它。

在使用Service时我们必须在清单文件(AndroidManifest.xml)中声明我们创建的服务如下:

<application ...>
    <service android:name=".BaseService"/>
</application>
复制代码

这样我们就可在组件中使用 startService() bindService() 方法启动或绑定服务了。

2.创建启动Service

1.启动服务

启动服务由另一个组件(如Activity)通过调用 startService() 启动,这会调用服务的 onStartCommand() 方法。

代码示例如下:

val intent = Intent(this,BaseService::class.java)
startService(intent)
复制代码

使用启动创建Service的生命周期如下:

onCreate() -> onStartCommand() -> onDestroy()
注意: Service只有在还没有创建时,启动才会调用 onCreate() 方法,如果已经创建在调用 startService()方法时,Service 只会调用 onStartCommand()方法。

2.停止服务

服务启动后,除非系统必须回收内存资源,否则系统不会停止或销毁服务,而且服务在 onStartCommand() 返回后会继续运行。其生命周期是独立于启动它的组件的,并且可以无限期的运行,即便启动它的组件被销毁了也是没有影响的。所以我们必须在服务中结束工作后通过调用 stopSelf() 方法来自行停止运行,或者由另一个组件通过调用 stopService() 方法来停止运行。
代码示例如下:

class MyService :Service(){

     override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        
        this.stopSelf()

        return super.onStartCommand(intent, flags, startId)
    }
}

class MainActivity : AppCompatActivity(){
    
    fun stopService(){
        val intent = Intent(this,MyService::class.java)
        this.stopService(intent)
    }
}

复制代码

一旦调用了 stopService() stopSelf() 方法停止服务,系统就会尽快的销毁服务。

但是,如果服务同时处理多个 onStartCommand() 请求,则应该在所有的启动请求处理完成之后在停止服务,而不是在第一个请求结束后而新的请求还未完成时就停止服务,这样会使第二个启动请求终止运行。为了避免这个问题,可以使用 stopSelf(int) stopSelfResult(int) 方法来确保服务停止请求始终是在最新的请求完成后。
stopSelfResult(int) :该方法会返回一个 Boolean 类型的值,如果为true,那么这个请求就是最新的请求,系统就会把该服务停止,如果为false,那么这个请求不是最新的请求,系统就不是停止该服务。
stopSelf(int) :该方法是一个没有返回值的stopSelfResult(int)方法,除了没有返回值,其他的都一样。

示例代码如下:

class MyService :Service(){

     override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        
        this.stopSelf(startId)
        
        //或var boolean = this.stopSelfResult(startId)

        return super.onStartCommand(intent, flags, startId)
    }
}
复制代码

注意: 为了避免浪费系统资源和消耗电池电量,我们应该在工作完成后停止服务。即使服务启用了绑定,一旦收到了启动请求的调用,那么我们也仍需要亲自停止服务。

3.创建绑定服务

绑定服务由应用组件通过调用 bindService() 与服务绑定,以便创建长期连接。

要创建绑定服务,必须实现 onBind() 回调方法以返回 IBinder 用于定义组服务通信接口。然后其他应用组件可以调用 bindService() 返回来进行绑定并获得 onBind() 回调方法返回的 IBinder 对象,组件可以通过 IBinder 对象和服务进行通信。绑定服务只是用于其绑定的组件,因此当没有组件绑定到该服务时,系统会自行销毁该服务(所以绑定服务不用像 通过 onStartCommand() 启动的服务需要手动来停止服务)。

绑定服务代码如下:

class MyService :BaseService(){




    override fun onBind(intent: Intent?): IBinder? {
        super.onBind(intent)
        Toast.makeText(this,"服务绑定了",Toast.LENGTH_SHORT).show()
//        this.stopSelf()

        return MyBinder()
    }

    override fun onUnbind(intent: Intent?): Boolean {
        Toast.makeText(this,"服务解绑",Toast.LENGTH_SHORT).show()
        return super.onUnbind(intent)
//        return true
    }

    public interface MyIBinder{
        fun invokeMethodInMyService()
    }

    open class MyBinder :Binder() ,MyIBinder{

        fun stopService(){

        }

        override fun invokeMethodInMyService() {
            TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
        }

    }
}


//MainAcitity
    if(myServiceConnection == null){
        myServiceConnection =  MyServiceConnection()
    }
    bindService(Intent(this,MyService::class.java),myServiceConnection, Context.BIND_AUTO_CREATE)

class MyServiceConnection:ServiceConnection{
        val TAG = "hugo"
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
        //这个是绑定服务时回调接收 IBinder 实例用的
            var binder = service as MyIBinder
            binder.invokeMethodInMyService()
            LogUtil.i(TAG,"onServiceConnected------  ComponentName:$name")
        }

        override fun onServiceDisconnected(name: ComponentName?) {
        //这个是当出现意外和绑定服务断开时的回调
            LogUtil.i(TAG,"onServiceDisconnected------  ComponentName:$name")
        }

    }
复制代码

在绑定服务时需要传入三个参数第一个是==Intent==,第二个是==ServiceConnection==接口的实现类,第三个是绑定的操作选项这个值可以是以下:

0
Context.BIND_AUTO_CREATE,
Context.BIND_DEBUG_UNBIND,
Context.BIND_NOT_FOREGROUND,
Context.BIND_ABOVE_CLIENT,
Context.BIND_ALLOW_OOM_MANAGEMENT,
Context.BIND_WAIVE_PRIORITY

这下面的是混合模式

0
Context. BIND_AUTO_CREATE,
Context.BIND_DEBUG_UNBIND,
Context.BIND_NOT_FOREGROUND,
Context. BIND_ABOVE_CLIENT,
Context.BIND_ALLOW_OOM_MANAGEMENT,
Context.BIND_WAIVE_PRIORITY,
Context.BIND_IMPORTANT
Context.BIND_ADJUST_WITH_ACTIVITY.

这些值的具体作用请看官方说明 注意需要科学上网

需要注意:
onCreate() 只有在服务第一次启动时才会调用;
onBind()方法只有在第一次绑定时才会调用,从第二次绑定开始都不会在调用该方法。

使用绑定服务创建Service生命流程如下:

onCreate() -> onBind()->ServiceConnection.onServiceConnected() ->onUnbind() ->onDestroy()
注意:当在 onUnbind()方法返回true时, 下一次绑定时会走 onRebind()方法。(这个只在使用了启动服务后才会生效)

4.解绑服务

绑定服务可以不用去解绑,因为绑定服务会在绑定的组件销毁的时候自动解绑,但是不建议这么做,因为这样会产生ServiceConnection泄漏 如下:

 android.app.ServiceConnectionLeaked: Activity com.hugo.myservice.OtherActivity has leaked ServiceConnection com.hugo.myservice.OtherActivity$MyServiceConnection@c818357 that was originally bound here
        at android.app.LoadedApk$ServiceDispatcher.<init>(LoadedApk.java:1610)
        at android.app.LoadedApk.getServiceDispatcher(LoadedApk.java:1502)
        at android.app.ContextImpl.bindServiceCommon(ContextImpl.java:1659)
        at android.app.ContextImpl.bindService(ContextImpl.java:1612)
        at android.content.ContextWrapper.bindService(ContextWrapper.java:698)
        at com.hugo.myservice.OtherActivity$onCreate$3.onClick(OtherActivity.kt:33)
        at android.view.View.performClick(View.java:6597)
        at android.view.View.performClickInternal(View.java:6574)
        at android.view.View.access$3100(View.java:778)
        at android.view.View$PerformClick.run(View.java:25885)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
复制代码

所以推荐在绑定组件销毁前调用unbindService()方法进行解绑操作 如下:


    override fun onDestroy() {
        super.onDestroy()
        //myServiceConnection 是 ServiceConnection 的实现类对象
        if(myServiceConnection != null){
           unbindService(myServiceConnection)
        }
    }
复制代码

调用unbindService()时传入绑定时传入的ServiceConnection 对象。

在解绑后服务不会马上销毁,只有所有与该服务绑定的组件都解绑且该服务没有用startService()启动过,这是系统才会销毁该服务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值