吃透 Android Service:从后台任务到保活

目录

前言:为什么 Service 是 Android 开发的 “后台核心”?

一、Service 基础认知:到底什么是 Service?

1.1 一句话搞懂 Service 的核心作用

1.2 必须澄清的 3 个常见误解

1.3 Service 与 Thread、IntentService 的区别

1.4 Service 的两种核心类型

二、Service 生命周期深度解析:两种启动方式的回调逻辑

2.1 核心生命周期回调方法(6 个关键方法)

2.2 启动型 Service(startService)的生命周期

核心流程:

关键说明:

示例 1:启动型 Service 生命周期日志

2.3 绑定型 Service(bindService)的生命周期

核心流程:

关键说明:

示例 2:绑定型 Service 生命周期与通信

2.4 混合启动(start+bind)的生命周期

核心规则:

三、onStartCommand () 返回值:决定 Service 被杀死后的行为

3.1 4 种返回值详解

3.2 实战示例:START_REDELIVER_INTENT 实现断点续传

关键说明:

四、IntentService:无需手动管理线程的 Service

4.1 IntentService 的核心优势

4.2 实战示例:用 IntentService 实现多任务下载

4.3 注意事项

五、Service 通信进阶:跨组件、跨进程通信

5.1 同进程通信:Binder + 接口回调(进阶)

示例 3:Service 主动推送下载进度到 Activity

5.2 跨进程通信:AIDL(Android 接口定义语言)

示例 4:用 AIDL 实现跨进程获取服务状态

步骤 1:创建 AIDL 文件(定义通信接口)

步骤 2:实现 AIDL 接口(Service 端)

步骤 3:注册 Service(允许跨进程调用)

步骤 4:客户端(另一个应用)调用 AIDL 服务

5.3 简单通信:广播(适合轻量数据传递)

示例 5:Service 通过广播通知 Activity 任务完成

5.4 3 种通信方式对比

六、Service 保活方案:避免被系统杀死的实战技巧

6.1 合法保活方案(推荐)

方案 1:前台服务(Foreground Service)

示例 6:实现前台服务

关键说明:

方案 2:WorkManager(替代后台 Service,Android 8.0 + 推荐)

示例 7:用 WorkManager 实现周期性数据同步

6.2 灰色保活方案(谨慎使用)

七、Service 常见坑点与避坑指南(实战经验总结)

7.1 坑点 1:Service 主线程耗时导致 ANR

7.2 坑点 2:绑定 Service 后未解绑导致内存泄漏

7.3 坑点 3:Android 8.0 + 后台 Service 容易被杀死

7.4 坑点 4:IntentService 并发任务执行失败

7.5 坑点 5:跨进程通信时抛出 RemoteException

八、总结:Service 核心知识点图谱


class 卑微码农:
    def __init__(self):
        self.技能 = ['能读懂十年前祖传代码', '擅长用Ctrl+C/V搭建世界', '信奉"能跑就别动"的玄学']
        self.发量 = 100  # 初始发量
        self.咖啡因耐受度 = '极限'
        
    def 修Bug(self, bug):
        try:
            # 试图用玄学解决问题
            if bug.严重程度 == '离谱':
                print("这一定是环境问题!")
            else:
                print("让我看看是谁又没写注释...哦,是我自己。")
        except Exception as e:
            # 如果try块都救不了,那就...
            print("重启一下试试?")
            self.发量 -= 1  # 每解决一个bug,头发-1
 
 
# 实例化一个我
我 = 卑微码农()

前言:为什么 Service 是 Android 开发的 “后台核心”?

刚入门 Android 时,我曾一度混淆 Service 和 Thread—— 觉得 “后台干活” 就该用 Thread,直到遇到 “退出 Activity 后音乐还在播放”“后台下载文件需要持续运行” 的场景,才发现 Service 的不可替代性。

其实 Service 是 Android 四大组件中专门负责 “后台任务” 的角色,它没有 UI 界面,却能在后台持续运行(即使应用退到后台),还能与 Activity、其他组件通信。但很多开发者用不好 Service,要么遇到 “Service 被系统杀死”“后台任务卡顿” 的问题,要么因不懂绑定机制导致内存泄漏。

本文会把 Service 的基础概念、生命周期、启动方式、通信交互、保活方案、常见坑点全讲透。包含 9 个完整可运行的原创示例,从基础入门到进阶实战,新手能直接上手,进阶开发者能夯实基础、避开坑点。

建议先收藏,再跟着示例一步步实操,遇到问题可以在评论区交流~


一、Service 基础认知:到底什么是 Service?

1.1 一句话搞懂 Service 的核心作用

Service 是 Android 系统提供的 “后台任务容器”,核心作用是在后台执行长时间运行的操作,且不依赖 UI 界面

简单说:Activity 负责 “前台交互”(比如点击按钮、显示页面),Service 负责 “后台干活”(比如音乐播放、文件下载、数据同步、定位追踪)。即使 Activity 销毁,Service 也能继续运行(前提是未被系统回收)。

1.2 必须澄清的 3 个常见误解

  • 误解 1:Service 是独立线程?—— 错!Service 默认运行在主线程(UI 线程),不能直接做耗时操作(否则会导致 ANR);
  • 误解 2:Service 会一直运行不被杀死?—— 错!当系统内存不足时,后台 Service 会被优先回收,需通过保活方案优化;
  • 误解 3:用 Thread 就能替代 Service?—— 不能!Thread 没有生命周期管理,应用退到后台后容易被系统回收,且无法与其他组件灵活通信。

1.3 Service 与 Thread、IntentService 的区别

