如题,本文将简述,同一个进程内,service如何创建,绑定等操作。
首先,service是什么?
官方描述:
A Service is an application component representing either an application's desire to perform a longer-running operation while not interacting with the user or to supply functionality for other applications to use. Each service class must have a corresponding[<service>](https://developer.android.google.cn/reference/android/R.styleable#AndroidManifestService)
declaration in its package'sAndroidManifest.xml
. Services can be started with[Context.startService()](https://developer.android.google.cn/reference/android/content/Context#startService(android.content.Intent))
and[Context.bindService()](https://developer.android.google.cn/reference/android/content/Context#bindService(android.content.Intent,%20android.content.ServiceConnection,%20int))
.
通俗一点,service可以理解为一个摸不着的东西,他区别于界面,运行在后台。
service的主要使用方法
1、startService()--用于启动service
2、stopService()--用于停止service
3、bindService()--绑定service
4、unBindService()--解绑service
要点:
(1)startService()和stopService(),bindService()和unBindService()都是成对出现的。
(2)首次使用startService(),service的onCreate()方法会执行,然后onStartCommand(),若无取消service()操作,后续再多次调用startService()方法时,service只会多次执行onStartCommand()方法。
(3)bindService()用于绑定service,结合ServiceConnection方法,实现绑定并且可以初始化一个绑定后的对象,用于调用binder方法。
(4)如果先调用bindService(),则service的生命周期方法执行顺序为:onCreate()-->onBind()。
(5)如果调用了startService()和bindService()方法,则后续需要同时调用unBindService()和stopService()用于取消绑定和停止service。
(6)注意在service不再使用的时候,及时释放service,避免导致内存泄漏
(7)注意安卓不同版本之间,service的适配
(8)使用service时,注意权限的申请
案例一
使用startService()和stopService()
代码地址
本次案例,主要调用startService()和stopService()两个方法,用于启动和取消service。
首先,看看service的实现:
//
class FirstService( override val channelName: String =javaClass::class.java.simpleName,override val getChannelId: Int =javaClass.hashCode()) : BaseServiceCompat() { override fun onCreate() {
super.onCreate()
KtLogUtil.d("service onCreate")
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
KtLogUtil.d("service onStartCommand")
return super.onStartCommand(intent, flags, startId)
}
override fun onDestroy() {
KtLogUtil.d("service onDestroy")
super.onDestroy()
}
override fun onBind(intent: Intent): IBinder? {
KtLogUtil.d("service onBind")
return null
}
}
//
该service继承自BaseServiceCompat,父类主要是适配了不同安卓版本启动service的方法,主要核心代码,就是若当前版本大于android.O,启动service的方法为startForegroundService(),并且在创建service的时候,调用startForeground发出一个前台通知。具体实现代码如下:
fun startService(context: Context, intent: Intent?) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//适配安卓8.0
context.startForegroundService(intent)
} else {
context.startService(intent)
}
}
override fun onCreate() {
super.onCreate()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
try {
//适配安卓8.0
val channelId = getChannelId
val channelName = channelName
val channel = NotificationChannel(
channelId.toString(),
channelName,
NotificationManager.IMPORTANCE_MIN
)
val manager = applicationContext.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannel(channel)
startForeground(channelId, notification)
} catch (e: Exception) {
}
}
}
而在MainActivity里面,我们直接调用即可:
代码如下图:
private fun initEvent() {
findViewById<Button>(R.id.start_service).setOnClickListener {
//显式开启
val serviceIntent = Intent(this, FirstService::class.java)
// //隐式开启--区别于显式, 需要在manifest中声明相关action参数
// val serviceIntent = Intent()
// serviceIntent.setAction("com.north.light.androidservice.FirstService")
// serviceIntent.setPackage("com.north.light.androidservice")
//启动service--android 8.0 service的启动方式有改变,需要适配
//8.0 ps:在系统创建服务后,应用有五秒的时间来调用该服务的 startForeground() 方法以显示新服务的用户可见通知。
//使用封装好的base service compat用于启动service
BaseServiceCompat.startService(this, serviceIntent)
}
findViewById<Button>(R.id.stop_service).setOnClickListener {
val serviceIntent = Intent(this, FirstService::class.java)
//关闭service
stopService(serviceIntent)
}
}
可以从上图看出,启动service,有两种方法:
第一个就是直接通过Service的对象作为intent初始化传参进行启动(显式调用)
第二个就是指定intent的action和package进行调用(隐式调用)
对于方法二:需要在manifest文件中,声明action
<service
android:name=".FirstService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.north.light.androidservice.FirstService" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
声明以后才可以进行隐式调用。
对于service的参数声明析意如下:
enable:表示是否启用该服务
exported:表示是否允许除了当前程序之外的其他程序访问这个服务
process:是否需要在单独的进程中运行,当设置为android:process=”:remote”时,代表Service在单独的进程中运行。
ps:很重要,它的意思是指要在当前进程名称前面附加上当前的包名,
所以“remote”和”:remote”不是同一个意思,前者的进程名称为:remote,而后者的进程名称为:App-packageName:remote。
对于intent-filter参数,常用于外部调用(隐式调用的情况下声明)
至此,已经完成了service的startService()和stopService()的实现了
案例二
在案例一的基础上,进行bindservice操作,并且通过binder调用service的方法
代码链接
实际项目中,建议调用的service方法顺序为:
1、startService()
2、bindService()
3、unBindService()
4、stopService()
本次案例调用service的顺序也是如上。
首先,service里面定义一个binder,如下图:
inner class FirstBinder : Binder() {
fun getService(): FirstService {
return this@FirstService
}
/**
* 发送消息
* */
fun sendMessage(toString: String) {
mListener.forEach {
it.result("收到了,返回给你:${toString}")
}
}
fun setOnListener(listener: FirstServiceListener) {
mListener.add(listener)
}
fun removeListener(listener: FirstServiceListener) {
mListener.remove(listener)
}
}
再在service的onBind()方法中,返回该binder的实例,至此,service的定义基本完成。
下面看看如何实现bindService()的。
findViewById<Button>(R.id.bind_service).setOnClickListener {
val serviceIntent = Intent(this, FirstService::class.java)
//绑定service
serConnect = object : ServiceConnection {
override fun onServiceConnected(p0: ComponentName?, p1: IBinder?) {
KtLogUtil.d("绑定了 service ")
mBinder = (p1 as FirstService.FirstBinder)
mBinder?.setOnListener(mBinderListener)
}
override fun onServiceDisconnected(p0: ComponentName?) {
mBinder = null
KtLogUtil.d("解绑了 service ")
}
}
bindService(serviceIntent, serConnect!!, BIND_AUTO_CREATE)
}
这里直接通过一个ServiceConnect即可实现bind service。里面有连接成功和断开连接的返回,这里只做案例实现,实际中需要在断开连接的时候,加上重连机制。
在onServiceConnected()方法中,能初始化binder,该binder则可以用作后续调用service方法的操作了。
that's all--------------------------------------------------------------------