一、基础Service
onCreate()方法是在Service第一次创建的时候调用的,而onStartCommand()方法则 在每次启动Service的时候都会调用。
第一次点击“Start Service”按钮, Service此时还未创建过,所以两个方法都会执行,之后如果你再连续多点击几次“Start Service”按钮,你就会发现只有onStartCommand()方法可以得到执行了。
class MyService : Service() {
...
override fun onCreate() {
super.onCreate()
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
return super.onStartCommand(intent, flags, startId)
}
override fun onDestroy() {
super.onDestroy()
}
}
二、启动、停止
构建了一个Intent对象,并调用 startService()方法来启动MyService。
在“Stop Service”按钮的点击事件里,我们同样构 建了一个Intent对象,并调用stopService()方法来停止MyService。
startService()和 stopService()方法都是定义在Context类中的,所以我们在Activity里可以直接调用这两个 方法。
另外,Service也可以自我停止运行,只需要在Service内部调用stopSelf()方法即 可。
class MainActivity : AppCompatActivity() {
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
}
}
}
三、Activity和Service进行通信
我们新建了一个DownloadBinder类,并让它继承自Binder,然后在它的内 部提供了开始下载以及查看下载进度的方法。当然这只是两个模拟方法,并没有实现真正的功 能,我们在这两个方法中分别打印了一行日志。 接着,在MyService中创建了DownloadBinder的实例,然后在onBind()方法里返回了这个 实例,这样MyService中的工作就全部完成了。
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
}
...
}
两个按钮分别是用于绑定和取消绑定Service的,那到底谁需要和Service绑定呢?当然就是 Activity了。当一个Activity和Service绑定了之后,就可以调用该Service里的Binder提供的 方法了。修改MainActivity中的代码
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) {
}
}
override fun onCreate(savedInstanceState: Bundle?) {
...
bindServiceBtn.setOnClickListener {
val intent = Intent(this, MyService::class.java)
bindService(intent, connection, Context.BIND_AUTO_CREATE) // 绑定Service
}
unbindServiceBtn.setOnClickListener {
unbindService(connection) // 解绑Service
}
}
}
我们首先创建了一个ServiceConnection的匿名类实现,并在里面重写了 onServiceConnected()方法和onServiceDisconnected()方法。
onServiceConnected()方法方法会在Activity与Service成功绑定的时候调用,而 onServiceDisconnected()方法只有在Service的创建进程崩溃或者被杀掉的时候才会调 用,这个方法不太常用。那么在onServiceConnected()方法中,我们又通过向下转型得到 了DownloadBinder的实例,有了这个实例,Activity和Service之间的关系就变得非常紧密 了。现在我们可以在Activity中根据具体的场景来调用DownloadBinder中的任何public方 法,即实现了指挥Service干什么Service就去干什么的功能。
这里仍然只是做了个简单的测 试,在onServiceConnected()方法中调用了DownloadBinder的startDownload()和 getProgress()方法。 当然,现在Activity和Service其实还没进行绑定呢,这个功能是在“Bind Service”按钮的点击 事件里完成的。可以看到,这里我们仍然构建了一个Intent对象,然后调用bindService()方 法将MainActivity和MyService进行绑定。bindService()方法接收3个参数,第一个参数就 是刚刚构建出的Intent对象,第二个参数是前面创建出的ServiceConnection的实例,第三 个参数则是一个标志位,这里传入BIND_AUTO_CREATE表示在Activity和Service进行绑定后 自动创建Service。这会使得MyService中的onCreate()方法得到执行,但 onStartCommand()方法不会执行。
四、前台Service
从Android 8.0系统开始,只有当应用保持在前台可见状态的情况下,Service 才能保证稳定运行,一旦应用进入后台之后,Service随时都有可能被系统回收。而如果你希望 Service能够一直保持运行状态,就可以考虑使用前台Service。前台Service和普通Service最 大的区别就在于,它一直会有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看 到更加详细的信息,非常类似于通知的效果
class MyService : Service() {
...
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)
}
...
}
//权限
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.servicetest">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
...
</manifest>
五、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")
}
}
这里首先要求必须先调用父类的构造函数,并传入一个字符串,这个字符串可以随意指定,只 在调试的时候有用。然后要在子类中实现onHandleIntent()这个抽象方法,这个方法中可以 处理一些耗时的逻辑,而不用担心ANR的问题
启动
class MainActivity : AppCompatActivity() {
...
override fun onCreate(savedInstanceState: Bundle?) {
...
startIntentServiceBtn.setOnClickListener {
// 打印主线程的id
Log.d("MainActivity", "Thread id is ${Thread.currentThread().name}")
val intent = Intent(this, MyIntentService::class.java)
startService(intent)
}
}
}
注册
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.servicetest">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
...
<service
android:name=".MyIntentService"
android:enabled="true"
android:exported="true"/>
</application>
</manifest>