组件运行线程生命周期管理核心优势适用场景
Service默认主线程受系统组件管理可后台持续运行、跨组件通信音乐播放、数据同步、定位追踪
Thread自定义子线程无(需手动管理)轻量、灵活临时耗时操作(如单次数据计算)
IntentService内置子线程任务完成后自动销毁无需手动管理线程异步单次任务(如单次文件下载)

1.4 Service 的两种核心类型

根据使用场景,Service 主要分为两种类型,后续所有用法都围绕这两种类型展开:

  • 启动型 Service(startService):通过startService()启动,一旦启动,Service 会独立运行,直到调用stopService()或自行停止,与启动者无直接通信;
  • 绑定型 Service(bindService):通过bindService()绑定,启动者(如 Activity)与 Service 建立连接,可双向通信,启动者销毁时需解绑,否则会内存泄漏。

二、Service 生命周期深度解析:两种启动方式的回调逻辑

Service 的生命周期比 Activity 简单,但因启动方式不同,回调流程差异很大。这是掌握 Service 的核心,也是最容易踩坑的地方。

2.1 核心生命周期回调方法(6 个关键方法)

方法名调用时机核心作用注意事项
onCreate()Service 首次创建时(仅调用 1 次)初始化资源(如创建线程、注册监听器)避免耗时操作(默认主线程)
onStartCommand()每次调用startService()时触发接收启动意图,执行后台任务需返回 int 类型结果(决定 Service 被杀死后的行为)
onBind()调用bindService()时触发(仅调用 1 次)返回 Binder 对象,建立组件与 Service 的通信通道必须返回 Binder 实例(或 null,不允许通信)
onUnbind()所有绑定者解绑(调用unbindService())后触发释放绑定相关资源可返回 boolean(true 表示后续绑定会触发 onRebind)
onRebind()onUnbind () 返回 true 时,再次绑定 Service 触发重新建立通信连接无需返回 Binder(复用之前的连接)
onDestroy()Service 销毁时(仅调用 1 次)彻底释放资源(如停止线程、注销监听器)是 Service 生命周期的最后一个方法

2.2 启动型 Service(startService)的生命周期

核心流程:

启动 → onCreate () → onStartCommand () → 运行中 → stopService ()/stopSelf () → onDestroy () → 销毁

关键说明:
  • 首次启动:执行onCreate() → onStartCommand()
  • 重复启动:不会调用onCreate(),直接调用onStartCommand()(多次启动会多次触发);
  • 停止方式:① 外部调用stopService(Intent);② Service 内部调用stopSelf();③ 系统内存不足时回收;
  • 示例验证:通过日志打印回调顺序。
示例 1:启动型 Service 生命周期日志
  1. 创建启动型 Service(StartServiceDemo):
public class StartServiceDemo extends Service {
    private static final String TAG = "ServiceTest";
    private Thread workThread;
    private boolean isRunning = false;

    // 首次创建时调用(仅1次)
    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate:Service创建");
        // 初始化后台线程(模拟耗时操作)
        initWorkThread();
    }

    // 每次startService时调用
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand:接收启动请求,startId=" + startId);
        // 从Intent中接收参数(示例:获取任务类型)
        if (intent != null) {
            String taskType = intent.getStringExtra("task_type");
            Log.d(TAG, "执行任务:" + taskType);
        }
        // 返回值决定Service被杀死后的行为(后续详解)
        return START_STICKY;
    }

    // 初始化后台线程(耗时操作必须放子线程)
    private void initWorkThread() {
        isRunning = true;
        workThread = new Thread(() -> {
            int count = 0;
            while (isRunning) {
                try {
                    Thread.sleep(1000);
                    count++;
                    Log.d(TAG, "后台任务执行中,计数:" + count);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        workThread.start();
    }

    // 绑定Service时调用(启动型Service可返回null,不支持绑定)
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind:不支持绑定,返回null");
        return null;
    }

    // Service销毁时调用
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy:Service销毁");
        // 停止后台线程(避免内存泄漏)
        isRunning = false;
        if (workThread != null) {
            workThread.interrupt();
        }
    }
}
  1. 在 Manifest 中注册 Service(必须步骤):
<!-- 注册Service,exported=false表示不允许其他应用调用 -->
<service
    android:name=".StartServiceDemo"
    android:exported="false" />
  1. 在 Activity 中启动和停止 Service:
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 启动Service
        findViewById(R.id.btn_start_service).setOnClickListener(v -> {
            Intent intent = new Intent(this, StartServiceDemo.class);
            intent.putExtra("task_type", "模拟文件下载");
            startService(intent);
        });

        // 停止Service
        findViewById(R.id.btn_stop_service).setOnClickListener(v -> {
            Intent intent = new Intent(this, StartServiceDemo.class);
            stopService(intent);
        });
    }
}
  1. 运行测试,观察 Logcat 日志:
  • 首次点击 “启动 Service”:
D/ServiceTest: onCreate:Service创建
D/ServiceTest: onBind:不支持绑定,返回null
D/ServiceTest: onStartCommand:接收启动请求,startId=1
D/ServiceTest: 执行任务:模拟文件下载
D/ServiceTest: 后台任务执行中,计数:1
D/ServiceTest: 后台任务执行中,计数:2
...
  • 再次点击 “启动 Service”(不停止的情况下):
D/ServiceTest: onStartCommand:接收启动请求,startId=2
D/ServiceTest: 执行任务:模拟文件下载

(不会再次调用 onCreate (),直接触发 onStartCommand ())

  • 点击 “停止 Service”:
D/ServiceTest: onDestroy:Service销毁

(后台线程停止,Service 销毁)

2.3 绑定型 Service(bindService)的生命周期

核心流程:

绑定 → onCreate () → onBind () → 建立连接 → 运行中 → 解绑(unbindService) → onUnbind () → onDestroy () → 销毁

