之前几篇文章简单梳理了在Android系统的四大组件之一,最主要的界面Activity
中,使应用程序与用户进行交互响应的相关知识点,那对于应用程序中不需要与用户交互的逻辑,又要用到哪些内容呢?本文开始将介绍应用程序无需界面交互的内部交互相关知识点,首先从另外一个四大组件之一的服务Service
开始。
在清单文件一文的组件声明中,已经知道服务Service
与界面Activity
一样,都要在清单文件中注册声明。同样的,每个注册声明的服务Service
类向上追溯都必须继承自android.app.Service父类,因此服务Service
也有自己的生命周期。
生命周期
Service
主要负责应用程序中不需要界面展示或交互的长时间操作,像是播放音乐,网络请求等都是可以在服务Service
中完成的。与界面Activity
的生命周期一样,在服务Service
的声明周期内不允许执行耗时操作,所以,虽然服务Service
中可以进行长时间操作,但是仍然需要将这部分耗时操作放入非Android系统主线程中。
(调用构造方法)对象实例化
首次启动新的服务Service
时,系统会申请内存空间存储该服务Service
的实例化对象。服务Service
有两种启动方式,其一与界面Activity
一样需要借助意图Intent
对象,调用上下文环境Context
对象的startService(Intent service)
启动。
其二是进程间通信时所用的方式,不仅要借助意图Intent
对象,还要使用实现android.content.ServiceConnection接口的对象,进而调用上下文环境Context
对象的bindService(Intent service, ServiceConnection conn, int flags)
方法,而参数三 flags 标记了启动该服务Service
时所绑定的模式,通常使用Context.BIND_AUTO_CREATE
标记会自动创建当前绑定的服务Service
。除此之外,还可以按位或的形式追加其他标记,例如追加|Context.BIND_NOT_FOREGROUND
标记当前绑定服务Service
为较低优先级的后台服务,在手机息屏或高能耗时,系统会优先杀死低优先级的服务;而追加|Context.BIND_IMPORTANT
标记可以提升绑定服务Service
的优先级为前台服务,在手机息屏或高能耗时,只要当前应用程序存活,当前服务Service
就不会被杀死;如果追加|Context.BIND_ABOVE_CLIENT
标记当前绑定服务Service
的优先级要高于当前应用程序,当手机息屏或高能耗时,系统可能会先杀死应用程序,但是当前服务Service
仍然存活。
Android系统为不同进程间的通信提供了一套AIDL语言规范,详情将在以后的文章中介绍,这里只需了解在实现ServiceConnection
接口中,要重写两个方法,分别是在进程通信接口与服务Service
连接成功后回调的onServiceConnected(ComponentName name, IBinder service)
方法,和连接断开之后回调的onServiceDisconnected(ComponentName name)
方法。
上述两种启动方式,都会触发系统在当前应用程序的清单文件中查找对应注册过的服务Service
,在找到之后,就会创建其实例化对象,如果在清单文件中没有找到对应的服务Service
,将不会有任何错误或异常。
与启动未注册的服务
Service
不同的是,当调用startActivity()
系列方法启动一个没有在清单文件中注册过的界面Activity
时,系统通常会抛出android.content.ActivityNotFoundException异常。
(调用attachBaseContext(Context base))加载运行环境
由于Service
也是android.content.ContentWrapper
的子类,所以系统在创建服务Service
的实例化对象后,也会优先对其加载上下文运行环境,将参数 base 作为当前应用程序的Context
对象与该服务Service
绑定。在该方法被调用之后的任意位置,就可以通过调用getBaseContext()
等系列方法获取并使用当前服务Service
所在的上下文环境了。
(调用onCreate())服务创建
在创建服务Service
并加载运行环境之后,系统会优先调用该方法,表示当前服务已经完成创建。可以重写该方法执行一些服务内部使用的资源初始化操作。在执行完该方法之后,就标志着当前服务Service
创建成功了,之后会一直处于运行状态,同时根据上述启动方式的不同,调用不同的生命周期方法。
(调用onStartCommand(Intent intent, int flags, int startId))服务启动
如果是通过上述启动方式一启动的服务Service
,每调用一次startService()
,系统都会调用一次该方法。
其中参数 intent 接收每次启动服务所传入的Intent
意图。
参数 flags 标记当前服务多次启动状态,一般默认是 0 ;当该服务Service
被首次调用该方法且成功返回Service.START_REDELIVER_INTENT=3
后,莫名被系统杀死,之后再次启动该服务时,标记参数则是 Service.START_FLAG_REDELIVERY=1 ;如果该服务Service
被首次调用该方法并未返回结果,系统将会再次尝试调用该方法,标记参数则为 Service.START_FLAG_RETRY=2 。
参数 startId 作为系统唯一值,以此标记当前服务Service
。
最终返回指定的int
类型,如果返回默认的Service.START_STICKY_COMPATIBILITY=0
,当系统杀死该服务Service
后,再次调用startService()
将不会再被系统回调该方法;另外如果返回Service.START_STICKY=1
,在系统杀死该服务Service
后,再次调用startService()
将会被系统重新创建实例化并回调该方法。
(调用onBind(Intent intent))服务绑定
如果是通过上述启动方式二启动的服务Service
,在首次调用bindService()
后,系统会调用该方法,而之后如果多次调用bindService()
,只有在当前服务Service
已经执行完onUnbind()
解绑的生命周期方法,并在解绑方法中返回 false 时,系统才会回调该方法。参数 intent 接收绑定服务所传入的Intent
意图,最终返回android.os.IBinder接口的实现类对象。返回结果可以在bindService(Intent service, ServiceConnection conn, int flags)
方法的参数二conn.onServiceConnected(ComponentName name, IBinder service)
方法中接收,也就是其中的参数二IBinder
类型的 service。
服务解绑(调用onUnbind(Intent intent))
如果是通过上述启动方式二启动的服务Service
,可以在bindService()
绑定服务Service
位置相对应的位置,调用unbindService()
解绑服务Service
。之后系统会回调该方法,同样的借助参数Intent
意图实例来指定要解绑的指令信息。
服务重绑(调用onRebind(Intent intent))
如果是通过上述启动方式二启动的服务Service
,如果当前服务Service
已经执行完解绑生命周期,并在onUnbind()
方法中返回 true 时,系统将会调用该方法以使用原有的IBinder
对象重新绑定当前服务Service
,因此该方法不需要返回值。其参数 intent 接收绑定服务所传入的Intent
意图。最终返回值boolean
类型,以标记当前服务再次被绑定时是否使用原有绑定过的IBinder
对象。
服务销毁(调用onDestroy())
启动之后的服务Service
会一直处于运行状态,直到系统可能因能耗过过而杀死低优先级的进程时,当前服务Service
将会被动杀死,或者当前服务Service
主动调用代码停止运行。与上述两种启动方式对应,服务Service
也有两种停止方式。
其一对应于startService()
启动的服务,调用上下文环境Context
对象的stopService(Intent service)
方法停止运行,或者调用当前服务Service
对象的stopSelf()
方法也可以停止运行自身服务。
其二对应于bindService()
绑定的服务,调用上下文环境Context
对象的unbindService(ServiceConnection conn)
方法解除绑定。由于绑定该服务的可以有多个ServiceCOnnection
连接,所以必须每个bindService()
绑定服务的位置都对应调用unbindService()
方法主动解绑,以防止出现内存泄漏或资源占用等误操作。
在服务Service
所有绑定已解绑或主动停止运行后,系统最终会调用该方法,之后将销毁内存中创建的该服务Service
实例化对象。因此可以重写该方法,对应于onCreate()
中申请的初始化资源在该方法中释放掉。
服务Service
的生命周期与界面Activity
有些类似,这也保证了在其中可以执行无用户交互的操作,那么针对这种应用场景还需要怎么构建子线程操作呢?而且服务Service
的设计还可以进行进程间通信。具体又是如何编写代码搭建进程间的沟通桥梁呢?敬请期待后续文章。