添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

service类型

android中service分为background service,bound service,foreground servce,其中background service运行在后台与ui没有交互,bound service通过service connect可以跟activity等通过binder进行数据交互,也可以通过messenger,aidl等进行多进程通信。 Foreground service与notification绑定,通过notification通知用户。

1、foreground service

foreground service 需要和notification结合使用,一般用在音乐播放器,和应用的推送等。Android 9以上前台service必须进行权限配置,并且foreground service必须要和notification一起使用。

manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>
    < uses-permission  android:name = "android.permission.FOREGROUND_SERVICE" />
    <application ...>
        ...
    </application>
</manifest>
1、创建foreground service

可以在service的onStartCommand方法中创建notification,如下:

@RequiresApi(Build.VERSION_CODES.O)
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    val pendingIntent: PendingIntent =
        Intent(this, LaunchActivity::class.java).let { notificationIntent ->
PendingIntent.getActivity(
                this, 0, notificationIntent,
                PendingIntent.FLAG_IMMUTABLE
createNotificationChannel()
    val notification: Notification = Notification.Builder(this, channel_default_importance)
        .setContentTitle(getText(R.string.notification_title))
        .setContentText(getText(R.string.notification_message))
        .setSmallIcon(R.drawable.ic_launcher_background)
        .setContentIntent(pendingIntent)
        .build()
    startForeground(12, notification)
    return START_NOT_STICKY
//createNotificationChannel
private fun createNotificationChannel() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val serviceChannel = NotificationChannel(
            channel_default_importance,
            "Foreground Service Channel",
            NotificationManager.IMPORTANCE_DEFAULT
        val manager = getSystemService(
            NotificationManager::class.java
        manager.createNotificationChannel(serviceChannel)

注意当sdk版本大于O时一定要创建NotificationChannel,不然会报下面错误

可以通过foreground service设置跳转的activity,设置标题和点击事件等。

2、启动foreground service
val foreSeIntent = Intent(this, ForegroundsService::class.java)
applicationContext.startForegroundService(foreSeIntent)
3、forground service使用场景

1、音乐播放器

qq音乐,网易云音乐,喜马拉雅等都会有foreground service 提供给用户进行操作。

2、应用推送

4、Foreground service 版本限制

在Android 12 (API level 31) 及以上,不能在应用处于后台时启动foreground service,否则会抛出:

ForegroundServiceStartNotAllowedException 在Android 12及以上应用要在后台启动前台service,可以通过workManager来实现,也可以在一下场景下解决应用后台时启动foreground service.

1、用户对与应用相关的UI元素执行操作。例如,bubble, notification, widget, 或者 activity。

2、通过监听 ACTION_BOOT_COMPLETED, ACTION_LOCKED_BOOT_COMPLETED, ACTION_MY_PACKAGE_REPLACED ACTION_TIMEZONE_CHANGED, ACTION_TIME_CHANGED, ACTION_LOCALE_CHANGED等广播事件。

3、引导用户关闭电量优化。

2、background service

Background service 运行在后台mainfest配置

<service android:name=".service.BackgroundService" />

创建background service,继承service必须重写service中的onBind方法,

class BackgroundService : Service() {
    override fun onBind(intent: Intent?): IBinder? {
        return null
1、startService
Intent intent = new Intent(this,BackgroundService.class);
startService(intent);
2、多次调用startService

只有首次调用startService时才会调用onCreate方法,后面调用startService只会调用onStartCommand方法。如下连续调用三次startService,startid每次增大1,

val intent = Intent(this, BackgroundService::class.java)
startService(intent)
startService(intent)
startService(intent)

输出log如下,只有调用一次onCreate,后面的startService都直接调用onStartCommand,只是startid有增加

 D/backgroundService: onCreate
 D/backgroundService: onStartCommand  startid 1  flags 0
 D/backgroundService: onStart  startid 1
 D/backgroundService: onStartCommand  startid 2  flags 0
 D/backgroundService: onStart  startid 2
 D/backgroundService: onStartCommand startid 3  flags 0
 D/backgroundService: onStart  startid 3
3、退出应用后再返回(不杀应用进程)
 D/backgroundService: onCreate
 D/backgroundService: onStartCommand  startid 1  flags 0
 D/backgroundService: onStart intent  startid 1
//退出应用,后返回
-------------------
 D/backgroundService: onStartCommand startid 2  flags 0
 D/backgroundService: onStart startid 2

由上面log可知退出应用后再启动应用,service会重新走onStartCommand,不会走onCreate,但是startid不相同。

4、停止background service
val intent = Intent(this, BackgroundService::class.java)
stopService(intent)
3、bound service

Bound service以client server形式,允许activity等组件跟service进行交互。activity可以通过bindService来绑定一个service,当所有的activity等组件都执行unBindService时 bound service会执行destroy。bound service的生命周期如下:
在这里插入图片描述

1、Bound service使用

1、创建bound service并且重写onBind方法

class BoundService : Service() {
    private val TAG: String = "BoundService"
    private var binder: Binder = MyImplementor()
    override fun onBind(intent: Intent?): IBinder {
        Log.d(TAG, "onBind")
        return binder

2、定义ServiceConnection关联service和activity或者其他组件。

connect = object : ServiceConnection {
    override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
        //connect
        aidlInterface = IMyAidlInterface.Stub.asInterface(service)
        Log.d(TAG, "onServiceConnected ${aidlInterface?.message}")
        slogin.text = aidlInterface?.message
    override fun onServiceDisconnected(name: ComponentName?) {
        //disconnect
        Log.d(TAG, "onServiceDisconnected")
        aidlInterface = null

3、bindService

applicationContext.bindService(boundIntent, connect, BIND_AUTO_CREATE)

使用bound service可以通过binder来从service中获取数据。

2、bound service使用注意

使用bound service时必须先bindservice,然后再unBindService,不然会报如下错误。
在这里插入图片描述

3、activity获取Bound service交互方式
1、binder

这里binder只适用于本地service,也就是默认进程创建的service,如果是remote指定的service,不能使用localBinder的方式。(也就是不支持多进程)

创建一个LocalBinder,在onBinder的时候返回

class LocalService : Service() {
    // Binder given to clients
    private val binder = LocalBinder()
    // Random number generator
    private val mGenerator = Random()
    /** method for clients  */
    val randomNumber: Int
        get() = mGenerator.nextInt(100)
     * Class used for the client Binder.  Because we know this service always
     * runs in the same process as its clients, we don't need to deal with IPC.
    inner class LocalBinder : Binder() {
        // Return this instance of LocalService so clients can call public methods
        fun getService(): LocalService = this@LocalService
    override fun onBind(intent: Intent): IBinder {
        return binder

在ServiceConnect中获取binder,并且通过binder获取service实例。

    /** Defines callbacks for service binding, passed to bindService()  */
    private val connection = object : ServiceConnection {
        override fun onServiceConnected(className: ComponentName, service: IBinder) {
            // We've bound to LocalService, cast the IBinder and get LocalService instance
            val binder = service as LocalService.LocalBinder
            mService = binder.getService()
            mBound = true
        override fun onServiceDisconnected(arg0: ComponentName) {
            mBound = false

通过service实例来获取service中的变量

if (mBound) 
    val num: Int = mService.randomNumber
    Toast.makeText(this, "number: $num", Toast.LENGTH_SHORT).show()
2、Messenger

messenger内部也是通过aidl来支持跟remote进程中的service通信,也就是跨进程通信。

值service中创建一个Messenger,并且创建一个handler内部类来处理msg。

/** Command to the service to display a message  */
private const val MSG_SAY_HELLO = 1
class MessengerService : Service() {
     * Target we publish for clients to send messages to IncomingHandler.
    private lateinit var mMessenger: Messenger
     * Handler of incoming messages from clients.
    internal class IncomingHandler(
            context: Context,
            private val applicationContext: Context = context.applicationContext
    ) : Handler() {
        override fun handleMessage(msg: Message) {
            when (msg.what) {
                MSG_SAY_HELLO ->
                    Toast.makeText(applicationContext, "hello!", Toast.LENGTH_SHORT).show()
                else -> super.handleMessage(msg)
     * When binding to the service, we return an interface to our messenger
     * for sending messages to the service.
    override fun onBind(intent: Intent): IBinder? {
        Toast.makeText(applicationContext, "binding", Toast.LENGTH_SHORT).show()
        mMessenger = Messenger(IncomingHandler(this))
        return mMessenger.binder

在serviceConnect中去获取binder,并且封装成一个Messenger,并且通过Messenger来发送数据。

private val mConnection = object : ServiceConnection {
        override fun onServiceConnected(className: ComponentName, service: IBinder) {
            // This is called when the connection with the service has been
            // established, giving us the object we can use to
            // interact with the service.  We are communicating with the
            // service using a Messenger, so here we get a client-side
            // representation of that from the raw IBinder object.
            mService = Messenger(service)
            bound = true
        override fun onServiceDisconnected(className: ComponentName) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected -- that is, its process crashed.
            mService = null
            bound = false
  fun sayHello(v: View) {
        if (!bound) return
        // Create and send a message to the service, using a supported 'what' value
        val msg: Message = Message.obtain(null, MSG_SAY_HELLO, 0, 0)
        try {
            mService?.send(msg)
        } catch (e: RemoteException) {
            e.printStackTrace()
3、aidl

aidl是支持进程间通信的,所以使用aidl可以跨进程通信使用aidl通信方式可以参考https://hanking.blog.csdn.net/article/details/125020718

4、bindservice vs startService
  • 先startservice后再进行bindService,此时进行unBindServide,service并不会执行destroy。如下
  • bindservice->startService->unBindService->bindService
  • 多次连续bindservice,只有首次会执行。
  • 多次startService首次执行onCreate后面只会执行onStartCommand

service生命周期

service相关面试题

1、Service 的 onStartCommand 方法有几种返回值及意义
1、START_STICKY

如果在执行完 onStartCommand 后,service被异常 kill 掉,系统会重启service并且重走onStartCommand方法,但是onStartCommand中的intent为null。(在轮询中可以使用这种方式,轮询时客户端可以定时的从服务端获取数据)

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    return START_STICKY
2、START_NO_STICKY

如果在执行完 onStartCommand 后,service被异常 kill 掉,系统不会自动重启该服务。

3、START_REDELIVER_INTENT

如果在执行完 onStartCommand 后,service被异 常 kill 掉,系统会自动重启该服务,并将 Intent 的值传入。(常用于后台下载资源,比如资源下载到一半时service被kill了,后面service重启时会可以根据intent的值下载后面没有下载的资源)

2、Service 和 IntentService 的区别?

IntentService继承service,并且IntentService在onCreate的时候创建了一个HandlerThread,并且创建一个mServiceHandler,

public void onCreate() {
    // TODO: It would be nice to have an option to hold a partial wakelock
    // during processing, and to have a static startService(Context, Intent)
    // method that would launch the service & hand off a wakelock.
    super.onCreate();
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();
    mServiceLooper = thread.getLooper();
    mServiceHandler = new ServiceHandler(mServiceLooper);

在service执行onStart的时候,通过handler发送一个message

@Override
public void onStart(@Nullable Intent intent, int startId) {
    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;
    msg.obj = intent;
    mServiceHandler.sendMessage(msg);

然后在serviceHandler中handlerMessage中处理这个msg,执行onHandleIntent((Intent)msg.obj);方法,并且stopSelf.

private final class ServiceHandler extends Handler {
    public ServiceHandler(Looper looper) {
        super(looper);
    @Override
    public void handleMessage(Message msg) {
        onHandleIntent((Intent)msg.obj);
        stopSelf(msg.arg1);

所以继承IntentService时要继承onHandleIntent(@Nullable Intent intent)并且可以在onHandleIntent中处理耗时操作。注意由于ServiceHandler中Looper是从ThreadHandler中获取的,所以onHandleIntent方法不是执行在主线程中。

3、service 里面是否 能执行耗时的操作?

service默认是在主线程中执行,所以service不能执行耗时操作,如果要执行耗时操作可以继承IntentService并且重写onHandleIntent来执行,也可以通过配置service所在的进程,如下

<service
    android:name="com.example.aidld.service.BoundService"
    android:enabled="true"
    android:exported="true"
    android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
    android:process=":remote">

通过配置android:process=":remote"来让service在其他进程中执行。

4、Service里面能弹toast吗?

service中可以弹toast,如下makeText时候传入this,service继承ContextWrapper。

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    Log.d(TAG,"onStartCommand intent $intent startid $startId  flags $flags")
    Toast.makeText(this,"this is toast", Toast.LENGTH_LONG).show()
    return START_STICKY
5、Service 的 onRebind(Intent)方法执行时机?
  • 同一个activity中如果直接执行bindService,并且执行unBindService,这时再执行bindService,service的生命周期如下:
  • 同一个activity中如果先执行startService,再进行bindService,然后unBindService 再进行bindService生命周期如下:
  • startService之后同一个activity多次执行bindService,只有首次bindService会调用Service中的onBind方法,后面bindService不会再执行onBind方法。

  • 多次执行startService方法

通过下面的adb指令打印services,然后查看自定义的service

adb shell dumpsys activity services

如下可以看到service的信息,

* ServiceRecord{38c5581 u0 com.example.greedwebview/com.example.aidld.service.BoundService}
  intent={cmp=com.example.greedwebview/com.example.aidld.service.BoundService}
  packageName=com.example.greedwebview
  processName=com.example.greedwebview:remote
  permission=android.permission.BIND_ACCESSIBILITY_SERVICE
  baseDir=/data/app/~~6XYMsYPqHpQ-rzLcM8zU6A==/com.example.greedwebview-HogsH4v4cZdX5ktjemR8tg==/base.apk
  dataDir=/data/user/0/com.example.greedwebview
  app=ProcessRecord{a56288b 12472:com.example.greedwebview:remote/u0a167}
  allowWhileInUsePermissionInFgs=true
  recentCallingPackage=com.example.greedwebview
  createTime=-4m48s864ms startingBgTimeout=--
  lastActivity=-4m47s482ms restartTime=-4m48s795ms createdFromFg=true
  startRequested=true delayedStop=false stopIfKilled=false callStart=true lastStartId=3
  • startService ->bindService->stopService->unbindService->bindService

执行startService后bindService然后再执行stopService,再执行unBindService此时service会执行destory方法,再次bindService时,会重新走onCreate->onBind

6、能不能多次调用unbindService?

同一个activity中如果没有bindService时调用unBindService,会有如下报错:

2022-05-28 16:20:03.044 10686-10686/com.example.aidl E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.aidl, PID: 10686
    java.lang.IllegalArgumentException: Service not registered: com.example.aidld.LaunchActivity$createConnect$1@162849d
        at android.app.LoadedApk.forgetServiceDispatcher(LoadedApk.java:1757)
        at android.app.ContextImpl.unbindService(ContextImpl.java:1874)
        at android.content.ContextWrapper.unbindService(ContextWrapper.java:792)
        at com.example.aidld.LaunchActivity.unBindService(LaunchActivity.kt:81)
7、service保活

1、设置service为前台service,提高service优先级。

<intent-filter android:priority="1000">
    <action android:name="BoundService" />
</intent-filter>

其中1000为最高优先级。

2、双进程service保护,在一个service被回收时启动另一个service。

private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { //链接上 Log.d("test","GuardService:建立链接"); @Override public void onServiceDisconnected(ComponentName componentName) { //断开链接 startService(new Intent(GuardService.this,StepService.class)); //重新绑定 bindService(new Intent(GuardService.this,StepService.class), mServiceConnection, Context.BIND_IMPORTANT);

3、加入电量白名单,跳过电量优化

1、https://developer.android.com/guide/components/foreground-services

2、https://developer.android.com/guide/components/bound-services

3、https://developer.android.com/training/notify-user/build-notification

Android 8.0 启动后台service 出错 IllegalStateException: Not allowed to start service Intent Android 8.0 不再允许后台service直接通过startService方式去启动, 具体行为变更如下: 如果针对 Android 8.0 的应用尝试在不允许其创建后台服务的情况下使用 startService() 函数,则该函数将引发一个 IllegalStateException。 新的 Context.startFo
文章目录一、背景二、后台的定义三、后台限制服务四、如何避免引起上述 Not allowed to start service Intent app is in background五、变通?参考文献 每次在后台运行时,应用都会消耗一部分有限的设备资源,例如 RAM。 这可能会影响用户体验,如果用户正在使用占用大量资源的应用(例如玩游戏或观看视频),影响尤为明显。 为了提升用户体验,And...
How to handle background services in ANDROID O? 如何处理Android O的后台Service 前段时间公司项目中做Android O的适配。在了解Android O新功能时,看到这个文章,虽然介绍的是Android O的预览版,但读了一遍感觉不错,记录下来。顺便翻译一下(不翻译大家肯定也看得懂,只是觉得单纯转一下太low了)… 原文地址: How ...
Android基础之Process 默认情况下,同一个应用程序中的所有组件运行在同一个进程中,而且绝大多数的应用程序也都是这样的。但是,如果我们想要控制让某个特定的组件属于某个进程,我们可以在manifest文件中进行配置。 在每种组件元素(activity、service、receiver、provider)的ma
Android12前台服务问题 :startForegroundService() not allowed due to mAllowStartForeground false 最近Google也是推出了Android12,在写项目的时候也是发现了一个适配Android12需要注意的问题 以 Android 12 为目标平台的应用在后台运行时无法再启动前台服务,但一些特殊情况除外。如果应用在后台运行时尝试启动前台服务,并且前台服务不符合任何特殊情况,则系统会抛出 ForegroundServiceSta
Android Service是一种在后台执行长时间运行任务的组件。它可以在应用程序的生命周期之外运行,并且可以在不与用户界面进行直接交互的情况下执行一些操作,如下载文件、处理数据、播放音乐等。 Android Service有两种类型:前台服务和后台服务。前台服务是一种在通知栏显示持续运行状态的服务,通常用于执行用户正在进行的操作,如播放音乐或进行文件下载。后台服务是一种在后台默默执行任务的服务,不会显示在通知栏上。 开发者可以通过继承Service类来创建自定义的服务。在Service类中,可以实现onCreate()、onStartCommand()和onDestroy()等方法来控制服务的生命周期和执行逻辑。通过调用startService()方法来启动服务,然后在不需要时调用stopService()方法来停止服务。 另外,服务还可以与其他组件进行通信,如Activity或BroadcastReceiver。可以通过Intent传递数据或者使用Binder进行跨进程通信。 总之,Android Service是一种在后台执行长时间任务的组件,它能够帮助开发者实现一些与用户界面交互无关的操作。