android之Service

2021-12-10  本文已影响0人  0246eafe46bd

Service简介

Service是Android中实现程序后台运行的解决方案,适合执行不需要和用户交互而且还要求长期运行的任务,不依赖于任何用户界面,即使程序被切换到后台,仍然能够保持正常运行

Service并不是运行在一个独立的进程当中的,而是依赖于创建Service时所在的应用程序进程。当某个应用程序进程被杀掉时,所有依赖于该进程的Service也会停止运行

Service不会自动开启线程,所有的代码都是默认运行在主线程当中的。我们需要在Service的内部手动创建子线程,并在这里执行具体的任务,否则就有可能出现主线程被阻塞的情况

Service简单用法

创建一个service

class MyService : Service() {
    companion object {
        private const val TAG = "MyService"
    }

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

    // 第一次创建的时候调用
    override fun onCreate() {
        Log.d(TAG, "onCreate: ")
        super.onCreate()
    }

    // 每次启动的时候调用
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.d(TAG, "onStartCommand: ")
        return super.onStartCommand(intent, flags, startId)
    }

    // 销毁的时候调用
    override fun onDestroy() {
        Log.d(TAG, "onDestroy: ")
        super.onDestroy()
    }
}

启动和停止Service

class ServiceActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_service)

        //启动Service
        start_service.setOnClickListener {
            val intent = Intent(this, MyService::class.java)
            startService(intent)
        }
        //停止Service,Service也可以自我停止运行,只需要在Service内部调用stopSelf()方法即可
        stop_service.setOnClickListener {
            val intent = Intent(this, MyService::class.java)
            stopService(intent)
        }
    }
}

从Android 8.0系统开始,应用的后台功能被大幅削减。现在只有当应用保持在前台可见状态的情况下,Service才能保证稳定运行,一旦应用进入后台之后,Service随时都有可能被系统回收。如果你真的需要长期在后台执行一些任务,可以使用前台Service或者WorkManager

Activity和Service进行通信

例如:在MyService里提供一个下载功能,然后在Activity中可以决定何时开始下载,以及随时查看下载进度

使用 Binder

解决方法是创建一个专门的Binder对象来对下载功能进行管理

class MyService : Service() {
    companion object {
        private const val TAG = "MyService"
    }

    private val mBinder = DownloadBinder()

    inner class DownloadBinder : Binder() {
        fun startDownload() {
            Log.d(TAG, "startDownload: ")
        }

        fun getProgress(): Int {
            Log.d(TAG, "getProgress: ")
            return 0
        }
    }

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

绑定和解绑Service

class ServiceActivity : AppCompatActivity() {
    companion object {
        private const val TAG = "ServiceActivity"
    }

    private val connection = object : ServiceConnection {
        // 会在Activity与Service成功绑定的时候调用
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            Log.d(TAG, "onServiceConnected: ")
            val downloadBinder = service as MyService.DownloadBinder
            downloadBinder.startDownload()
            downloadBinder.getProgress()
        }

        //只有在Service的创建进程崩溃或者被杀掉的时候才会调用
        override fun onServiceDisconnected(name: ComponentName?) {
            Log.d(TAG, "onServiceDisconnected: ")
        }

    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_service)

        // 绑定Service
        bind_service.setOnClickListener {
            val intent = Intent(this, MyService::class.java)
            bindService(intent, connection, Context.BIND_AUTO_CREATE)
        }
        // 解绑Service
        unbind_service.setOnClickListener {
            unbindService(connection)
        }
    }
}

bindService()方法接收3个参数,第一个参数就是刚刚构建出的Intent对象,第二个参数是前面创建出的ServiceConnection的实例,第三个参数则是一个标志位,这里传入BIND_AUTO_CREATE表示在Activity和Service进行绑定后自动创建Service。这会使得MyService中的onCreate()方法得到执行,但onStartCommand()方法不会执行

Service的生命周期

896629-20171003150906568-1811048252.png

如果对一个Service既调用了startService()方法,又调用了bindService()方法的,要同时调用stopService()和unbindService()方法,onDestroy()方法才会执行

前台Service

只有当应用保持在前台可见状态的情况下,Service才能保证稳定运行,一旦应用进入后台之后,Service随时都有可能被系统回收。如果希望Service能够一直保持运行状态,就可以考虑使用前台Service。前台Service和普通Service最大的区别就在于,它一直会有一个正在运行的图标在系统的状态栏显示

前台Service示例

class MyService : Service() {
    companion object {
        private const val TAG = "MyService"
    }

    // 第一次创建的时候调用
    override fun onCreate() {
        Log.d(TAG, "onCreate: ")
        super.onCreate()

        val notificationManager =
            getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        // NotificationChannel类和createNotificationChannel()方法都是Android 8.0系统中新增的API
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            // 创建通知渠道
            val notificationChannel =
                NotificationChannel(
                    channelId,
                    "前台Service通知",
                    NotificationManager.IMPORTANCE_DEFAULT
                )
            notificationManager.createNotificationChannel(notificationChannel)
        }

        val notification = getNotification()
        startForeground(1, notification)
    }

    private fun getNotification(): Notification {
        val intent = Intent(this, ServiceActivity::class.java)
        val pendingIntent = PendingIntent.getActivity(this, 0, intent, 0)

        //第一个参数是context,第二个参数是渠道ID,需要和我们在创建通知渠道时指定的渠道ID相匹配
        return NotificationCompat.Builder(applicationContext, channelId).apply {
            // 点击跳转后,取消通知的显示
            setAutoCancel(true)
            // 设置 PendingIntent,点击通知会执行 PendingIntent 里面的 Intent 的意图
            setContentIntent(pendingIntent)
            // 配置通知的标题、正文内容、图标
            setContentTitle("this is content title")
            setStyle(
                NotificationCompat.BigPictureStyle()
                    .bigPicture(BitmapFactory.decodeResource(resources, R.drawable.big_image))
            )
            setStyle(
                NotificationCompat.BigTextStyle()
                    .bigText("this is content text this is content text ")
            )
            // 小图标会显示在系统状态栏上,只能使用纯alpha图层的图片(.png)进行设置,否则只是一块灰色区域
            setSmallIcon(R.drawable.small_icon)
            // 当下拉系统状态栏时,就可以看到设置的大图标
            setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.big_image))
        }.build()
    }
......
}

调用startForeground()方法后就会让MyService变成一个前台Service,并在系统状态栏显示出来,例如按 HOME 键将应用转到后台,MyService会继续运行,但如果按返回键退出应用,就会停止,因为整个应用已经退出了。从Android 9.0系统开始,使用前台Service必须在AndroidManifest.xml文件中进行权限声明

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

IntentService

IntentService 是一个异步的、会自动停止的Service

class MyIntentService : IntentService("MyIntentService") {
    companion object {
        private const val TAG = "MyIntentService"
    }

    override fun onHandleIntent(intent: Intent?) {
        Log.d(TAG, "onHandleIntent: Thread is ${Thread.currentThread().name}")
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d(TAG, "onDestroy: ")
    }
}
// 启动 IntentService
start_intentservice.setOnClickListener {
    val intent = Intent(this, MyIntentService::class.java)
    startService(intent)
}

onHandleIntent()这个抽象方法,这个方法已经在子线程中运行,这个Service在运行结束后是会自动停止的,也是因为这点,在 onHandleIntent 方法中不要再开线程,否则这个线程可能无法正常执行,因为 onHandleIntent 方法结束后 Service 就结束了

上一篇 下一篇

猜你喜欢

热点阅读