关键说明:
  • 首次绑定:执行onCreate() → onBind()
  • 重复绑定:不会调用 onCreate () 和 onBind (),直接复用已建立的连接;
  • 必须解绑:绑定者(如 Activity)销毁前必须调用unbindService(),否则会导致 Service 内存泄漏;
  • 通信核心:onBind () 返回的IBinder对象是组件与 Service 通信的桥梁。
示例 2:绑定型 Service 生命周期与通信
  1. 创建绑定型 Service(BindServiceDemo),实现 IBinder 通信:
public class BindServiceDemo extends Service {
    private static final String TAG = "ServiceTest";
    // 1. 创建Binder子类(通信桥梁)
    private final IBinder mBinder = new LocalBinder();
    // 模拟后台数据(供Activity获取)
    private int mCounter = 0;
    private Thread workThread;
    private boolean isRunning = false;

    // 2. Binder子类:暴露Service的方法给Activity
    public class LocalBinder extends Binder {
        // 返回Service实例,让Activity可调用Service的公共方法
        BindServiceDemo getService() {
            return BindServiceDemo.this;
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate:Service创建");
        // 启动后台计数任务
        startCounterTask();
    }

    // 绑定Service时调用,返回Binder对象
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind:建立连接,返回Binder");
        return mBinder;
    }

    // 所有绑定者解绑后调用
    @Override
    public boolean onUnbind(Intent intent) {
        Log.d(TAG, "onUnbind:所有绑定者解绑");
        // 返回true,表示后续再次绑定会触发onRebind()
        return true;
    }

    // 再次绑定(onUnbind返回true时)
    @Override
    public void onRebind(Intent intent) {
        super.onRebind(intent);
        Log.d(TAG, "onRebind:重新建立连接");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy:Service销毁");
        // 停止后台任务
        isRunning = false;
    }

    // 3. Service的公共方法(供Activity调用)
    public int getCounter() {
        return mCounter;
    }

    public void resetCounter() {
        mCounter = 0;
    }

    // 后台计数任务
    private void startCounterTask() {
        isRunning = true;
        workThread = new Thread(() -> {
            while (isRunning) {
                try {
                    Thread.sleep(1000);
                    mCounter++;
                    Log.d(TAG, "后台计数:" + mCounter);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        workThread.start();
    }
}
  1. 在 Manifest 中注册:
<service
    android:name=".BindServiceDemo"
    android:exported="false" />
  1. 在 Activity 中绑定 Service 并通信:
public class BindServiceActivity extends AppCompatActivity {
    private static final String TAG = "ServiceTest";
    // 绑定服务的连接对象
    private ServiceConnection mConnection;
    // Service实例(通过Binder获取)
    private BindServiceDemo mService;
    // 是否已绑定
    private boolean mBound = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bind_service);

        // 1. 初始化ServiceConnection(监听绑定状态)
        initServiceConnection();

        // 绑定Service
        findViewById(R.id.btn_bind).setOnClickListener(v -> {
            Intent intent = new Intent(this, BindServiceDemo.class);
            // 绑定Service,指定连接对象和标记(BIND_AUTO_CREATE:绑定后自动创建Service)
            bindService(intent, mConnection, BIND_AUTO_CREATE);
        });

        // 解绑Service
        findViewById(R.id.btn_unbind).setOnClickListener(v -> {
            if (mBound) {
                unbindService(mConnection);
                mBound = false;
            }
        });

        // 调用Service的方法(获取计数)
        findViewById(R.id.btn_get_counter).setOnClickListener(v -> {
            if (mBound) {
                int counter = mService.getCounter();
                Toast.makeText(this, "当前计数:" + counter, Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "请先绑定Service", Toast.LENGTH_SHORT).show();
            }
        });

        // 调用Service的方法(重置计数)
        findViewById(R.id.btn_reset_counter).setOnClickListener(v -> {
            if (mBound) {
                mService.resetCounter();
                Toast.makeText(this, "计数已重置", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "请先绑定Service", Toast.LENGTH_SHORT).show();
            }
        });
    }

    // 初始化ServiceConnection
    private void initServiceConnection() {
        mConnection = new ServiceConnection() {
            // 绑定成功时触发(获取Binder对象)
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.d(TAG, "onServiceConnected:绑定成功");
                // 转换为自定义Binder,获取Service实例
                BindServiceDemo.LocalBinder binder = (BindServiceDemo.LocalBinder) service;
                mService = binder.getService();
                mBound = true;
            }

            // 绑定意外断开时触发(如Service崩溃)
            @Override
            public void onServiceDisconnected(ComponentName name) {
                Log.d(TAG, "onServiceDisconnected:绑定断开");
                mBound = false;
            }
        };
    }

    //  Activity销毁时解绑(必须!避免内存泄漏)
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }
}
  1. 运行测试,观察日志:
  • 点击 “绑定 Service”:
D/ServiceTest: onCreate:Service创建
D/ServiceTest: onBind:建立连接,返回Binder
D/ServiceTest: onServiceConnected:绑定成功
D/ServiceTest: 后台计数:1
D/ServiceTest: 后台计数:2
...
  • 点击 “获取计数”:Toast 显示当前计数(与 Service 同步);
  • 点击 “解绑 Service”:
D/ServiceTest: onUnbind:所有绑定者解绑
D/ServiceTest: onDestroy:Service销毁
  • 再次绑定(已解绑后):
D/ServiceTest: onCreate:Service创建
D/ServiceTest: onBind:建立连接,返回Binder
D/ServiceTest: onServiceConnected:绑定成功

(因 onUnbind 返回 true,若再次绑定前未销毁 Service,会触发 onRebind ())

