Android Service全面解析

在 Android 四大组件(Activity、Service、BroadcastReceiver、ContentProvider)中,Service 是负责后台执行长时间运行操作的核心组件。它没有可视化界面,却支撑着音乐播放、文件下载、数据同步等核心场景的实现。

一、初识 Service

Service 是 Android 中用于在后台执行耗时操作的组件,它运行在应用进程的主线程(UI 线程)中,并非独立的后台线程。

核心特点有:

  • 无界面:无法直接与用户交互,需通过广播、Binder、EventBus 等方式与其他组件通信;
  • 主线程运行:所有生命周期方法(onCreate、onStartCommand 等)均在主线程执行,耗时操作必须手动开子线程;
  • 后台存活:即使启动 Service 的 Activity 销毁,Service 仍可继续运行(取决于启动方式);
  • 需注册:必须在 AndroidManifest.xml 中注册(四大组件唯一例外是 BroadcastReceiver 可动态注册)。

二、Service 基础使用

Service 有两种核心启动方式:startService(启动式)和 bindService(绑定式),两者的使用场景和生命周期完全不同。

1. 创建基础 Service

先定义一个自定义 Service,重写核心生命周期方法:

Kotlin 复制代码
class MyService : Service() {
    // Binder对象,用于绑定式Service与客户端通信
    private val binder = LocalBinder()

    inner class LocalBinder : Binder() {
        fun getService(): MyService = this@MyService
    }

    // 生命周期方法:Service创建时调用(仅一次)
    override fun onCreate() {
        super.onCreate()
        Log.d("MyService", "onCreate 执行,线程:${Thread.currentThread().name}")
        // 注意:此处是主线程,耗时操作需开子线程
        Thread {
            // 模拟耗时操作(如下载、数据处理)
            Thread.sleep(5000)
            Log.d("MyService", "耗时操作执行完成")
        }.start()
    }

    // 启动式Service核心方法:每次调用startService都会执行
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.d("MyService", "onStartCommand 执行,startId:$startId")
        // 返回值决定Service被系统杀死后的重启策略:
        // START_STICKY:被杀后重启,intent为null
        // START_NOT_STICKY:被杀后不重启
        // START_REDELIVER_INTENT:被杀后重启,重新传递最后一个intent
        return START_STICKY
    }

    // 绑定式Service核心方法:首次bindService时执行
    override fun onBind(intent: Intent): IBinder {
        Log.d("MyService", "onBind 执行")
        return binder
    }

    // 解绑时执行(所有绑定都解绑后触发)
    override fun onUnbind(intent: Intent?): Boolean {
        Log.d("MyService", "onUnbind 执行")
        return super.onUnbind(intent)
    }

    // Service销毁时执行:释放资源的核心位置
    override fun onDestroy() {
        super.onDestroy()
        Log.d("MyService", "onDestroy 执行")
    }
}

2. 注册Service

AndroidManifest.xml 中注册:

XML 复制代码
<manifest ...>
    <application ...>
        <!-- 注册Service -->
        <service
            android:name=".MyService"
            android:exported="false" /> <!-- exported=false:仅本应用可用 -->
    </application>
</manifest>

3. startService(启动式)

核心特点
  • 与调用者(如 Activity)解耦,调用者销毁后 Service 仍运行;
  • 多次调用 startService 只会重复执行 onStartCommand,不会重复创建;
  • 必须手动停止 Service,否则会一直运行。
Kotlin 复制代码
// Activity中启动Service
val startIntent = Intent(this, MyService::class.java)
startService(startIntent) // Android 8.0+ 后台启动需用startForegroundService

// 停止Service(两种方式)
// 方式1:外部调用stopService
val stopIntent = Intent(this, MyService::class.java)
stopService(stopIntent)

// 方式2:Service内部调用stopSelf(推荐,可指定startId停止)
stopSelf() // 停止当前Service
stopSelf(startId) // 停止指定startId的Service(多任务场景)

4. bindService(绑定式)

核心特点
  • 与调用者绑定,调用者(如 Activity)销毁后,Service 会自动解绑;
  • 支持调用者与 Service 双向通信(通过 Binder);
  • 多次绑定同一 Service 只会执行一次 onBind
Kotlin 复制代码
class MainActivity : AppCompatActivity() {
    private var myService: MyService? = null
    private var isBound = false

    // ServiceConnection:绑定回调(核心)
    private val serviceConnection = object : ServiceConnection {
        // 绑定成功时回调(运行在主线程!)
        override fun onServiceConnected(className: ComponentName, service: IBinder) {
            Log.d("MainActivity", "onServiceConnected 线程:${Thread.currentThread().name}")
            val binder = service as MyService.LocalBinder
            myService = binder.getService()
            isBound = true
            // 调用Service的方法(通信示例)
            myService?.let {
                Log.d("MainActivity", "已绑定Service,可调用其方法")
            }
        }

        // 绑定意外断开时回调(正常解绑不会触发)
        override fun onServiceDisconnected(className: ComponentName) {
            Log.d("MainActivity", "onServiceDisconnected 执行")
            isBound = false
        }
    }

