攻略大全
1. 粘贴攻略
1.1 Service概述
- 定义:Service是Android四大组件之一, 属于 计算型组件
- 作用:提供 需在后台长期运行的服务,如:复杂计算、音乐播放、下载等
- 特点:无用户界面、在后台运行、生命周期长
Service是一个可以在后台执行长时间运行操作而没有用户界面的组件。
Service是Android系统中的四大组件之一,主要有两个应用场景:后台运行和跨进程访问。
Service可以在后台执行长时间运行操作而不提供用户界面,除非系统必须回收内存资源,否则系统不会停止或销毁服务。服务可由其他应用组件启动,即使用户切换到其他应用,服务仍将在后台继续运行。
此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。需要注意的是,Service默认是在主线程执行操作的,可能会因为执行耗时操作而导致ANR。
Service可以分为以下三种形式:
-
启动
当应用组件通过调用 startService() 启动服务时,服务即处于“启动”状态。一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响。 已启动的服务通常是执行单一操作,而且不会将结果返回给调用方
-
绑定
当应用组件通过调用 bindService() 绑定到服务时,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。多个组件可以同时绑定服务,服务只会在组件与其绑定时运行,一旦该服务与所有组件之间的绑定全部取消,系统便会销毁它。
-
启动且绑定
服务既可以是启动服务,也允许绑定。此时需要同时实现以下回调方法:onStartCommand()和 onBind()。系统不会在所有客户端都取消绑定时销毁服务。为此,必须通过调用 stopSelf() 或 stopService() 显式停止服务。 要使用服务,必须继承Service类(或者Service类的现有子类),在子类中重写某些回调方法,以处理服务生命周期的某些关键方面并提供一种机制将组件绑定到服务。
1.2 Service类型
1.3 Service与Thread
一般会将 Service 和 Thread联合着用,即在Service中再创建一个子线程(工作线程)去处理耗时操作逻辑,如下代码:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//新建工作线程
new Thread(new Runnable() {
@Override
public void run() {
// 开始执行后台任务
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
class MyBinder extends Binder {
public void serviceConnectActivity() {
//新建工作线程
new Thread(new Runnable() {
@Override
public void run() {
// 执行具体的下载任务
}
}).start();
}
}
1.4 Service和IntentService
-
Service 这是所有服务的父类。扩展此类时,如果要执行耗时操作,必须创建一个用于执行操作的新线程,因为默认情况下服务将运行于UI线程。
-
IntentService 这是 Service 的子类,它使用工作线程逐一处理所有启动请求。如果应用不需要同时处理多个请求,这是最好的选择。IntentService只需实现构造函数与 onHandleIntent() 方法即可,onHandleIntent()方法会接收每个启动请求的 Intent。由于大多数启动服务都不必同时处理多个请求,因此使用 IntentService 类实现服务也许是最好的选择。
1.4.1 关于IntentService
IntentService 执行以下操作:
- 创建默认的工作线程,用于在应用的主线程外执行传递给 onStartCommand() 的所有 Intent。
- 创建工作队列,用于将 Intent 逐一传递给 onHandleIntent() 实现,这样就不必担心多线程问题
- 在处理完所有启动请求后停止服务,因此不必自己调用 stopSelf()方法。
- 提供 onBind() 的默认实现(返回 null)。
- 提供 onStartCommand() 的默认实现,可将 Intent 依次发送到工作队列和 onHandleIntent()。
因此,只需实现构造函数与 onHandleIntent() 方法即可。
1.4.1.1 IntentService的工作原理 & 源码工作流程
1.4.1.2 特别注意
若启动IntentService 多次,那么 每个耗时操作 则 以队列的方式 在 IntentService的 onHandleIntent回调方法中依次执行,执行完自动结束。
1.4.1.3 源码分析
问题1:IntentService如何单独开启1个新的工作线程
@Override
public void onCreate() {
super.onCreate();
// 1. 通过实例化andlerThread新建线程 & 启动;故 使用IntentService时,不需额外新建线程
// HandlerThread继承自Thread,内部封装了 Looper
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
// 2. 获得工作线程的 Looper & 维护自己的工作队列
mServiceLooper = thread.getLooper();
// 3. 新建mServiceHandler & 绑定上述获得Looper
// 新建的Handler 属于工作线程 ->>分析1
mServiceHandler = new ServiceHandler(mServiceLooper);
}
/**
* 分析1:ServiceHandler源码分析
**/
private final class ServiceHandler extends Handler {
// 构造函数
public ServiceHandler(Looper looper) {
super(looper);
}
// IntentService的handleMessage()把接收的消息交给onHandleIntent()处理
@Override
public void handleMessage(Message msg) {
// onHandleIntent 方法在工作线程中执行
// onHandleIntent() = 抽象方法,使用时需重写 ->>分析2
onHandleIntent((Intent)msg.obj);
// 执行完调用 stopSelf() 结束服务
stopSelf(msg.arg1);
}
}
/**
* 分析2: onHandleIntent()源码分析
* onHandleIntent() = 抽象方法,使用时需重写
**/
@WorkerThread
protected abstract void onHandleIntent(Intent intent);
问题2:IntentService 如何通过onStartCommand() 将Intent 传递给服务 & 依次插入到工作队列中
/**
* onStartCommand()源码分析
* onHandleIntent() = 抽象方法,使用时需重写
**/
public int onStartCommand(Intent intent, int flags, int startId) {
// 调用onStart()->>分析1
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
/**
* 分析1:onStart(intent, startId)
**/
public void onStart(Intent intent, int startId) {
// 1. 获得ServiceHandler消息的引用
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
// 2. 把 Intent参数 包装到 message 的 obj 发送消息中,这里的Intent = 启动服务时startService(Intent) 里传入的 Intent
msg.obj = intent;
// 3. 发送消息,即 添加到消息队列里
mServiceHandler.sendMessage(msg);
}
源码总结 从上面源码可看出:IntentService本质 = Handler + HandlerThread:
- 通过HandlerThread 单独开启1个工作线程:IntentService
- 创建1个内部 Handler :ServiceHandler
- 绑定 ServiceHandler 与 IntentService
- 通过 onStartCommand() 传递服务intent 到ServiceHandler 、依次插入Intent到工作队列中 & 逐个发送给 onHandleIntent()
- 通过onHandleIntent() 依次处理所有Intent对象所对应的任务
因此我们通过复写onHandleIntent()并在里面根据Intent的不同进行不同线程操作即可。
1.4.1.4 注意事项
有两个注意事项需要关注的:
- 工作任务队列 = 顺序执行
- 不建议通过 bindService() 启动 IntentService
注意事项1:工作任务队列 = 顺序执行
即 若一个任务正在IntentService中执行,此时你再发送1个新的任务请求,这个新的任务会一直等待直到前面一个任务执行完毕后才开始执行。
原因:
- 由于onCreate()只会调用一次 = 只会创建1个工作线程;
- 当多次调用 startService(Intent)时(即 onStartCommand()也会调用多次),其实不会创建新的工作线程,只是把消息加入消息队列中 & 等待执行。
- 所以,多次启动 IntentService 会按顺序执行事件。
若服务停止,则会清除消息队列中的消息,后续的事件不执行。
注意事项2:不建议通过 bindService() 启动 IntentService
原因:
// 在IntentService中,onBind()默认返回null
@Override
public IBinder onBind(Intent intent) {
return null;
}
采用 bindService()启动 IntentService的生命周期如下onCreate() ->> onBind() ->> onunbind()->> onDestory()
。
即,并不会调用onStart() 或 onStartcommand(),故不会将消息发送到消息队列,那么onHandleIntent()将不会回调,即无法实现多线程的操作。
此时,你应该使用Service,而不是IntentService。
1.4.1.5 与Service的区别
1.4.1.6 与线程的区别
1.5 Service生命周期
1.5.1 生命周期常用方法
在Service的生命周期里,常用的有:
- 4个手动调用的方法
- 5个自动调用的方法
1.5.2 生命周期方法具体介绍
1.5.3 常见的生命周期使用
1.6 stopService()和stopSelf()
两者皆可停止服务。
Android官方不推荐使用stopService来关闭服务,因为它就像强制结束进程一样是种不负责任的做法,这种时候服务往往不能正常的退出。Android推荐使用 Service 的 stopSelf 这个方法来关闭服务,也就是服务自己关闭自己,这样就能保证服务里所有的流程和状态在关闭时得到处理和保存。
而从外部关闭Service应当像从外部关闭Activity一样,通过广播或者其他事件工具通知Service或Activity,然后由Service或Activity自己去调用 stopSelf 或是 finish 去关闭自己。
Service的stopself方法的功能是:当完成所有功能之后,将service停掉,而不是等着系统回收。同样finish方法,是当系统执行完onCreate方法之后,调用onDestory方法销毁。
在onStartCommand方法里面调用stopSelf方法时,不会马上停止,而是onStartCommond方法执行结束才会停止。
调用stopSelf方法之后,service会执行onDestory方法。
如果onStartCommand中启动一个线程,调用stopSelf,线程不会被杀死。
1.7 前台Service
前台Service和普通Service最大的区别就在于,它会一直有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更加详细的信息,非常类似于通知的效果。
1.8 声明service
如同其他组件一样,想要使用Service,必须在清单文件中对其进行声明。
1.8.1 属性声明
-
name:服务类的完全限定名。
-
exported:设置其他应用程序的组件是否可以调用本服务或与其交互,如果可以,则为“true”。当值为“false”时,只有同一个应用程序或具有相同用户ID的应用程序的组件可以启动该服务或绑定到该服务。该属性的默认值取决于服务是否包含Intent filters。没有任何过滤器意味着它只能通过指定其确切的类名来调用,这意味着该服务仅用于应用程序内部使用(因为其他人不知道类名)。所以在这种情况下,默认值为“false”。 另一方面,如果存在至少一个过滤器,意味着该服务打算供外部使用,因此默认值为“true”。
-
description:对服务进行描述,属性值应为对字符串资源的引用,以便进行本地化
-
directBootAware:设置是否可以在用户解锁设备之前运行,默认值为“false”
-
enabled:设置是否可以由系统来实例化服务。< application >元素有自己的enabled属性,适用于包括服务在内的所有应用程序组件。要启用服务,< application >和< service >属性必须都为“true”(默认情况下都为true)。如果其中一个是“false”,则服务被禁用
-
icon:服务的图标,属性值应是对drawable资源的引用。如果未设置,则将使用应用程序图标。
-
isolatedProcess:设置该服务是否作为一个单独的进程运行,如果设置为true,此服务将在与系统其余部分隔离的特殊进程下运行,并且没有自己的权限,与它唯一的通信是通过服务API(绑定和启动)。
-
label:可以向用户显示的服务的名称,属性值应是对字符串资源的引用。
-
permission:设定组件必须具有的权限,得以启动服务或绑定服务。如果startService(),bindService()或stopService()的调用者没有被授予此权限,则该方法将不会工作,并且Intent对象不会传递到服务中。
-
process:用来运行服务的进程的名称。通常,应用程序的所有组件都运行在应用程序创建的默认进程中,它与应用程序包名具有相同的名称。 < application >元素的process属性可以为所有组件设置不同的默认值,但组件可以使用自己的进程属性覆盖默认值,从而允许跨多个进程扩展应用程序。
为了确保应用的安全性,最好始终使用显式 Intent 启动或绑定 Service,且不要为服务声明 Intent 过滤器。 启动哪个服务存在一定的不确定性,而如果对这种不确定性的考量非常有必要,则可为服务提供 Intent 过滤器并从 Intent 中排除相应的组件名称,但随后必须使用 setPackage() 方法设置 Intent 的软件包,这样可以充分消除目标服务的不确定性 此外,还可以通过添加exported属性并将其设置为 "false",确保服务仅适用于本应用。这可以有效阻止其他应用启动本应用内的服务,即便在使用显式 Intent 时也是如此。
1.9 绑定Service
应用组件(客户端)通过调用 bindService() 绑定到服务,绑定是异步的,系统随后调用服务的 onBind() 方法,该方法返回用于与服务交互的 IBinder。要接收 IBinder,客户端必须提供一个 ServiceConnection 实例用于监控与服务的连接,并将其传递给 bindService()。当 Android 系统创建了客户端与服务之间的连接时,会回调ServiceConnection 对象的onServiceConnected()方法,向客户端传递用来与服务通信的 IBinder
多个客户端可同时连接到一个服务。不过,只有在第一个客户端绑定时,系统才会调用服务的 onBind() 方法来检索 IBinder。系统随后无需再次调用 onBind(),便可将同一 IBinder 传递至其他绑定的客户端。当所有客户端都取消了与服务的绑定后,系统会将服务销毁(除非 startService() 也启动了该服务)
另外,只有 Activity、服务和内容提供者可以绑定到服务,无法从广播接收器绑定到服务
可以通过以下三种方法定义IBinder接口:
-
扩展 Binder 类
如果服务是供本应用专用,并且运行在与客户端相同的进程中,则应通过扩展 Binder 类并从 onBind() 返回它的一个实例来创建接口。客户端收到 Binder 后,可利用它直接访问 Service 中可用的公共方法。
-
使用 Messenger
如需让接口跨不同的进程工作,则可使用 Messenger 为服务创建接口。服务可以这种方式定义对应于不同类型 Message 对象的 Handler。此 Handler 是 Messenger 的基础,后者随后可与客户端分享一个 IBinder,从而让客户端能利用 Message 对象向服务发送命令。此外,客户端还可定义自有 Messenger,以便服务回传消息。这是执行进程间通信 (IPC) 的最简单方法,因为 Messenger 会在单一线程中创建包含所有请求的队列,这样就不必对服务进行线程安全设计。
-
使用 AIDL
AIDL(Android 接口定义语言)执行所有将对象分解成原语的工作,操作系统可以识别这些原语并将它们编组到各进程中,以执行 IPC。 之前采用 Messenger 的方法实际上是以 AIDL 作为其底层结构。 如上所述,Messenger 会在单一线程中创建包含所有客户端请求的队列,以便服务一次接收一个请求。 不过,如果想让服务同时处理多个请求,则可直接使用 AIDL。 在此情况下,服务必须具备多线程处理能力,并采用线程安全式设计。如需直接使用 AIDL,必须创建一个定义编程接口的 .aidl 文件。Android SDK 工具利用该文件生成一个实现接口并处理 IPC 的抽象类,随后可在服务内对其进行扩展。
1.9.1 绑定时机
- 如果只需要在 Activity 可见时与服务交互,则应在 onStart() 期间绑定,在 onStop() 期间取消绑定
- 如果希望 Activity 在后台停止运行状态下仍可接收响应,则可在 onCreate() 期间绑定,在 onDestroy() 期间取消绑定。这意味着 Activity 在其整个运行过程中(包括后台运行期间)都需要使用此服务
- 通常情况下,切勿在 Activity 的 onResume() 和 onPause() 期间绑定和取消绑定,因为每一次生命周期转换都会发生这些回调,应该使发生在这些转换期间的处理保持在最低水平。假设有两个Activity需要绑定到同一服务,从Activity A跳转到Activity B,这个过程中会依次执行A-onPause,B-onCreate,B-onStart,B-onResume,A-onStop。这样系统会在A-onPause的时候销毁服务,又在B-onResume的时候重建服务。当Activity B回退到Activity A时,会依次执行B-onPause,A-onRestart,A-onStart,A-onResume,B-onStop,B-onDestroy。此时,系统会在B-onPause时销毁服务,又在A-onResume时重建服务。这样就造成了多次的销毁与重建,因此需要选定好绑定服务与取消绑定服务的时机
1.10 远程服务Service
1.10.1 远程服务与本地服务的区别
- 远程服务与本地服务最大的区别是:远程Service与调用者不在同一个进程里(即远程Service是运行在另外一个进程);而本地服务则是与调用者运行在同一个进程里
- 二者区别的详细区别如下图:
1.10.2 使用场景
多个应用程序共享同一个后台服务(远程服务),即一个远程Service与多个应用程序的组件(四大组件)进行跨进程通信。
1.10.3 具体使用
为了让远程Service与多个应用程序的组件(四大组件)进行跨进程通信(IPC),需要使用AIDL。
在多进程通信中,存在两个进程角色(以最简单的为例):服务端和客户端
以下是两个进程角色的具体使用步骤:
服务端(Service)
步骤1:新建定义AIDL文件,并声明该服务需要向客户端提供的接口
步骤2:在Service子类中实现AIDL中定义的接口方法,并定义生命周期的方法(onCreat、onBind()、blabla)
步骤3:在AndroidMainfest.xml中注册服务 & 声明为远程服务
客户端(Client)
步骤1:拷贝服务端的AIDL文件到目录下
步骤2:使用Stub.asInterface接口获取服务器的Binder,根据需要调用服务提供的接口方法
步骤3:通过Intent指定服务端的服务名称和所在包,绑定远程Service
1.10.4 具体实例
1.10.4.1 服务端(Service)
步骤1. 新建一个AIDL文件
步骤2. 在新建AIDL文件里定义Service需要与Activity进行通信的内容(方法),并进行编译(Make Project)
步骤3:在Service子类中实现AIDL中定义的接口方法,并定义生命周期的方法(onCreat、onBind()、blabla)
MyService.java
// 实例化AIDL的Stub类(Binder的子类)
AIDL_Service1.Stub mBinder = new AIDL_Service1.Stub() {
//重写接口里定义的方法
@Override
public void AIDL_Service() throws RemoteException {
System.out.println("客户端通过AIDL与远程后台成功通信");
}
};
//重写与Service生命周期的相关方法`
@Override
public void onCreate() {
super.onCreate();
System.out.println("执行了onCreat()");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("执行了onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
System.out.println("执行了onDestory()");
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
System.out.println("执行了onBind()");
//在onBind()返回继承自Binder的Stub类型的Binder,非常重要
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
System.out.println("执行了onUnbind()");
return super.onUnbind(intent);
}
}
步骤4:在AndroidMainfest.xml中注册服务 & 声明为远程服务
<service
android:name=".MyService"
android:process=":remote" //将本地服务设置成远程服务
android:exported="true" //设置可被其他进程调用
>
//该Service可以响应带有scut.carson_ho.service_server.AIDL_Service1这个action的Intent。
//此处Intent的action必须写成“服务器端包名.aidl文件名”
<intent-filter>
<action android:name="scut.carson_ho.service_server.AIDL_Service1"/>
</intent-filter>
</service>
至此,服务器端(远程Service)已经完成了。
1.10.4.2 客户端(Client)
步骤1:将服务端的AIDL文件所在的包复制到客户端目录下(Project/app/src/main),并进行编译。 记得要原封不动地复制!!什么都不要改!
步骤2:在主布局文件定义“绑定服务”的按钮
MainActivity.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="scut.carson_ho.service_client.MainActivity">
<Button
android:layout_centerInParent="true"`
android:id="@+id/bind_service"`
android:layout_width="match_parent"`
android:layout_height="wrap_content"`
android:text="绑定服务"/>
</RelativeLayout>
步骤3:在MainActivity.java里
- 使用Stub.asInterface接口获取服务器的Binder;
- 通过Intent指定服务端的服务名称和所在包,进行Service绑定;
- 根据需要调用服务提供的接口方法。
MainActivity.java
public class MainActivity extends AppCompatActivity {
private Button bindService;
//定义aidl接口变量
private AIDL_Service1 mAIDL_Service;
//创建ServiceConnection的匿名类
private ServiceConnection connection = new ServiceConnection() {
//重写onServiceConnected()方法和onServiceDisconnected()方法
//在Activity与Service建立关联和解除关联的时候调用
@Override
public void onServiceDisconnected(ComponentName name) {
}
//在Activity与Service建立关联时调用
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//使用AIDLService1.Stub.asInterface()方法获取服务器端返回的IBinder对象
//将IBinder对象传换成了mAIDL_Service接口对象
mAIDL_Service = AIDL_Service1.Stub.asInterface(service);
try {
//通过该对象调用在MyAIDLService.aidl文件中定义的接口方法,从而实现跨进程通信
mAIDL_Service.AIDL_Service();
} catch (RemoteException e) {
e.printStackTrace();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindService = (Button) findViewById(R.id.bind_service);
//设置绑定服务的按钮
bindService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//通过Intent指定服务端的服务名称和所在包,与远程Service进行绑定
//参数与服务器端的action要一致,即"服务器包名.aidl接口文件名"
Intent intent = new Intent("scut.carson_ho.service_server.AIDL_Service1");
//Android5.0后无法只通过隐式Intent绑定远程Service
//需要通过setPackage()方法指定包名
intent.setPackage("scut.carson_ho.service_server");
//绑定服务,传入intent和ServiceConnection对象
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
});
}
}
2. 造火箭攻略
2.1 Service启动流程简述
2.2 Service启动流程图示
2.3 Service绑定流程简述
2.4 Service绑定流程图示
3. 拧螺丝攻略
3.1 Service启动过程
3.1.1 ContextImpl到AMS的调用过程
时序图如下下所示:
startService方法,在ContextWrapper中实现,代码如下所示:
在startService方法中会调用mBase的startService方法,Context类型的mBase具体指向的就是ContextImpl。
查看ContextImpl的startService方法,代码如下所示:
在startService方法中会返回startServiceCommon方法,在startServiceCommon方法中会在注释1处调用AMS的代理IActivityManager的startService方法,最终调用的是AMS的startService方法。
3.1.2 ActivityThread启动Service
时序图如下所示:
查看AMS的startService方法,如下所示:
- 注释1处调用mServices的startServiceLocked方法,mServices的类型是ActiveServices。
ActiveServices的startServiceLocked方法代码如下所示:
-
注释1处的retrieveServiceLocked方法会查找是否有与参数service对应的ServiceRecord,如果没有找到,就会调用PackageManagerService去获取参数service对应的Service信息,并封装到ServiceRecord中,最后将ServiceRecord封装为ServiceLookupResult返回。
-
其中ServiceRecord用于描述一个Service,和此ActivityRecord类似。
-
在注释2处通过注释1处返回的ServiceLookupResult得到参数service对应的ServiceRecord,并传入到注释3处的startServiceInnerLocked方法中。
在startServiceInnerLocked方法中又调用了bringUpServiceLocked方法,如下所示:
-
在注释1处得到ServiceRecord 的processName值并赋给procName,其中processName用来描述Service想要在哪个进程中运行,默认是当前进程,我们也可以在AndroidManifest文件中设置android:process 属性来新开启一个进程运行Service。
-
在注释2处将procName和Service的uid传入到AMS的getProcessRecordLocked 方法中,查询是否存在一个与Service对应的ProcessRecord类型的对象app,ProcessRecord主要用来描述运行的应用程序进程的信息。
-
在注释5处判断Service对应的app为null则说明用来运行Service的应用程序进程不存在,则调用注释6处的AMS的startProcessLocked方法来创建对应的应用程序进程,关于创建应用程序进程可查看Android App Process启动流程攻略。这里只讨论没有设置android:process属性,即应用程序进程存在的情况。
-
在注释3处判断如果用来运行Service的应用程序进程存在,则调用注释4处的realStartServiceLocked方法来启动Service:
-
在realStartServiceLocked方法中调用了app.thread的scheduleCreateService方法。
-
其中app.thread是IApplicationThread 类型的,它的实现是ActivityThread的内部类ApplicationThread。
ApplicationThread的scheduleCreateService方法如下所示:
- sendMessage方法向H类发送类型为CREATE_SERVICE的消息,并将CreateServiceData传递过去,这个过程和ActivityThread启动Activity的过程是类似的。
sendMessage方法有多个重载方法,最终调用的sendMessage方法如下所示:
- mH指的是H,它是ActivityThread的内部类并继承自Handler,是应用程序进程中主线程的消息管理类。
接着查看H的handleMessage方法:
handleMessage 方法根据消息类型为CREATE_SERVICE,会调用handleCreateService方法:
-
在注释1处获取要启动Service的应用程序的LoadedApk,LoadedApk是一个APK文件的描述类。
-
在注释2处通过调用LoadedApk的getClassLoader方法来获取类加载器。
-接着在注释3处根据CreateServiceData对象中存储的Service信息,创建Service实例。
-
在注释4处创建Service的上下文环境ContextImpl对象。
-
在注释5处通过Service的attach方法来初始化Service。
-
在注释6处调用Service的onCreate方法,这样Service就启动了。
-
在注释7处将启动的Service加入到ActivityThread的成员变量mServices中,其中mServices是ArrayMap类型。
Service的启动过程,就此完成。
3.2 Service的绑定过程
3.2.1 ContextImpl到AMS的调用过程
时序图如下:
bindService方法绑定Service,也是在ContextWrapper中实现,代码如下所示:
mBase具体就是指向ContextImpl的,接着查看ContextImpl的bindService方法:
在bindService方法中,又返回了bindServiceCommon方法,代码如下所示:
-
在注释1处调用了LoadedApk类型的对象mPackageInfo的getServiceDispatcher方法,它的主要作用是将ServiceConnection封装为IServiceConnection类型的对象sd,从IServiceConnection的名字我们就能得知它实现了Binder机制,这样Service的绑定就支持了跨进程。
-
接着在注释2处我们又看见了熟悉的代码,最终会调用AMS的bindService方法。
3.2.2 Service的绑定过程
AMS的bindService方法代码如下所示:
bindService方法最后会调用ActiveServices类型的对象mServices的bindServiceLocked方法:
-
ServiceRecord:用于描述一个Service。
-
ProcessRecord:一个进程的信息。
-
ConnectionRecord:用于描述应用程序进程和Service建立的一次通信。
-
AppBindRecord:应用程序进程通过Intent绑定Service时,会通过AppBindRecord来维护Service与应用程序进程之间的关联。其内部存储了谁绑定的Service (ProcessRecord)、被绑定的Service (AppBindRecord)、绑定Service的Intent(IntentBindRecord)和所有绑定通信记录的信息(ArraySet<ConnectionRecord>)。
-
IntentBindRecord:用于描述绑定Service的Intent。
-
在注释1处调用了ServiceRecord的retrieveAppBindingLocked方法来获得AppBindRecord,retrieveAppBindingLocked 方法内部创建IntentBindRecord,并对IntentBindRecord的成员变量进行赋值。
-
在注释2处调用bringUpServiceLocked方法,在bringUpServiceLocked方法中又调用realStartServiceLocked方法,最终由ActivityThread 来调用Service的onCreate 方法启动Service,这也说明了bindService方法内部会启动Service,启动Service这一过程在3.1节中已经讲过。
-
在注释3处s.app!=null 表示Service 已经运行,其中s 是ServiceRecord类型对象,app是ProcessRecord类型对象。b.intent.received表示当前应用程序进程已经接收到绑定Service时返回的Binder,这样应用程序进程就可以通过Binder 来获取要绑定的Service的访问接口。
-
在注释4处调用c.conn的connected方法,其中c.conn指的是IServiceConnection,它的具体实现为ServiceDispatcher.InnerConnection,其中ServiceDispatcher是LoadedApk的内部类,InnerConnection的connected方法内部会调用H的post方法向主线程发送消息,并且解决当前应用程序进程和Service跨进程通信的问题。
-
在注释5处如果当前应用程序进程是第一个与Service进行绑定的,并且Service已经调用过onUnBind方法,则需要调用注释6处的代码。
-
在注释7处如果应用程序进程的Client端没有发送过绑定Service的请求,则会调用注释8处的代码,注释8处和注释6处的代码区别就是最后一个参数rebind为false,表示不是重新绑定。
查看注释6处的requestServiceBindingLocked方法,代码如下所示:
注释1处i.requested表示是否发送过绑定Service的请求,从bindServiceLocked方法的注释5处得知是发送过的,因此,!i.requested为false。从bindServiceLocked方法的注释5处得知rebind值为true,那么(!i.requested||rebind)的值为true。i.apps.size()>0表示什么呢?其中i是IntentBindRecord 类型的对象,AMS 会为每个绑定Service的Intent分配一个IntentBindRecord类型对象,代码如下所示:
我们来查看IntentBindRecord 类,不同的应用程序进程可能使用同一个Intent来绑定Service,因此在注释1处会用apps来存储所有用当前Intent绑定Service的应用程序进程。i.apps.size()> 0表示所有用当前Intent绑定Service的应用程序进程个数大于0,下面来验证i.apps.size()>0是否为ture。我们回到bindServiceLocked方法的注释1处,ServiceRecord的retrieveAppBindingLocked方法如下所示:
注释1处创建了IntentBindRecord,注释2处根据ProcessRecord获得IntentBindRecord中存储的AppBindRecord,如果AppBindRecord不为null就返回,如果为null就在注释3处创建AppBindRecord,并将ProcessRecord作为key,AppBindRecord作为value保存在IntentBindRecord的apps(i.apps)中。回到requestServiceBindingLocked方法的注释1处,结合ServiceRecord的retrieveAppBindingLocked方法,我们得知i.apps.size()>0为true,这样就会调用注释2处的代码,r.app.thread的类型为IApplicationThread,它的实现我们已经很熟悉了,是ActivityThread的内部类ApplicationThread,scheduleBindService方法如下所示:
首先将Service的信息封装成BindServiceData对象,BindServiceData的成员变量rebind的值为false,后面会用到它。接着将BindServiceData传入到sendMessage方法中。sendMessage向H发送消息,我们接着查看H的handleMessage方法:
H 在接收到BIND_SERVICE类型消息时,会在handleMessage方法中会调用handleBindService方法:
- 在注释1处获取要绑定的Service。
- 注释2处的BindServiceData的成员变量rebind的值为false,这样会调用注释3处的代码来调用Service的onBind方法,到这里Service处于绑定状态了。如果rebind的值为true就会调用注释5处的Service的onRebind方法,这一点结合前文的bindServiceLocked方法的注释5处,得出的结论就是:如果当前应用程序进程第一个与Service进行绑定,并且Service已经调用过onUnBind方法,则会调用Service的onRebind方法。
- handleBindService方法有两个分支,一个是绑定过Servive的情况,另一个是未绑定的情况,这里分析未绑定的情况,查看注释4处的代码,实际上是调用AMS的publishService方法。
Service的绑定过程前半部分调用关系时序图:
查看AMS的publishService方法,代码如下所示:
在publishService方法中调用了ActiveServices类型的mServices对象的publishServiceLocked方法:
注释1处的代码在前面介绍过,c.conn指的是IServiceConnection,它是ServiceConnection在本地的代理,用于解决当前应用程序进程和Service跨进程通信的问题,具体实现为ServiceDispatcher.InnerConnection,其中ServiceDispatcher是LoadedApk的内部类,ServiceDispatcher.InnerConnection的connected方法的代码如下所示:
在注释1处调用了ServiceDispatcher类型的sd对象的connected方法,代码如下所示:
在注释1处调用Handler类型的对象mActivityThread的post方法,mActivityThread实际上指向的是H。因此,通过调用H的post方法将RunConnection对象的内容运行在主线程中。RunConnection是LoadedApk的内部类,定义如下所示:
在RunConnection的run方法中调用了doConnected方法:
在注释1处调用了ServiceConnection 类型的对象mConnection的onServiceConnected方法,这样在客户端实现了ServiceConnection接口类的onServiceConnected方法就会被执行。至此,Service 的绑定过程就分析完成。最后给出剩余部分的代码时序图,如图4-11所示。