Android Service 知识体系

生命周期

startService和stopService方式

使用startService启动:

startService(Intent(this@MainActivity, MyService::class.java))

使用stopService停止:

stopService(Intent(this@MainActivity, MyService::class.java))

生命周期如下

Created with Raphaël 2.2.0 onCreate onStartCommand onDestroy

启动时的生命周期如下:

Created with Raphaël 2.2.0 onCreate onStartCommand

Service启动后多次调用startService,只会执行onStartCommand,不会再次执行onCreate。生命周期如下:

Created with Raphaël 2.2.0 onCreate onStartCommand onStartCommand onStartCommand ...

bindService和unbindService方式

使用bindService启动,unbindService停止:

class MainActivity : AppCompatActivity() {
    private val connection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            Log.d("~~~",Thread.currentThread().stackTrace[2].methodName)
        }

        override fun onServiceDisconnected(name: ComponentName?) {
            Log.d("~~~",Thread.currentThread().stackTrace[2].methodName)
        }
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        btnBindService.setOnClickListener {
            bindService(Intent(this@MainActivity, MyService::class.java), connection, BIND_AUTO_CREATE)
        }
        btnUnbindService.setOnClickListener {
            unbindService(connection)
        }
    }
}

生命周期如下:

Created with Raphaël 2.2.0 onCreate onBind onServiceConnected onUnbind onDestroy

Service启动后多次执行bindService,不会执行任何回调。

如果一个Service既被startService启动了,又被bindService绑定了,那么单独调用stopService或unbindService都不会使Service销毁,直到stopService和unbindService都被调用后Service才会被销毁。
也就是说,点击Stop Service按钮只会让Service停止,点击Unbind Service按钮只会让Service和Activity解除关联,一个Service必须要在既没有和任何Activity关联又处于停止状态的时候才会被销毁。

onRebind回调

Service中还有个onRebind回调,如果Service未被销毁,并被同一个Activity多次绑定,并且onUnbind方法中返回的是true,则会执行onRebind回调。
例如:在MyService中编写以下代码:

class MyService : Service() {
    val myBinder = MyBinder()

    override fun onCreate() {
        super.onCreate()
        Log.d("~~~", Thread.currentThread().stackTrace[2].methodName)
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.d("~~~", Thread.currentThread().stackTrace[2].methodName)
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onBind(intent: Intent?): IBinder? {
        Log.d("~~~", Thread.currentThread().stackTrace[2].methodName)
        return myBinder
    }

    override fun onUnbind(intent: Intent?): Boolean {
        Log.d("~~~",Thread.currentThread().stackTrace[2].methodName)
        return true
    }

    override fun onRebind(intent: Intent?) {
        super.onRebind(intent)
        Log.d("~~~",Thread.currentThread().stackTrace[2].methodName)
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d("~~~", Thread.currentThread().stackTrace[2].methodName)
    }

    inner class MyBinder : Binder() {

        fun startTask() {
            Log.d("~~~", Thread.currentThread().stackTrace[2].methodName)
        }
    }
}

在MainActivity中编写如下代码:

class MainActivity : AppCompatActivity() {
    private lateinit var myBinder:MyService.MyBinder
    private val connection = object : ServiceConnection {

        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            Log.d("~~~",Thread.currentThread().stackTrace[2].methodName)
            myBinder = service as MyService.MyBinder
            myBinder.startTask()
        }

        override fun onServiceDisconnected(name: ComponentName?) {
        }
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initView()
    }

    private fun initView() {
        btnStartService.setOnClickListener {
            startService(Intent(this@MainActivity, MyService::class.java))
        }
        btnStopService.setOnClickListener {
            stopService(Intent(this@MainActivity, MyService::class.java))
        }
        btnBindService.setOnClickListener {
            bindService(Intent(this@MainActivity, MyService::class.java), connection, BIND_AUTO_CREATE)
        }
        btnUnbindService.setOnClickListener {
            unbindService(connection)
        }
    }
}

运行程序,查看Log控制台。
点击startService:

~~~: onCreate
~~~: onStartCommand

点击bindService:

~~~: onBind
~~~: onServiceConnected
~~~: startTask

点击unbindService:

~~~: onUnbind

再点击bindService:

~~~: onServiceConnected
~~~: startTask
~~~: onRebind

再点击unbindService:

~~~: onUnbind

点击stopService:

~~~: onDestroy

Service 和 Thread

Service作为四大组件之一,本身和Thread是没有任何关系的。Service就像是一个没有界面的Activity,默认在主线程中运行,如果要在Service中执行耗时任务的话,需要在Service中开一个子线程。例如:

class MyService : Service() {
    private val myBinder = MyBinder()
    private val timerTask by lazy {
        object : TimerTask() {
            override fun run() {
                Log.d("~~~", "${System.currentTimeMillis()}")
            }
        }
    }

    override fun onBind(intent: Intent?): IBinder? {
        return myBinder
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // 执行耗时任务,这里开启了一个定时器模拟耗时任务
        Thread(Runnable {
            Timer().schedule(timerTask, 0, 1000)
        }).start()
        return super.onStartCommand(intent, flags, startId)
    }

    inner class MyBinder : Binder() {
        fun startTask() {
            // 执行耗时任务,这里开启了一个定时器模拟耗时任务
            Thread(Runnable {
                Timer().schedule(timerTask, 0, 1000)
            }).start()
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        timerTask.cancel()
    }
}

在Service中开启子线程的好处是Service可以全局共享,多个
Activity绑定Service时,获取到的是同一个binder实例。

前台 Service

由于Service默默地在后台运行,优先级比较低,在系统内存不足时,系统会根据进程优先级从低到高来杀进程。如果想要Service不被系统轻易杀死,可以使用前台Service。
前台Service最明显的区别就在于他会在通知栏显示当前Service正在运行,优先级较高。来看一张效果图:

使用前台Service需要添加权限:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

Android8.0之后,通知栏需要添加channelId,使用前台Service示例如下:

class MyService : Service() {
    private val myBinder = MyBinder()

    override fun onBind(intent: Intent?): IBinder? {
        return myBinder
    }

    override fun onCreate() {
        super.onCreate()
        val notificationIntent = Intent(this, MainActivity::class.java)
        val pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0)
        val channelId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            createNotificationChannel("my_service", "My Background Service")
        } else {
            // If earlier version channel ID is not used
            ""
        }
        val builder = NotificationCompat.Builder(this, channelId)
        val notification = builder.setOngoing(true)
            .setContentTitle("content title")
            .setContentText("content text")
            .setContentIntent(pendingIntent)
            .setSmallIcon(R.mipmap.ic_launcher)
            .setPriority(PRIORITY_MIN)
            .build()
        startForeground(1, notification)
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private fun createNotificationChannel(channelId: String, channelName: String): String {
        val chan = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_NONE)
        chan.lightColor = Color.BLUE
        chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
        val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        service.createNotificationChannel(chan)
        return channelId
    }

    inner class MyBinder : Binder() {
        fun startTask() {
        }
    }
}

pendingIntent用于设置通知栏点击事件,channelID是Android8.0之后需要设置的参数,使用前要先调用NotificationManager的createNotificationChannel创建渠道。builder中可以设置通知栏的标题、内容、图标等属性。

参考文章

Android Service完全解析,关于服务你所需知道的一切(上)

原创文章 67 获赞 68 访问量 6万+

猜你喜欢

转载自blog.csdn.net/AlpinistWang/article/details/90777176