2.4 混合启动(start+bind)的生命周期

实际开发中可能需要 “启动 Service 让其持续运行,同时绑定 Service 进行通信”(如音乐播放器:startService 保持播放,bindService 控制播放 / 暂停)。

核心规则:
  • 启动 + 绑定后,Service 会持续运行,需同时满足 “停止启动” 和 “解除绑定” 才会销毁;
  • 生命周期流程:startService () → onCreate () → onStartCommand () → bindService () → onBind () → 运行中 → stopService () → unbindService () → onUnbind () → onDestroy ();
  • 注意:必须先停止启动(stopService),再解绑(unbindService),否则 Service 可能无法正常销毁。

三、onStartCommand () 返回值:决定 Service 被杀死后的行为

启动型 Service 的onStartCommand()方法需要返回一个 int 类型值,这个返回值决定了 “当 Service 被系统杀死后,系统是否会重启它”,是 Service 保活的基础。

3.1 4 种返回值详解

返回值核心行为适用场景
START_STICKY系统杀死后会重启 Service,但不保留之前的 Intent无需恢复任务(如音乐播放、定位)
START_NOT_STICKY系统杀死后不重启 Service一次性任务(如单次数据同步)
START_REDELIVER_INTENT系统杀死后会重启 Service,并保留最后一个 Intent需要恢复任务(如文件下载、断点续传)
START_STICKY_COMPATIBILITY兼容旧版本的 START_STICKY,行为不稳定最低版本 < Android 2.0 时使用

3.2 实战示例:START_REDELIVER_INTENT 实现断点续传

场景:文件下载时 Service 被系统杀死,重启后恢复之前的下载任务。

public class DownloadService extends Service {
    private static final String TAG = "DownloadService";
    private String mDownloadUrl; // 下载地址(需保留)
    private Thread mDownloadThread;
    private boolean isDownloading = false;

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate:下载Service创建");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand:启动下载任务");
        // 接收下载地址(首次启动或重启后恢复)
        if (intent != null) {
            mDownloadUrl = intent.getStringExtra("download_url");
            if (!isDownloading && mDownloadUrl != null) {
                startDownload();
            }
        } else {
            Log.d(TAG, "intent为null:可能是系统重启Service");
        }
        // 返回START_REDELIVER_INTENT,重启后保留最后一个Intent
        return START_REDELIVER_INTENT;
    }

    // 模拟文件下载
    private void startDownload() {
        isDownloading = true;
        mDownloadThread = new Thread(() -> {
            try {
                Log.d(TAG, "开始下载:" + mDownloadUrl);
                // 模拟下载进度(10秒完成)
                for (int i = 1; i <= 10; i++) {
                    Thread.sleep(1000);
                    Log.d(TAG, "下载进度:" + i + "0%");
                }
                Log.d(TAG, "下载完成!");
            } catch (InterruptedException e) {
                e.printStackTrace();
                Log.d(TAG, "下载中断,等待重启恢复");
            } finally {
                isDownloading = false;
                // 下载完成后停止Service
                stopSelf();
            }
        });
        mDownloadThread.start();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy:下载Service销毁");
        isDownloading = false;
        if (mDownloadThread != null) {
            mDownloadThread.interrupt();
        }
    }
}
关键说明:
  • 若下载过程中 Service 被系统杀死,系统会在内存充足时重启 Service,并将最后一个 Intent 重新传入onStartCommand()
  • 重启后,mDownloadUrl会重新赋值,startDownload()继续执行,实现 “断点续传” 效果;
  • 若返回START_STICKY,重启后 Intent 为 null,无法恢复下载地址,适合无需恢复任务的场景。

四、IntentService:无需手动管理线程的 Service

前面提到,Service 默认运行在主线程,耗时操作必须手动开线程,且需在任务完成后停止 Service,容易出错。而 IntentService 是 Service 的子类,内置了子线程和任务队列,能自动处理耗时任务,且任务完成后自动销毁,无需手动管理。

4.1 IntentService 的核心优势

  • 内置子线程:无需手动创建 Thread,直接在onHandleIntent()中做耗时操作;
  • 任务队列:多个启动请求会按顺序执行(串行),不会并发;
  • 自动销毁:所有任务执行完成后,自动调用stopSelf()销毁 Service;
  • 简化代码:无需处理线程管理、停止 Service 等逻辑。

4.2 实战示例:用 IntentService 实现多任务下载

public class MyIntentService extends IntentService {
    private static final String TAG = "IntentServiceTest";

    // 必须实现无参构造方法(否则报错)
    public MyIntentService() {
        // 父类构造方法:传入线程名称
        super("MyIntentServiceThread");
    }

    // 核心方法:运行在子线程,处理耗时任务
    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent != null) {
            // 接收任务参数
            String taskName = intent.getStringExtra("task_name");
            int taskId = intent.getIntExtra("task_id", 0);
            Log.d(TAG, "开始执行任务:" + taskName + "(ID:" + taskId + ")");

            // 模拟耗时操作(3秒)
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            Log.d(TAG, "任务执行完成:" + taskName + "(ID:" + taskId + ")");
        }
    }

    // Service创建时调用
    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate:IntentService创建");
    }

    // Service销毁时调用(所有任务完成后)
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy:IntentService销毁");
    }
}
  1. 注册 Service(Manifest):
<service
    android:name=".MyIntentService"
    android:exported="false" />
  1. 在 Activity 中启动多任务:
