目录
创建一个Service
Activity与Service之间的通信
绑定Activity与Service
使用前台Service
使用IntentService
创建一个Service
每一个Service都是需要在ActivityManifest.xml中进行注册的,一般都会自动注册好。
注册service:
<service
android:name=".MyService"
android:enabled="true"
android:exported="true">
</service>
创建service:
class MyService : Service() {
override fun onBind(intent: Intent): IBinder {
}
override fun onCreate() {
super.onCreate()
Log.d("MyService", "onCreate executed")
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
Log.d("MyService", "onStartCommand executed")
return super.onStartCommand(intent, flags, startId)
}
override fun onDestroy() {
super.onDestroy()
Log.d("MyService", "onDestroy executed")
}
}
onStartCommand中,不要写在onCreate中,onCreate只会在第一次创建service的时候调用。 onDestroy()则负责在service结束时进行资源的释放操作。这样一个service就创建好了,下一步就是启用和停止service了,首先在activity_main.xml中定义两个按钮,用于启动和停止service。然后在activity中设置监听事件:
startServiceBtn.setOnClickListener {
val intent = Intent(this, MyService::class.java)
startService(intent) // 启动Service
}
stopServiceBtn.setOnClickListener {
val intent = Intent(this, MyService::class.java)
stopService(intent) // 停止Service
}
这样就完成了service的基本使用。
Activity与Service之间的通信
绑定Activity与Service
要实现 Activity与Service之间的通信还要借助onBind方法,onBind方法增强service与activity之间的联系,是service与activity之间通信的方式,使activity可以决定什么时候时候使用service和查看service的进度。这里定义了一个继承自Binder的DownloadBinder类,然后在类内部定义了两个方法用于模拟进行下载操作(实际上这两个方法都没有具体实现)。最后用onBind方法向Activity传递该类的实例mBinder。
class MyService : Service() {
private val mBinder = DownloadBinder()
class DownloadBinder : Binder() {
fun startDownload() {
Log.d("MyService", "startDownload executed")
}
fun getProgress(): Int {
Log.d("MyService", "getProgress executed")
return 0
}
}
override fun onBind(intent: Intent): IBinder {
return mBinder
}
override fun onCreate() {
super.onCreate()
Log.d("MyService", "onCreate executed")
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
Log.d("MyService", "onStartCommand executed")
return super.onStartCommand(intent, flags, startId)
}
override fun onDestroy() {
super.onDestroy()
Log.d("MyService", "onDestroy executed")
}
}
然后在Activity中对service进行绑定,在activity_main.xml中再定义两个按钮,用于绑定和解绑service。首先,我们实现了ServiceConnect的匿名类实现,并重写onServiceConnected方法和onServiceDisconnected方法,onServiceConnected方法在Activity和service绑定成功的时候调用,而onServiceDisconnected方法在service崩溃或者进程被杀掉的时候调用。绑定时使用了bindService方法,传入的参数为 Intent对象,ServiceConnection实例,标志位(这里Context.BIND_AUTO_CREATE表示绑定成功自动创建service,此时onCreate会执行,但onStartCommand不会执行)。注意,当service解绑之后,service中的onDestroy方法就会执行,而要是之前执行过startService方法,那就需要同时调用stopService和 unbindService方法才能销毁service。
class MainActivity : AppCompatActivity() {
lateinit var downloadBinder: MyService.DownloadBinder
private val connection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
downloadBinder = service as MyService.DownloadBinder
downloadBinder.startDownload()
downloadBinder.getProgress()
}
override fun onServiceDisconnected(name: ComponentName) {
Log.d("MyService", "onServiceDisconnected")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
startServiceBtn.setOnClickListener {
val intent = Intent(this, MyService::class.java)
startService(intent) // 启动Service
}
stopServiceBtn.setOnClickListener {
val intent = Intent(this, MyService::class.java)
stopService(intent) // 停止Service
}
bindServiceBtn.setOnClickListener {
val intent = Intent(this, MyService::class.java)
bindService(intent, connection, Context.BIND_AUTO_CREATE) // 绑定Service
}
unbindServiceBtn.setOnClickListener {
unbindService(connection) // 解绑Service
}
}
}
使用前台Service
后台的service并不能保证稳定运行,所以我们可以使用前台service,将service改造,如下:
class MyService : Service() {
private val mBinder = DownloadBinder()
class DownloadBinder : Binder() {
fun startDownload() {
Log.d("MyService", "startDownload executed")
}
fun getProgress(): Int {
Log.d("MyService", "getProgress executed")
return 0
}
}
override fun onBind(intent: Intent): IBinder {
return mBinder
}
override fun onCreate() {
super.onCreate()
Log.d("MyService", "onCreate executed")
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel("my_service", "前台Service通知", NotificationManager.IMPORTANCE_DEFAULT)
manager.createNotificationChannel(channel)
}
val intent = Intent(this, MainActivity::class.java)
val pi = PendingIntent.getActivity(this, 0, intent, 0)
val notification = NotificationCompat.Builder(this, "my_service")
.setContentTitle("This is content title")
.setContentText("This is content text")
.setSmallIcon(R.drawable.small_icon)
.setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.large_icon))
.setContentIntent(pi)
.build()
startForeground(1, notification)
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
Log.d("MyService", "onStartCommand executed")
thread {
// 处理具体的逻辑
stopSelf()
}
return super.onStartCommand(intent, flags, startId)
}
override fun onDestroy() {
super.onDestroy()
Log.d("MyService", "onDestroy executed")
}
}
修改了onCreate方法和 onStartCommand方法 ,从后台service转变为前台service其实就是将消息通知写入service中,因为前台service还要进行权限的声明,所以还要修改AndroidManifest.xml文件如下:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.servicetest"> <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> ... </manifest>
而修改 onStartCommand方法是因为service是在主线程中运行的,所以service中的耗时操作会导致ANR(application not responding)问题。因此,我们需要在方法里面开启子线程来处理耗时操作,使用 stopSelf()来停止service。
使用IntentService
因为service处理耗时操作时频繁开启线程过于繁琐,所以推出了IntentService。使用方法如下:
class MyIntentService : IntentService("MyIntentService") {
override fun onHandleIntent(intent: Intent?) {
// 打印当前线程的id
Log.d("MyIntentService", "Thread id is ${Thread.currentThread().name}")
}
override fun onDestroy() {
super.onDestroy()
Log.d("MyIntentService", "onDestroy executed")
}
}
别忘了在AndroidManifest.xml中注册service
<service
android:name=".MyIntentService"
android:enabled="true"
android:exported="true"/>
耗时操作在onHandleIntent中进行即可,它是在子线程中运行的,而在Activity中的具体使用如下:
这里定义一个按钮来开启IntentService
startIntentServiceBtn.setOnClickListener {
// 打印主线程的id
Log.d("MainActivity", "Thread id is ${Thread.currentThread().name}")
val intent = Intent(this, MyIntentService::class.java)
startService(intent)
}