【Android】四大组件之Service

概念

就是默默运行在后台的组件,可以理解为是没有前台的activity,适合用来运行不需要前台界面的代码
服务可以被手动关闭,不会重启,但是如果被系统关闭,内存充足就会重启


进程优先级

  1. 前台进程:拥有一个正在与用户交互的activity(onResume方法被调用)的进程
  2. 可见进程:拥有一个非前台,但是对用户可见的activity(onPause方法被调用)的进程
  3. 服务进程:拥有一个通过startService方法启动的服务的进程
  4. 后台进程:拥有一个后台activity(onStop方法被调用)的进程
  5. 空进程:没有拥有任何活动的应用组件的进程,也就是没有任何服务和activity在运行

五种前台进程

  1. activity执行了onresume方法,获得焦点
  2. 拥有一个跟正在与用户交互的activity绑定的服务
  3. 拥有一个服务执行了startForeground()方法
  4. 拥有一个正在执行onCreate()、onStart()或者onDestroy()方法中的任意一个的服务
  5. 拥有一个正在执行onReceive方法的广播接收者

两种可见进程

  1. activity执行了onPause方法,失去焦点,但是可见
  2. 拥有一个跟可见或前台activity绑定的服务

Service

创建方式

  • 定义一个类继承Service,并在清单文件中注册该service

服务的两种启动方式

startService

  • 使用Context中的startService方法将服务启动,会使进程变成为服务进程
  • 启动服务的activity和服务不再有关系
fun start(view: View){
   val intent = Intent(this, MyService::class.java)
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//8.0以上,需要启动前台服务
        startForegroundService(intent)
    } else {
        startService(intent)
    }
}
  • 8.0以上使用startForegroundService后,需要在Service中启动前台通知,否则会报错
override fun onCreate() {
    super.onCreate()
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val channel =
            NotificationChannel("my", "MyService", NotificationManager.IMPORTANCE_HIGH)
        channel.lockscreenVisibility = Notification.VISIBILITY_SECRET
        val nm = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        nm.createNotificationChannel(channel)
        val notification = Notification.Builder(this, "my")
            .setContentTitle("系统关键服务")
            .setContentText("关闭会造成死机,请谨慎")
            .setSmallIcon(R.mipmap.ic_launcher)//这个一定要设置,不然就是普通的通知
            .build()
        startForeground(1, notification)
    }
}
  • 启动前台服务,需要在清单文件中声明权限
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

通知要设置setSmallIcon方法,否则则会显示默认的通知

已设置
在这里插入图片描述
未设置
在这里插入图片描述

  • 使用Context中的stopService方法将服务停止
fun stop(view:View){
   val intent= Intent(this,MyService::class.java)
   stopService(intent)
}

bindService

activity与service建立连接,进程优先级不变,绑定的服务与它的activity同生共死,activity销毁了,服务解绑并销毁了,但是如果是服务先销毁了,并不会影响activity。所以bindService不适合做后台任务。

  • 绑定服务
fun bind(view: View) {
    val intent = Intent(this, MyService::class.java)
    bindService(intent, conn, Context.BIND_AUTO_CREATE)
}

class MyConn : ServiceConnection {
    override fun onServiceDisconnected(name: ComponentName?) {
    	//连接因为异常而终止才会调用,正常unBind是不会调用此方法的
        println("服务中断")
    }

    override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
    	//Service中的onBind返回有返回值才有调用
        println("服务连接")
    }
}
  • 解绑服务
fun unbind(view: View) {
    unbindService(conn)
}

服务的生命周期

  • 如果当前服务未启动,调用startService时,首先会调用Service中的onCreate方法,其次调用onStartCommand方法。如果一开始调用的是bindService,则首先调用Service中的onCreate方法,再调用onBind方法。
  • 多次启动服务,如果是使用startService启动的则会调用onStartCommand,如果是使用bindService启动的,只有第一次会调用onBind方法,后面多次调用bindService,则不会调用onBind方法
  • 停止服务时,stopService会调用onDestroy方法,unbindService会先调用unBind方法,再调用onDestroy方法,多次解绑会抛出异常,使用bindService方式启动时,activity销毁,服务也会调用解绑方法,并且销毁。