public class IntentServiceActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_intent_service);

        // 启动3个任务(串行执行)
        findViewById(R.id.btn_start_tasks).setOnClickListener(v -> {
            startTask("下载文件A", 1);
            startTask("下载文件B", 2);
            startTask("下载文件C", 3);
        });
    }

    // 启动任务
    private void startTask(String taskName, int taskId) {
        Intent intent = new Intent(this, MyIntentService.class);
        intent.putExtra("task_name", taskName);
        intent.putExtra("task_id", taskId);
        startService(intent);
    }
}
  1. 运行日志(观察串行执行和自动销毁):
D/IntentServiceTest: onCreate:IntentService创建
D/IntentServiceTest: 开始执行任务:下载文件A(ID:1)
D/IntentServiceTest: 任务执行完成:下载文件A(ID:1)
D/IntentServiceTest: 开始执行任务:下载文件B(ID:2)
D/IntentServiceTest: 任务执行完成:下载文件B(ID:2)
D/IntentServiceTest: 开始执行任务:下载文件C(ID:3)
D/IntentServiceTest: 任务执行完成:下载文件C(ID:3)
D/IntentServiceTest: onDestroy:IntentService销毁

4.3 注意事项

  • IntentService 的任务是串行的,若需并发执行,需改用 Service + 多线程;
  • Android 8.0 及以上,IntentService 的后台运行限制与普通 Service 一致(容易被系统杀死);
  • 若需与 Activity 通信,可通过广播、EventBus 等方式(后续详解)。

五、Service 通信进阶:跨组件、跨进程通信

Service 不仅能与启动它的 Activity 通信,还能与其他组件(如 Fragment、BroadcastReceiver)、甚至其他应用(跨进程)通信。核心通信方式有 3 种:Binder(同进程)、AIDL(跨进程)、广播(简单通信)。

5.1 同进程通信:Binder + 接口回调(进阶)

前面示例中,Activity 通过 Binder 获取 Service 实例后,只能主动调用 Service 的方法。若要实现 “Service 主动向 Activity 推送数据”(如下载进度实时更新),需结合接口回调。

示例 3:Service 主动推送下载进度到 Activity
  1. 定义回调接口(DownloadCallback.java):
// 下载进度回调接口
public interface DownloadCallback {
    void onProgressUpdate(int progress); // 进度更新
    void onDownloadSuccess(String filePath); // 下载成功
    void onDownloadFailed(String errorMsg); // 下载失败
}
  1. 改造 Service(支持注册回调):
public class ProgressService extends Service {
    private static final String TAG = "ProgressServiceTest";
    private LocalBinder mBinder = new LocalBinder();
    private Thread mDownloadThread;
    private boolean isDownloading = false;
    private DownloadCallback mCallback; // 回调接口实例

    // 注册回调(Activity调用)
    public void setDownloadCallback(DownloadCallback callback) {
        mCallback = callback;
    }

    // 解绑回调(避免内存泄漏)
    public void removeDownloadCallback() {
        mCallback = null;
    }

    public class LocalBinder extends Binder {
        ProgressService getService() {
            return ProgressService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    // 开始下载(供Activity调用)
    public void startDownload(String url) {
        if (!isDownloading) {
            isDownloading = true;
            mDownloadThread = new Thread(() -> {
                try {
                    // 模拟下载进度(0-100%,每500毫秒更新一次)
                    for (int progress = 0; progress <= 100; progress += 10) {
                        Thread.sleep(500);
                        // 主动回调进度(需切换到主线程,避免UI崩溃)
                        runOnUiThread(() -> {
                            if (mCallback != null) {
                                mCallback.onProgressUpdate(progress);
                            }
                        });
                    }
                    // 下载成功回调
                    runOnUiThread(() -> {
                        if (mCallback != null) {
                            mCallback.onDownloadSuccess("/sdcard/test.apk");
                        }
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    // 下载失败回调
                    runOnUiThread(() -> {
                        if (mCallback != null) {
                            mCallback.onDownloadFailed("下载中断");
                        }
                    });
                } finally {
                    isDownloading = false;
                }
            });
            mDownloadThread.start();
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        isDownloading = false;
        if (mDownloadThread != null) {
            mDownloadThread.interrupt();
        }
        removeDownloadCallback(); // 解绑回调
    }
}
  1. Activity 实现回调接口,接收进度:
public class ProgressActivity extends AppCompatActivity implements DownloadCallback {
    private ProgressService mService;
    private boolean mBound = false;
    private ProgressBar mProgressBar;
    private TextView mTvStatus;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_progress);
        mProgressBar = findViewById(R.id.progress_bar);
        mTvStatus = findViewById(R.id.tv_status);

        // 绑定Service
        ServiceConnection connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                ProgressService.LocalBinder binder = (ProgressService.LocalBinder) service;
                mService = binder.getService();
                mBound = true;
                // 注册回调
                mService.setDownloadCallback(ProgressActivity.this);
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                mBound = false;
            }
        };
        bindService(new Intent(this, ProgressService.class), connection, BIND_AUTO_CREATE);

        // 开始下载
        findViewById(R.id.btn_start_download).setOnClickListener(v -> {
            if (mBound) {
                mService.startDownload("https://example.com/test.apk");
                mTvStatus.setText("下载中...");
            }
        });
    }

    // 实现回调方法(进度更新)
    @Override
    public void onProgressUpdate(int progress) {
        mProgressBar.setProgress(progress);
        mTvStatus.setText("下载进度:" + progress + "%");
    }

    // 实现回调方法(下载成功)
    @Override
    public void onDownloadSuccess(String filePath) {
        mTvStatus.setText("下载成功:" + filePath);
    }

    // 实现回调方法(下载失败)
    @Override
    public void onDownloadFailed(String errorMsg) {
        mTvStatus.setText("下载失败:" + errorMsg);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mBound) {
            // 解绑回调和Service
            mService.removeDownloadCallback();
            unbindService(new ServiceConnection() {
                @Override
                public void onServiceConnected(ComponentName name, IBinder service) {}
                @Override
                public void onServiceDisconnected(ComponentName name) {}
            });
        }
    }
}

