Service 基础
Service 的分类:
按运行地点分类:本地服务和远程服务;
按运行类型分类:前台服务和后台服务;
按功能分类:可通信服务和不可通信服务
特点:
按运行地点分类:
类别 | 特点 | 优点 | 缺点 | 应用场景 |
---|---|---|---|---|
本地服务(LocalService) | 1,运行在主线程 2, 主线程被终止后,服务也会被终止。 |
1, 节约资源 2, 通信方便:由于在同一进程中因此不需要IPC1和AIDL。 |
限制性大:主进程被终止后,服务也会终止。 | (最常用)需要依附某个进程的服务,例如音乐播放。 |
远程服务(RemoteService) | 1, 运行在独立的进程 2, 服务常驻在后台,不受其他Activity的影响。 |
灵活,服务常驻后台,不受其他Activity 的影响。 | 1,消耗资源:单独进程 2,使用AIDL 进行IPC1复杂。 |
系统级别服务 |
按运行类型分类:
类别 | 特点 | 应用场景 |
---|---|---|
前台服务 | 在通知栏显示通知(用户可以看到) | 服务使用时需要让用户知道并进行相关操作,音乐播放并在状通知栏显示播放的音乐信息,并能进行一些操作。 |
后台服务 | 处于后台的服务(用户无法看到) | 服务使用时不需要让用户知道并进行相关操作,如天气刷新,日期同步等。 |
按功能分类:
类别 | 特点 | 应用场景 |
---|---|---|
不可通信的服务 | 1,用startService() 启动2,调用者退出后service 仍谈存在。 |
该后台服务不进行任何通信。 |
可通信的服务 | 1,用bindService() 启动2,调用者退出后,随着调用者一起被销毁。 |
该后台需要进行通信。 |
本地Service
具体使用步骤:
-
步骤1:新建子类继承Service 类
需要重写父类的
onCreate()
、onStartCommand()
、onDestory()
和onBind()
方法 -
步骤2:构建用于启动Service 的Intent 对象
-
步骤3:调用
startService()
启动Service、调用stopService()
停止服务 -
步骤4:在
AndroidManifest.xml
中注册Service
AndroidManifext.xml 中Service 的常见属性说明
属性 | 说明 | 备注 |
---|---|---|
android:name | Service 的类名 | - |
android:label | Service 的名字 | 如不设置,默认为Service 的类名 |
android:icon | Service 的图标 | - |
android:permission | 申明此Service 的权限 | 有提供了该权限的应用才能控制或者链接此服务 |
android:process | 表明该服务是否在另一个进程中运行(远程服务) | 不设置默认为本地服务; remote 则设置成远程服务 |
android:enabled | 系统默认启动 | true: Service 将会默认被系统启动;不设置默认为false |
android:exported | 该服务是否能够被其他应用程序所控制或连接 | 不设置默认此项为false |
Service 的生命周期
Service 的5个生命周期方法:
onCreate()
、 onStartCommand()
、 onBind()
、onUnbind()
、 onDestory()
只使用startService启动服务的生命周期
startService启动服务的生命周期
只使用BindService绑定服务的生命周期
BindService绑定服务的生命周期
同时使用startService()启动服务、BindService()绑定服务的生命周期
Activity 向Service通信
第一种方式:通过MyBinder 方式调用Service 方法
步骤(以购票举例):
-
继承Binder 定义中间对象
class TicketService : Service() { override fun onBind(intent: Intent?): IBinder? { return BuyTicketBinder() } public fun buyTicket(money: Int){ //50元一张票,当money 大于等于时购票成功,否则失败 if (money >= 50){ Toast.makeText(this, "出票成功!", Toast.LENGTH_SHORT).show() }else { Toast.makeText(this, "票款不足,出票失败!", Toast.LENGTH_SHORT).show() } } inner class BuyTicketBinder : Binder() { public fun callBuyTicket(money: Int){ buyTicket(money) } } }
-
重写ServiceConnection, onServiceConnected 时调用中间人对象绑定服务
class TicketActivity : AppCompatActivity() { var ticketBinder: TicketService.BuyTicketBinder ?= null var ticketConnection: TicketConnection ?= null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_ticket) var intent: Intent = Intent(this, TicketService::class.java) //建立连接服务 ticketConnection = TicketConnection() bindService(intent, ticketConnection!!, Context.BIND_AUTO_CREATE) //点击按钮开始购票 findViewById<Button>(R.id.buy_ticket).setOnClickListener { ticketBinder?.callBuyTicket(40) } } //监听服务状态 inner class TicketConnection : ServiceConnection{ //断开连接 override fun onServiceDisconnected(name: ComponentName?) { Log.i("TicketConnection", "onServiceDisconnected") } //连接成功 override fun onServiceConnected(name: ComponentName?, service: IBinder?) { Log.i("TicketConnection", "onServiceConnected") ticketBinder = service as TicketService.BuyTicketBinder } } override fun onDestroy() { //当activity 销毁的时候 解绑服务 super.onDestroy() ticketConnection?.let { unbindService(it) } } }
记得在AndroidManifest.xml
中注册TicketActivity
和TicketService
通过接口调用Service 方法
使用接口调用Service 和直接调用本质都是一样的,只不过多了接口的步骤。
步骤(依旧以上的购票为例子):
- 继承Binder 定义中间人对象
- 定义接口
interface TicketInterface {
fun callBuyTicket(money: Int) //买票
fun callRefundTicket() //退票
}
-
重写
ServiceConnection
,onServiceConnected
时调用中间人对象,强制转换为接口(ticketBinder = service as TicketService.BuyTicketBinder
)绑定服务详细代码如下:
TicketActivityByInterface.kt:
class TicketActivityByInterface : AppCompatActivity() { var ticketBinder: TicketServiceByInterface.BuyTicketBinder ?= null var ticketConnection: TicketConnectionByInterface ?= null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_ticket) var intent: Intent = Intent(this, TicketServiceByInterface::class.java) //建立连接服务 ticketConnection = TicketConnectionByInterface() bindService(intent, ticketConnection!!, Context.BIND_AUTO_CREATE) //点击按钮开始购票 findViewById<Button>(R.id.buy_ticket).setOnClickListener { ticketBinder?.callBuyTicket(40) } } //监听服务状态 inner class TicketConnectionByInterface : ServiceConnection{ //断开连接 override fun onServiceDisconnected(name: ComponentName?) { Log.i("TicketConnection", "onServiceDisconnected") } //连接成功 override fun onServiceConnected(name: ComponentName?, service: IBinder?) { Log.i("TicketConnection", "onServiceConnected") ticketBinder = service as TicketServiceByInterface.BuyTicketBinder } } override fun onDestroy() { //当activity 销毁的时候 解绑服务 super.onDestroy() ticketConnection?.let { unbindService(it) } } }
TicketServiceByInterface.kt:
class TicketServiceByInterface : Service() { override fun onBind(intent: Intent?): IBinder? { return BuyTicketBinder() } public fun buyTicket(money: Int){ //50元一张票,当money 大于等于时购票成功,否则失败 if (money >= 50){ Toast.makeText(this, "出票成功!", Toast.LENGTH_SHORT).show() }else { Toast.makeText(this, "票款不足,出票失败!", Toast.LENGTH_SHORT).show() } } inner class BuyTicketBinder : Binder(), TicketInterface { override fun callBuyTicket(money: Int) { buyTicket(money) } override fun callRefundTicket() { //TODO 退票方法就不做实现了 } } }
Service 和Activity 通信总结:
方法一:
-
新建服务继承自Service,并添加一个内部类继承自Binder
- 覆写
onBind
方法,返回步骤一中的Binder 对象 - 调用的Activity 中添加一个内部类继承
ServiceConnection
, 在onServiceConnected
中获取到Binder 对象并调用逻辑方法绑定服务
方法二:
- 实现一个接口,并在Service 类的Binder 对象中继承该接口并实现相应方法,其他用方法一相同
源码中Binder 机制
该问题主要时说一下binder 干什么的,Service Manager是是如何成为一个守护进程的
Binder 是干什么的
Binder: 是一种进程间通信(IPC)机制,基于OpenBinder
- Client、Server 和Service Manager 实现在用户空间中,Binder 驱动程序实现在内核空间中
- Binder 驱动程序和Service Manager 在Android 平台中已经实现,开发者只需要在用户空间实现自己的Client 和Sercer
- Binder 驱动程序提供设备文件/dev/binder 与用户空间交互,Client、Server 和Service Manager 通过open 和ioctl 文件操作函数与Binder 驱动程序进行通信
- Client和Server之间的进程间通信通过Binder驱动程序间接实现
- Service Manager是一个守护进程,用来管理Server,并向Client提供查询Server接口的能力
未完待续。。。