class MyService : Service() {
    override fun onBind(intent: Intent?): IBinder? {
        println("绑定")
        return null
    }

    override fun onUnbind(intent: Intent?): Boolean {
        println("解绑")
        return super.onUnbind(intent)
    }

    override fun onCreate() {
        super.onCreate()
        println("service onCreate")
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        println("service onStartCommand")
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onDestroy() {
        println("service onDestroy")
        super.onDestroy()
    }

}

访问Service中的方法

访问调用Service中的方法,需要使用bindService方法进行启动服务,这样创建的ServiceConnection对象就是与Service连接的对象,这时需要Service中返回IBinder对象。

实现

  • 在Service中定义一个内部类实现IBinder或者继承Binder,一般都是继承Binder的
inner class MyBinder : Binder() {

}
  • 然后定义外部要访问的方法接口,让该类实现此接口
inner class MyBinder : Binder(), IFun {
    override fun sayHello() {
    }

    override fun sayHaHa() {
    }
}
  • 实现这些方法时,根据内部类可以访问外部类的属性和方法,调用Service中的方法即可
inner class MyBinder : Binder(), IFun {
    override fun sayHello() {
        say("hello")
    }

    override fun sayHaHa() {
        say("haha")
    }
}

fun say(text: String) {
    Toast.makeText(this, text, Toast.LENGTH_SHORT).show()
}
  • 创建此对象,让Service中的onBind方法返回
class MyService : Service() {
  override fun onBind(intent: Intent?): IBinder? {
      println("绑定")
      return MyBinder()
  }

  override fun onUnbind(intent: Intent?): Boolean {
      println("解绑")
      return super.onUnbind(intent)
  }
}
  • 外部调用时,首先使用bindService方法,绑定服务,在ServiceConnection对象中的onServiceConnected的IBinder对象,就是服务返回的IBinder对象
private val conn by lazy { MyConn() }

private var iFun: IFun? = null

fun bind(view: View) {
    val intent = Intent(this, MyService::class.java)
    bindService(intent, conn, Context.BIND_AUTO_CREATE)
}

fun unbind(view: View) {
    unbindService(conn)
}

fun callService(view: View) {
    iFun?.sayHello()
}

inner class MyConn : ServiceConnection {
    override fun onServiceDisconnected(name: ComponentName?) {
    }

    override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
        iFun = service as? IFun
    }
}

服务的混合启动

即可以将Service运行在服务进程,又可以在Activity中调用service中的方法。
先start,再bind,销毁时先unbind,在stop


服务的分类

本地服务

服务和启动它的组件在同一个进程

远程服务

服务和启动它的组件不在同一个进程

实现

  • 首先定义一个提供服务的应用,在清单文件中,注册该服务,远程服务只能是隐式启动,在清单文件中配置Service标签时,必须配置intent-filter子节点,并指定action子节点
<service android:name=".AliPayService">
    <intent-filter>
        <action android:name="com.wzh.alipay"/>
    </intent-filter>
</service>
  • 服务

class AliPayService : Service() {
    override fun onBind(intent: Intent?): IBinder? {
        println("AliPayService onBind")
        return null
    }

    override fun onUnbind(intent: Intent?): Boolean {
        println("AliPayService onUnbind")
        return super.onUnbind(intent)
    }

    override fun onCreate() {
        super.onCreate()
        println("AliPayService onCreate")
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel("alipay", "支付服务", NotificationManager.IMPORTANCE_HIGH)
            val nm = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            nm.createNotificationChannel(channel)
            val notification = Notification.Builder(this, "alipay")
                .setContentTitle("正在支付10000000元")
                .setContentText("剩余金额19088900088945元")
                .setSmallIcon(R.drawable.icon_alipay)
                .setWhen(System.currentTimeMillis())
                .setAutoCancel(false)
                .build()
            startForeground(1, notification)
        }
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        println("AliPayService onStartCommand")
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onDestroy() {
        super.onDestroy()
        println("AliPayService onDestroy")
    }
}

注意:需要在清单文件中配置前台服务的权限

<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
  • 在需要启动服务的应用中,写启动服务的代码,配置所需的intent参数,5.0以上如果按以前的启动方式,则会报异常Service Intent must be explicit,服务意图必须明确,因为5.0以前启动服务必须显示声明,解决的方法是,将隐式意图转为显式意图