    // 绑定Service
    fun bindService(view: View) {
        val bindIntent = Intent(this, MyService::class.java)
        bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE)
        // BIND_AUTO_CREATE:绑定后自动创建Service(无需先start)
    }

    // 解绑Service(必须在Activity销毁前执行,否则内存泄漏)
    fun unbindService(view: View) {
        if (isBound) {
            unbindService(serviceConnection)
            isBound = false
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        // 页面销毁时确保解绑
        if (isBound) {
            unbindService(serviceConnection)
            isBound = false
        }
    }
}

两种方式主要有以下的区别:

维度 startService bindService
关联关系 与调用者解耦 与调用者绑定
通信能力 仅单向(调用者→Service) 双向(调用者↔Service)
生命周期 onCreate→onStartCommand→onDestroy onCreate→onBind→onUnbind→onDestroy
停止方式 stopService/stopSelf unbindService(所有绑定解绑后)
适用场景 无交互的后台任务(如下载) 需交互的后台任务(如音乐播放控制)

三、 Service 生命周期

官方图:

  • onCreate:Service 首次创建时执行,无论启动多少次,仅执行一次;
  • onStartCommand:每次调用 startService 都会执行;
  • onBind:首次调用 bindService 时执行,多次绑定仅执行一次;
  • onDestroy:Service 销毁时执行,需在此释放资源(如关闭线程、取消广播监听);

所有的生命周期方法都运行在主线程中。

四、主流Service保活方案

Android 系统会根据进程优先级 回收内存,Service 所在进程默认是「服务进程」(优先级第 3),容易被杀死。进程保活的核心思路是:提升进程优先级 + 被杀后重启

Android 进程优先级(从高到低):

前台进程(Foreground):用户正在交互的 Activity / 前台 Service;

可见进程(Visible):用户可见但不交互的组件(如悬浮窗);

服务进程(Service):运行后台 Service 的进程;

后台进程(Background):用户退出的 Activity 进程;

空进程(Empty):无活跃组件的进程。

1. 前台Service(官方推荐)

将 Service 提升为前台进程,系统几乎不会杀死。需显示一个前台通知。

Kotlin 复制代码
override fun onCreate() {
    super.onCreate()

    // 构建前台通知
    val notification = NotificationCompat.Builder(this, "service_channel")
        .setContentTitle("前台服务运行中")
        .setContentText("正在执行后台任务")
        .setSmallIcon(R.mipmap.ic_launcher)
        .build()

    // 启动前台Service(id不能为0)
    startForeground(1, notification)
}

2. 使用 onStartCommand 返回值

onStartCommand 返回的重启策略,当系统因内存不足杀死 Service 所在进程后,AMS 会检查该 Service 的「重启策略」,决定是否重建 Service、重建时是否传递原 Intent:

Kotlin 复制代码
override fun onStartCommand(...): Int {
    return START_STICKY
}

Service被杀后重建,但不能防杀

3. WorkManager

利用系统调度任务,定期检查 Service 是否存活,若被杀则重启:

Kotlin 复制代码
WorkManager.getInstance(context)
    .enqueue(OneTimeWorkRequest.from(MyWorker::class.java))

4. 双进程守护

两个进程的 Service 互相监听,一个被杀后另一个重启对方。核心是利用 ServiceonDestroy 或广播监听进程状态。

Service保活的核心是提高进程优先级,最可靠的方式是前台Service,通过 startForeground 提升优先级。能交给系统调度的,就交给 WorkManager ;真正需要长期、实时运行的任务,才使用前台 Service,并明确告知用户其存在和价值。

相关推荐
alexhilton3 小时前
Jetpack ViewModel内幕:内部机制与跨平台设计
android·kotlin·android jetpack
_李小白5 小时前
【Android FrameWork】延伸阅读: Android应用安装过程
android
光头闪亮亮6 小时前
Android手持机扫码出入库的开发详解-6.APP下载更新
android
光头闪亮亮6 小时前
Android手持机扫码出入库的开发详解-7.SQLite CRUD操作
android
键来大师6 小时前
Android16 设置壁纸出现APK重启问题和悬浮控件等图标变成黑色图框
android·framework·rk3576
_李小白6 小时前
【Android FrameWork】第四十二天:PMS main函数
android
BoomHe7 小时前
Android LMK(Low Memory Killer)机制
android
时光呀时光慢慢走7 小时前
MAUI 开发安卓 MQTT 客户端:实现远程控制 (完整源码 + 避坑指南)
android·物联网·mqtt·c#
成都大菠萝7 小时前
2-2-44 快速掌握Kotlin-函数类型操作
android