四大组件---Service

301 阅读5分钟

四大组件---Service

一、进程的概念

【1】 android的四大组件都运行在主线程中

【2】 进程是有生命周期的,最先杀死最不重要的进程

【3】 进程的优先级

[1]	Foreground process(前台进程)
	优先级最高,相当于activity执行了onResume
[2]	Visible process(可视进程)
	一直影响用户看到见,相当于activity执行了onPause
[3]	Service process(服务进程)
	通过startService()开启的服务
[4]	Background process(后台进程)
	用户不可见,相当于activity中调用了onStop方法,但是并没有销毁
[5]	Empty process(空进程)
	不会维持任何组件运行

二、start方式开启服务

【1】 定义一个类继承Service,在清单文件中注册

【2】 服务是在后台运行的,相当于没有界面的activity

【3】 服务启动后执行onCreate方法和onStart方法,之后如果再次启动,则只执行onStart方法,android 8.0以上的,建议不要用startServce,因为google为了省电,后台服务无操作后,一段时间后会自动停止服务,所以Android 8.0启动服务的解决方案是,调用startForegroundService(intent),调用此方法后,需要5s秒在service中调用startForeground(int id, Notification notification),否则系统会报出ANR异常,需要在清单文件中添加权限

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

java代码,在service类中

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    // android 8.0以上通知栏必须创建NotificationChannel
    val channelId = "myServiceId"
    val channel =
        NotificationChannel(channelId, "my", NotificationManager.IMPORTANCE_HIGH)
    // 创建NotificationManager
    val notificationManager =
        getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    // 使用NotificationManager创建channel
    notificationManager.createNotificationChannel(channel)
    // 创建通知栏,注意Builder的第二个参数是上面创建的channelId
    val notification = Notification.Builder(this, channelId)
        .build()
    // 开启前台服务
    startForeground(100, notification)
}

【4】 服务一旦开启,就会在后台长期运行,需要手动停止

三、bind方式启动服务

引入bindService,目的是为了在activity可以调用service中的方法

1.bindService的特点

【1】 使用bindService(Intent,ServiceConnection,flag)绑定服务,定义一个类实现ServiceConnection

【2】 注意在activity中绑定服务后,多次bindService没有效果,在界面销毁时,需要解绑服务,调用unbindService(ServiceConnection),但是不能多次解绑

【3】 使用绑定这种方式启动服务,在activity销毁的时候,服务也跟着销毁

【4】 启动服务时,service只会执行onCreate,不会执行onStartCommand

【5】 ServiceConnection中的onServiceConnected执行的时机,是在service中的onBind方法是否有返回值

【6】 这种启动方式,在设置中不能找到该服务,相当于一个隐形服务

2.调用service中的方法

① 使用Binder的方式

【1】 定义一个类继承Binder对象,在该类中定义外部调用的方法

inner class MyBind : Binder() {
    fun f1() {
        // 调用service中方法
        this@MyService.f1()
    }
}

【2】 在service中定义外部需要调用的函数,在Binder子类中调用

【3】 在service中的onBind方法中,返回刚创建的Binder的子类

override fun onBind(intent: Intent?): IBinder? {
    println("绑定")
    return MyBind()
}

【4】 在activity中bindService,在ServiceConnection的实现类中,的onServiceConnected方法中,获取IBinder对象,强转为自己刚定义的Binder子类,即可调用方法,间接的调用到service中的方法

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

    override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
        println("onServiceConnected")
        if (service is MyService.MyBind){
            service.f1()
        }
    }

}

② 使用接口方式调用服务里的方法(比较常用)

【1】 定义一个接口,把想暴露的方法定义在接口中

【2】 在定义的binder对象实现这个接口

【3】 在OnServiceConnected中获取IBinder对象,将IBinder对象转为接口的类型,即可调用接口中方法

四、混合方式开启服务

既让service在后台长期运行,又可以调用service中方法

【1】 调用startService使服务在后台长期运行

【2】 调用bindService可以调用service的方法

【3】 同时调用startService和bindService,解绑服务后,服务还没销毁,需要再调用stopService,服务才会被kill,同样的,如果先调用stopService,也要再调用unbindSerivce,服务才会停止

五、Aidl

1.本地服务:运行自己应用里的服务

【1】 不涉及进程间的通信

2.远程服务:运行在其他应用里的服务

【1】 实现进程间的通信IPC

【2】 aidl:Android Interface Definition Language,专门解决进程间的通信

【3】 应用场景:支付宝

【4】 实现的步骤:

比如两个应用之间通信
1.A应用中创建一个service,例如是RemoteService,与普通的service实现的一样
2.创建一个aidl,就是相当于一个接口,编译
3.在RemoteService中的onBind方法,返回一个IBinder对象,这时创建一个类,继承刚创建的aidl中的Stub
4.在B应用中开启service,例如使用bindService,开启服务时,需要用到隐式意图启动,
  注意:在android 5.0以上,服务默认都要显示启动,如果一定要隐式启动的话,需要指定package属性
5.将A应用的中aidl,连同包名一起复制到B应用中,编译
  在B应用中的ServiceConnection的实现类中的onServiceConnected方法中,调用Stub.asInterface(IBinder)方法,返回的是aidl接口类型,这时就可以调用接口中的方法了

java代码实现

A应用:

class RemoteService : Service() {
override fun onBind(intent: Intent?): IBinder? {
    return RemoteBinder()
}

override fun onCreate() {
    super.onCreate()
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val notificationManager =
            getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        val channel =
            NotificationChannel("remote", "远程服务", NotificationManager.IMPORTANCE_HIGH)
        notificationManager.createNotificationChannel(channel)
        val notification = Notification.Builder(this, "remote").build()
        startForeground(1, notification)
    }
}

fun remoteMethod() {
    println("远程服务中的方法")
}

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

    override fun remoteMethod() {
        this@RemoteService.remoteMethod()
    }
}
}


B应用:

class MainActivity : AppCompatActivity() {

private val conn: RemoteConn
    get() = RemoteConn()

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

    // 调用远程服务
    val intent = Intent()
    intent.action = ("com.wzh.a03_servicedemo.aidl.RemoteService")
    intent.`package` = "com.wzh.a03_servicedemo"
    bindService(intent, conn, Context.BIND_AUTO_CREATE)
}

override fun onDestroy() {
    super.onDestroy()
    unbindService(conn)
}

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

    override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
        val iRemoteServiceAidl = IRemoteServiceAidl.Stub.asInterface(service)
        iRemoteServiceAidl.remoteMethod()
    }

}
}


aidl文件:

interface IRemoteServiceAidl {
    /**
     * 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 remoteMethod();
}