fun createExplicitFromImplicitIntent(context: Context, implicitIntent: Intent): Intent? {
    val pm = context.packageManager
    val resolveInfo = pm.queryIntentServices(implicitIntent, 0)
    if (resolveInfo.size != 1) {
        return null
    }
    val serviceInfo = resolveInfo[0]
    val packageName = serviceInfo.serviceInfo.packageName
    val className = serviceInfo.serviceInfo.name
    val component = ComponentName(packageName, className)
    val explicitIntent = Intent(implicitIntent)
    explicitIntent.component = component
    return explicitIntent
}
  • 启动远程服务
fun startRemoteService(v: View) {
    val intent = Intent()
    intent.action = "com.wzh.alipay"
    val it = createExplicitFromImplicitIntent(this, intent)
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        startForegroundService(it)
    } else {
        startService(it)
    }
}

fun stopRemoteService(v: View) {
    val intent = Intent()
    intent.action = "com.wzh.alipay"
    val it = createExplicitFromImplicitIntent(this, intent)
    stopService(it)
}

fun bindRemoteService(v: View) {
    val intent = Intent()
    intent.action = "com.wzh.alipay"
    val it = createExplicitFromImplicitIntent(this, intent)
    bindService(it, conn, Context.BIND_AUTO_CREATE)
}

fun unbindRemoteService(v: View) {
    unbindService(conn)
}


inner class AlipayServiceConnection : ServiceConnection {
    override fun onServiceDisconnected(name: ComponentName?) {
        
    }

    override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
    		println("获取连接对象")
    }
}
  • 启动服务后,执行的结果是
startService
System.out: AliPayService onCreate
System.out: AliPayService onStartCommand

bindService
System.out: AliPayService onCreate
System.out: AliPayService onBind

stopService
System.out: AliPayService onDestroy

unBind
System.out: AliPayService onUnbind

ServiceConnection中的onServiceConnected执行需要服务中的onBind方法有返回值,并且是bindService的方式才会执行
inner class AliPayBinder :Binder(){

}
override fun onBind(intent: Intent?): IBinder? {
    println("AliPayService onBind")
    return AliPayBinder()
}

AIDL

概念

Android interface definition language安卓接口定义语言
作用:跨进程通信
应用场景:远程服务中的中间人对象,其他应用是拿不到的,那么在通过绑定服务获取中间人对象时,就无法强制转换,使用aidl,就可以在其他应用中拿到中间人类所实现的接口
如果需要调用远程服务的方法就需要使用aidl

实现

  • 在提供服务的应用中,创建一个供外部调用的功能接口,把这个接口的后缀名改为aidl,aidl文件中不能有任何修饰符。在Android studio中,使用右键创建aidl文件
    创建aidl
// IAliPay.aidl
package com.wzh.alipay;

// Declare any non-default types here with import statements

interface IAliPay {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

    void pay();
}
  • 立刻编译,生成java文件
    编译后,aidl文件对应的java类
    这个类中有个重要的内部类
public static abstract class Stub extends android.os.Binder implements com.wzh.alipay.IAliPay
  • 获取到Stub类

这个类既继承了Binder类,同时又实现了接口方法,从这里可以联想到上面定义一个类继承Binder,实现功能接口,然后让onBind方法返回出去。所以这时的这个内部类需要继承Stub类(必须)。

inner class AliPayBinder :IAliPay.Stub(){
    override fun basicTypes(
        anInt: Int,
        aLong: Long,
        aBoolean: Boolean,
        aFloat: Float,
        aDouble: Double,
        aString: String?
    ) {
    }

    override fun pay() {
        println("支付成功")
    }
}
  • 调用方,创建一个和服务方包名和名称相同aidl文件,编译
  • 调用方绑定服务时,创建的ServiceConnection对象中的中间人对象,需要将中间人进行强转,这里只能使用Stub.asInterface
inner class AlipayServiceConnection : ServiceConnection {
    override fun onServiceDisconnected(name: ComponentName?) {

    }

    override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
        println("获取连接对象")
        alipay = IAliPay.Stub.asInterface(service)
    }
}
  • 这样,使用这个aidl对象,就可以调用到服务方的方法了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值