5.2 跨进程通信:AIDL(Android 接口定义语言)

当需要 “两个不同应用” 或 “同一应用的不同进程” 通信时,Binder 无法直接使用,需通过 AIDL 实现。AIDL 的核心是定义跨进程通信的接口,系统会自动生成通信相关的代码。

示例 4:用 AIDL 实现跨进程获取服务状态
步骤 1:创建 AIDL 文件(定义通信接口)
  1. main目录下新建aidl文件夹(与java同级);
  2. 在 aidl 文件夹下创建包名(与 Java 代码包名一致,如com.example.service);
  3. 新建 AIDL 文件(IMyAidlInterface.aidl):
// IMyAidlInterface.aidl
package com.example.service;

// 定义跨进程通信的接口
interface IMyAidlInterface {
    // 获取服务状态(示例方法)
    String getServiceStatus();
    // 启动服务任务
    void startServiceTask(String taskName);
}
  1. 点击 Android Studio 的 “Sync Project with Gradle Files”,系统会自动生成 Java 代码(在build/generated/aidl目录下)。
步骤 2:实现 AIDL 接口(Service 端)
public class AIDLService extends Service {
    private static final String TAG = "AIDLServiceTest";
    private boolean isTaskRunning = false;

    // 实现AIDL接口(自动生成的Stub类)
    private final IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {
        @Override
        public String getServiceStatus() throws RemoteException {
            // 跨进程调用时执行此方法(运行在Binder线程池)
            return isTaskRunning ? "服务正在执行任务" : "服务空闲";
        }

        @Override
        public void startServiceTask(String taskName) throws RemoteException {
            Log.d(TAG, "跨进程启动任务:" + taskName);
            isTaskRunning = true;
            // 模拟任务执行(3秒后结束)
            new Thread(() -> {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                isTaskRunning = false;
            }).start();
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        // 返回Stub实例(跨进程通信的桥梁)
        return mBinder;
    }
}
步骤 3:注册 Service(允许跨进程调用)
<service
    android:name=".AIDLService"
    android:exported="true"> <!-- exported=true:允许其他应用调用 -->
    <intent-filter>
        <!-- 隐式启动的Action(供其他应用匹配) -->
        <action android:name="com.example.service.AIDL_SERVICE" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</service>
步骤 4:客户端(另一个应用)调用 AIDL 服务
  1. 复制 AIDL 文件到客户端应用(包名必须与服务端一致);
  2. 同步 Gradle,生成 Java 代码;
  3. 客户端绑定服务并调用接口:
public class AIDLClientActivity extends AppCompatActivity {
    private IMyAidlInterface mAidlInterface;
    private boolean mBound = false;
    private TextView mTvStatus;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_aidl_client);
        mTvStatus = findViewById(R.id.tv_status);

        // 绑定服务端的AIDLService
        ServiceConnection connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                // 转换为AIDL接口实例
                mAidlInterface = IMyAidlInterface.Stub.asInterface(service);
                mBound = true;
                mTvStatus.setText("绑定成功");
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                mAidlInterface = null;
                mBound = false;
                mTvStatus.setText("绑定断开");
            }
        };

        Intent intent = new Intent();
        // 设置服务端的Action(与Manifest一致)
        intent.setAction("com.example.service.AIDL_SERVICE");
        // 跨应用绑定需设置包名(Android 11及以上必须)
        intent.setPackage("com.example.service"); // 服务端应用包名
        bindService(intent, connection, BIND_AUTO_CREATE);

        // 获取服务状态
        findViewById(R.id.btn_get_status).setOnClickListener(v -> {
            if (mBound && mAidlInterface != null) {
                try {
                    String status = mAidlInterface.getServiceStatus();
                    mTvStatus.setText("服务状态:" + status);
                } catch (RemoteException e) {
                    e.printStackTrace();
                    mTvStatus.setText("获取状态失败");
                }
            }
        });

        // 启动服务任务
        findViewById(R.id.btn_start_task).setOnClickListener(v -> {
            if (mBound && mAidlInterface != null) {
                try {
                    mAidlInterface.startServiceTask("跨进程测试任务");
                    mTvStatus.setText("任务已启动");
                } catch (RemoteException e) {
                    e.printStackTrace();
                    mTvStatus.setText("启动任务失败");
                }
            }
        });
    }
}

5.3 简单通信:广播(适合轻量数据传递)

若通信需求简单(如 Service 完成任务后通知 Activity),可使用广播,无需绑定机制,实现更简单。

示例 5:Service 通过广播通知 Activity 任务完成
  1. Service 发送广播:
public class BroadcastService extends Service {
    private static final String TAG = "BroadcastServiceTest";
    // 自定义广播Action
    public static final String ACTION_TASK_COMPLETE = "com.example.service.TASK_COMPLETE";

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 模拟耗时任务
        new Thread(() -> {
            try {
                Thread.sleep(3000);
                // 任务完成,发送广播
                Intent broadcastIntent = new Intent(ACTION_TASK_COMPLETE);
                broadcastIntent.putExtra("result", "任务执行成功");
                // 发送本地广播(仅本应用接收,更安全)
                LocalBroadcastManager.getInstance(this).sendBroadcast(broadcastIntent);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                stopSelf();
            }
        }).start();
        return START_NOT_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}
  1. Activity 接收广播:
