[toc]
01. Service介绍
1.1 Service是什么
-
什么是 Service:
Service是一个不需要和用户交互,运行在后台的 Android 组件。负责执行与用户界面无关的任务。比如后台播放音乐和后台下载。
-
如何理解“不与用户交互”和“后台运行”:
-
不与用户交互:指服务无需显示 UI 界面,也不需要直接响应用户的点击或输入事件。
-
后台运行:指服务在后台任务栈中运行,优先级较低,可能被系统回收。
-
1.2 分类
它分为两种类型,启动服务和绑定服务。
启动服务是指其它应用组件通过 startService() 启动一个服务,一旦启动,服务将在后台运行,直到主动调用 stopService() 停止。否则即使启动服务的组件(如 Activity)被销毁,服务也会继续运行,不受影响。
示例:音乐播放器可以在后台播放,即使用户关闭了播放器的界面,音乐依然在后台播放。
绑定服务通过 bindService() 启动。绑定服务的生命周期与绑定它的组件相关联。当所有绑定到服务的组件都解绑后,服务就会销毁。绑定服务允许组件与服务交互,甚至可以通过 IPC 进行跨进程通信。
- 启动服务是一对一的关系,但是即使这个一不stop,即使死了,也不影响另一个一。
- 绑定服务是多对一的关系,多中只要有一个存在,那么这个一就存在。
1.3 服务运行在哪
服务通常运行在主线程(UI线程)中,所以说它不能做耗时操作。
但是我们可以让其运行在一个独立的线程中,此时其就成为一个独立的服务进程。需要通过下面指定
<service
android:name=".MyService"
android:process=":myservice_process"/>
02. Service生命周期
两种不同类型的bind生命周期不同。
通过 startService,Service 会经历 onCreate 到 onStartCommand ,然后处于运行状态,stopService 的时候调用 onDestroy 方法。
通过 bindService,Service 会运行 onCreate ,然后是调用 onBind , 这个时候调用者和 Service 绑定在一起。当所有调用者都解绑了,Srevice 就会调用 onUnbind -> onDestroyed 方法。
03. IntentService
3.1 什么是IntentService
Service 默认运行在应用的主线程中,因此如果你在 Service 中执行耗时的任务,会导致主线程阻塞,最终可能引发 "Application Not Responding"(ANR)。
IntentService 是自带独立工作线程的Service。这些耗时的任务会在工作线程中执行。本质是一个Handle工作线程
3.2 IntentService原理
其在OnCreate时会启动HandlerThread工作线程,其内部包含Looper和MsgQueue。
当组件调用startService(intent1)来启动service时,在onStartCommand() 生命周期中会将每个 Intent 转换为一个消息 (Message),并放入消息队列中。
任务线程在消息循环中取出消息任务,然后进行消息任务异步的处理。
当消息队列已空,所有任务都处理完毕时,IntentService 会自动调用 stopSelf(),停止循环,并最终停止服务。
3.3 IntentService案例演示
我们有一个 IntentService,用于执行两个独立的后台任务,比如从服务器下载数据。这两个任务通过传递不同的 Intent 来启动。
-
主线程启动
IntentService:- 应用中的
Activity调用startService(intent1)来启动IntentService。此时,Intent对象intent1传递给IntentService,包含要执行的第一个任务(如从服务器下载文件1)。 - 紧接着,主线程又调用了
startService(intent2),传递了第二个Intent对象intent2,代表另一个任务(如下载文件2)。
// 主线程中,用户点击按钮,启动服务 Intent intent1 = new Intent(this, MyIntentService.class); intent1.putExtra("task", "download_file1"); startService(intent1); Intent intent2 = new Intent(this, MyIntentService.class); intent2.putExtra("task", "download_file2"); startService(intent2); - 应用中的
-
IntentService开始工作(onCreate):- 当
IntentService第一次启动时,onCreate()被调用。此时,IntentService会创建一个HandlerThread,该线程会自动创建一个Looper,使工作线程能够持续循环处理消息。 - 然后,
Handler被创建,并与该Looper关联,用于接收传递进来的Intent(作为消息的一部分)。
@Override public void onCreate() { super.onCreate(); HandlerThread thread = new HandlerThread("IntentServiceThread"); thread.start(); mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); } - 当
-
Intent 被添加到消息队列中:
startService(intent1)和startService(intent2)被调用后,IntentService的onStartCommand()方法会将每个Intent转换为一个消息 (Message),并放入消息队列中。Handler会处理这些消息。
@Override public int onStartCommand(Intent intent, int flags, int startId) { Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; msg.obj = intent; mServiceHandler.sendMessage(msg); return START_NOT_STICKY; }动态过程:
intent1(下载文件1的任务)被封装为消息1,放入消息队列中。intent2(下载文件2的任务)被封装为消息2,放入消息队列中。
-
Looper循环读取消息队列:Looper在工作线程中保持循环,它依次从消息队列中读取消息(每个Intent),并通过Handler传递给onHandleIntent()方法处理。- 第一个消息
intent1被读取,调用onHandleIntent()方法,开始处理任务1(下载文件1)。
@Override protected void onHandleIntent(Intent intent) { if (intent != null) { String task = intent.getStringExtra("task"); if (task.equals("download_file1")) { // 模拟下载文件1的任务 downloadFile("file1_url"); } else if (task.equals("download_file2")) { // 模拟下载文件2的任务 downloadFile("file2_url"); } } } -
所有任务完成后,自动停止服务:
- 当
Looper检测到消息队列已空,所有任务都处理完毕时,IntentService会自动调用stopSelf(),停止循环,并最终停止服务。
- 当