public class BroadcastActivity extends AppCompatActivity {
    private LocalBroadcastManager mLocalBroadcastManager;
    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(BroadcastService.ACTION_TASK_COMPLETE)) {
                String result = intent.getStringExtra("result");
                Toast.makeText(context, result, Toast.LENGTH_SHORT).show();
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_broadcast);

        // 初始化本地广播管理器
        mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
        // 注册广播接收器
        IntentFilter filter = new IntentFilter();
        filter.addAction(BroadcastService.ACTION_TASK_COMPLETE);
        mLocalBroadcastManager.registerReceiver(mReceiver, filter);

        // 启动Service
        findViewById(R.id.btn_start_service).setOnClickListener(v -> {
            startService(new Intent(this, BroadcastService.class));
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 注销广播(避免内存泄漏)
        mLocalBroadcastManager.unregisterReceiver(mReceiver);
    }
}

5.4 3 种通信方式对比

通信方式适用场景优点缺点
Binder + 回调同进程、需双向通信效率高、灵活需绑定,代码稍复杂
AIDL跨进程、复杂通信支持多进程交互代码复杂,需处理 RemoteException
广播同 / 跨进程、简单通知无需绑定,实现简单效率低,不适合大量数据传递

六、Service 保活方案:避免被系统杀死的实战技巧

Android 系统为了优化内存,会在内存不足时回收后台 Service,这对需要持续运行的场景(如音乐播放、定位追踪)是致命的。但保活需 “合法合规”,避免使用违规方案(如双 Service 互相唤醒、后台弹出 Activity),否则会被应用商店拒审。

6.1 合法保活方案(推荐)

方案 1:前台服务(Foreground Service)

前台服务会在系统状态栏显示通知,优先级更高,几乎不会被系统回收(除非内存极度紧张)。适用于需要用户感知的后台任务(如音乐播放、导航)。

示例 6:实现前台服务
public class ForegroundService extends Service {
    private static final String TAG = "ForegroundServiceTest";
    private static final int NOTIFICATION_ID = 1001; // 通知ID(必须唯一)

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate:前台服务创建");
        // 启动前台服务(必须显示通知)
        startForeground(NOTIFICATION_ID, createNotification());
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 模拟后台任务(如音乐播放)
        new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(1000);
                    Log.d(TAG, "前台服务运行中...");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    break;
                }
            }
        }).start();
        return START_STICKY;
    }

    // 创建前台服务通知
    private Notification createNotification() {
        // Android 8.0及以上需创建通知渠道
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                    "foreground_channel", // 渠道ID
                    "前台服务通知", // 渠道名称
                    NotificationManager.IMPORTANCE_DEFAULT // 重要性
            );
            // 注册通知渠道
            NotificationManager manager = getSystemService(NotificationManager.class);
            manager.createNotificationChannel(channel);
        }

        // 构建通知
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "foreground_channel")
                .setSmallIcon(R.mipmap.ic_launcher) // 通知图标(必须)
                .setContentTitle("前台服务") // 通知标题
                .setContentText("服务正在后台运行...") // 通知内容
                .setPriority(NotificationCompat.PRIORITY_DEFAULT);

        // 设置通知点击事件(可选)
        Intent intent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(
                this, 0, intent, PendingIntent.FLAG_IMMUTABLE
        );
        builder.setContentIntent(pendingIntent);

        return builder.build();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy:前台服务销毁");
        // 停止前台服务(移除通知)
        stopForeground(true);
    }
}
关键说明:
  • Android 8.0 及以上必须创建通知渠道,否则通知无法显示,前台服务启动失败;
  • 前台服务的通知不能手动取消(除非服务停止),需通过stopForeground(true)移除;
  • 需在 Manifest 中添加前台服务权限(Android 9.0 及以上):
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
方案 2:WorkManager(替代后台 Service,Android 8.0 + 推荐)

Android 8.0(API 26)后,系统对后台 Service 限制加强(后台 Service 容易被杀死),Google 推荐用 WorkManager 替代 “非即时性后台任务”(如数据同步、文件备份、周期性任务)。

WorkManager 的优势:

  • 兼容所有 Android 版本(API 14+);
  • 自动适配系统限制,在后台安全执行任务;
  • 支持周期性任务、延迟任务、网络条件触发等;
  • 任务可持久化,设备重启后自动恢复。
示例 7:用 WorkManager 实现周期性数据同步
  1. 添加依赖(AndroidX):
implementation 'androidx.work:work-runtime:2.8.1'
  1. 创建 Worker(任务执行类):
public class SyncWorker extends Worker {
    private static final String TAG = "SyncWorkerTest";

    public SyncWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }

    // 核心方法:执行后台任务(运行在子线程)
    @NonNull
    @Override
    public Result doWork() {
        Log.d(TAG, "开始数据同步...");
        try {
            // 模拟数据同步(2秒)
            Thread.sleep(2000);
            Log.d(TAG, "数据同步完成!");
            return Result.success(); // 任务成功
        } catch (InterruptedException e) {
            e.printStackTrace();
            return Result.retry(); // 任务重试(可设置重试策略)
        }
    }
}
  1. 在 Activity 中调度任务:
public class WorkManagerActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_work_manager);

        // 调度周期性任务(每15分钟执行一次,最小周期为15分钟)
        findViewById(R.id.btn_schedule_sync).setOnClickListener(v -> {
            // 1. 配置任务参数(周期性任务)
            PeriodicWorkRequest syncRequest = new PeriodicWorkRequest.Builder(
                    SyncWorker.class,
                    15, TimeUnit.MINUTES // 周期15分钟
            )
                    // 设置约束条件(如网络可用时执行)
                    .setConstraints(new Constraints.Builder()
                            .setRequiredNetworkType(NetworkType.CONNECTED)
                            .build())
                    .build();

            // 2. 提交任务(唯一名称,重复提交会替换)
            WorkManager.getInstance(this)
                    .enqueueUniquePeriodicWork(
                            "data_sync_work",
                            ExistingPeriodicWorkPolicy.REPLACE, // 重复时替换
                            syncRequest
                    );

            Toast.makeText(this, "任务调度成功", Toast.LENGTH_SHORT).show();
        });

        // 取消任务
        findViewById(R.id.btn_cancel_sync).setOnClickListener(v -> {
            WorkManager.getInstance(this).cancelUniqueWork("data_sync_work");
            Toast.makeText(this, "任务已取消", Toast.LENGTH_SHORT).show();
        });
    }
}

6.2 灰色保活方案(谨慎使用)

以下方案适用于特殊场景(如企业级应用、工具类应用),普通应用不建议使用,可能影响用户体验或被应用商店拒审:

  • 方案 3:1 像素 Activity 唤醒:Service 被杀死前,启动一个透明的 1 像素 Activity,让应用回到前台,避免被回收;
  • 方案 4:广播唤醒:注册系统广播(如屏幕亮灭、网络变化),收到广播后重启 Service;
  • 方案 5:双进程守护:创建两个进程的 Service,互相监听,一个被杀死后,另一个重启它。

七、Service 常见坑点与避坑指南(实战经验总结)

7.1 坑点 1:Service 主线程耗时导致 ANR

  • 现象:在 Service 的 onCreate ()、onStartCommand () 中做耗时操作(如网络请求、文件读写),导致应用无响应(ANR);
  • 原因:Service 默认运行在主线程,主线程阻塞超过 5 秒会触发 ANR;
  • 解决方案:
    1. 所有耗时操作必须放在子线程(Thread、AsyncTask、Coroutine);
    2. 推荐用 IntentService 或 WorkManager,无需手动管理线程;
    3. 示例(正确的耗时操作):
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    // 正确:耗时操作放子线程
    new Thread(() -> {
        // 执行网络请求、文件下载等操作
    }).start();
    return START_STICKY;
}

7.2 坑点 2:绑定 Service 后未解绑导致内存泄漏

  • 现象:Activity 绑定 Service 后,未调用 unbindService () 就销毁,导致 Service 无法回收,进而造成内存泄漏;
  • 原因:绑定后,Service 会持有 Activity 的引用,Activity 销毁时引用未释放;
  • 解决方案:
    1. 在 Activity 的 onDestroy () 中强制解绑(判断是否已绑定);
    2. 避免在非 Activity 组件(如 Application)中绑定 Service;
    3. 示例(正确的解绑逻辑):
@Override
protected void onDestroy() {
    super.onDestroy();
    if (mBound) {
        unbindService(mConnection);
        mBound = false;
    }
}

7.3 坑点 3:Android 8.0 + 后台 Service 容易被杀死

  • 现象:Android 8.0 及以上,应用退到后台后,Service 很快被系统杀死;
  • 原因:Android 8.0 引入 “后台服务限制”,后台应用的 Service 会被系统限制运行;
  • 解决方案:
    1. 即时性任务:用前台服务(Foreground Service);
    2. 非即时性任务:用 WorkManager 替代;
    3. 避免在后台 Service 中执行长时间运行的任务。

7.4 坑点 4:IntentService 并发任务执行失败

  • 现象:向 IntentService 发送多个任务,期望并发执行,但实际是串行执行;
  • 原因:IntentService 的任务队列是串行的,多个任务按顺序执行,无法并发;
  • 解决方案:
    1. 需并发执行的任务:改用 Service + 多线程(如线程池);
    2. 示例(Service + 线程池实现并发):
public class ConcurrentService extends Service {
    // 线程池(3个核心线程)
    private ExecutorService mExecutor = Executors.newFixedThreadPool(3);

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        String taskName = intent.getStringExtra("task_name");
        // 提交任务到线程池(并发执行)
        mExecutor.submit(() -> {
            Log.d(TAG, "执行任务:" + taskName + ",线程:" + Thread.currentThread().getName());
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        return START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mExecutor.shutdown(); // 关闭线程池
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

7.5 坑点 5:跨进程通信时抛出 RemoteException

  • 现象:AIDL 跨进程通信时,调用接口方法抛出 RemoteException;
  • 原因:跨进程通信可能因网络异常、服务端崩溃等导致通信失败;
  • 解决方案:
    1. 所有 AIDL 接口调用必须放在 try-catch 块中;
    2. 通信前检查服务是否已绑定;
    3. 示例:
try {
    if (mAidlInterface != null) {
        String status = mAidlInterface.getServiceStatus();
    }
} catch (RemoteException e) {
    e.printStackTrace();
    Toast.makeText(this, "通信失败,请重试", Toast.LENGTH_SHORT).show();
}

八、总结:Service 核心知识点图谱

到这里,Service 的核心内容已经全部讲完,我们用一张图谱梳理重点:

Service核心知识点
├── 基础认知:后台任务容器、与Thread/IntentService的区别
├── 生命周期:启动型(startService)、绑定型(bindService)、混合启动
├── onStartCommand返回值:4种类型(决定被杀死后的行为)
├── 关键子类:IntentService(自动管理线程和销毁)
├── 通信方式:Binder+回调(同进程)、AIDL(跨进程)、广播(简单通信)
├── 保活方案:前台服务(合法)、WorkManager(Android 8.0+推荐)、灰色方案(谨慎使用)
├── 避坑指南:ANR、内存泄漏、后台限制、并发问题、跨进程异常

其实 Service 的学习关键是 “理解生命周期 + 区分使用场景”—— 启动型 Service 适合独立后台任务,绑定型 Service 适合组件通信,IntentService 适合异步单次任务,WorkManager 适合非即时性后台任务。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值