---
title: '四大組件 - Service'
disqus: kyleAlien
---
四大組件 - Service
===
## OverView of Content
Service 的基本使用請看 [**Service 使用**](https://hackmd.io/qwwq4oLMTmaw2UksbpPOUg)
[TOC]
## Service 概述
* Service 特點
1. Service 也是 **AMS 所管理的對象**
2. **Service 可以單獨啟動,而不用跟隨 Activity 一起啟動** (但同樣要啟動該 Service 所屬的進程,也就是 `ActivityThread`)
3. AMS 會通知 ActivityThread 來啟動 Service
* 我們一般 App 啟動 Service 有兩種方式
1. **通過 Context#`startService` 來啟動指定 Service**
2. **通過 Context#`bindService` 綁定 Service**
## 綁定 Service
目前主要分析 App 應用端使用 bindService 方法呼叫指定 Service,連線 Service 時回調 onServiceConnected 接口的狀況
```java=
public class TestService extends AppCompatActivity {
// 匿名類
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent(this, MyService.class);
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
}
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
// 向下轉型 (當然你也可以用 Binder 提供的方法)
MyService.MyBindClass bindClass = (MyService.MyBindClass) iBinder;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
}
```
### [ContextImpl](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/ContextImpl.java;l=1?q=ContextImpl.java) - bindService 調用 AMS 註冊 Receiver
* **Context 的實作類是 `ContextImpl 類` (原因可以看看 Activity#attach 函數,這裡就不細講),所以在使用 `bindService` 的時候,實作類就是 [ContextImpl](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/ContextImpl.java;l=1?q=ContextImpl.java)**
1. **取得 `IServiceConnection` 對象**:透過 LoadApk#getServiceDispatcher 取得 `IServiceConnection` 對象,**IServiceConnection 是 APP 端 IBinder Server 對象**,**將來讓 AMS 做回調用** (下個小節說明)
2. **取得 Binder Proxy 訪問 AMS**:ActivityManager#getService 取得 Binder 代理類,透過代理類呼叫 AMS#`bindIsolatedService` 方法
```java=
// ContextImpl.java
final @NonNull LoadedApk mPackageInfo;
@Override
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
...
return bindServiceCommon(service, conn, flags, null, mMainThread.getHandler(), null,
getUser());
}
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
String instanceName, Handler handler, Executor executor, UserHandle user) {
IServiceConnection sd;
// 判空
if (conn == null) {
throw new IllegalArgumentException("connection is null");
}
if (handler != null && executor != null) {
throw new IllegalArgumentException("Handler and Executor both supplied");
}
// mPackageInfo 類型 LoadedApk
if (mPackageInfo != null) {
// @ 1. 主要是要透過 LoadedApk#getServiceDispatcher,包裝出 IServiceConnection
if (executor != null) {
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), executor, flags);
} else {
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
}
} else {
throw new RuntimeException("Not supported in system context");
}
validateServiceIntent(service);
try {
... 省略部分
// @ 2. 分析 AMS#bindIsolatedService 方法
int res = ActivityManager.getService().bindIsolatedService(
mMainThread.getApplicationThread(), getActivityToken(), service,
service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, instanceName, getOpPackageName(), user.getIdentifier());
// 綁定結果
if (res < 0) {
throw new SecurityException(
"Not allowed to bind to service " + service);
}
return res != 0;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
```
> 
* 相關 UML 關係
> 
### IServiceConnection 創建 Binder 回調 - Service 連線
* 分析 [**LoadedApk**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/LoadedApk.java;l=1?q=LoadedApk.java)#**getServiceDispatcherCommon 方法**:可以發現它主要在創建 IServiceConnection 接口,該接口之後會讓 AMS 做回調
1. **檢查 Cache**:首先檢查本地緩存是否有該 Service 的 Binder 回調 (Context 作為 Key),如果有的話直接返回,否則創建 ServiceDispatcher 對象
2. 如果沒有就 **創建 LoadedApk.ServiceDispatcher 對象**
3. 透過 getIServiceConnection 取得 Binder Server 回調接口
```java=
// LoadedApk.java
public final class LoadedApk {
// ServiceDispatcher 的緩存
private final ArrayMap<Context, ArrayMap<ServiceConnection,
LoadedApk.ServiceDispatcher>> mServices = new ArrayMap<>();
public final IServiceConnection getServiceDispatcher(ServiceConnection c,
Context context, Handler handler, int flags) {
// @ 追蹤 getServiceDispatcherCommon
return getServiceDispatcherCommon(c, context, handler, null, flags);
}
private IServiceConnection getServiceDispatcherCommon(ServiceConnection c,
Context context, Handler handler, Executor executor, int flags) {
synchronized (mServices) {
LoadedApk.ServiceDispatcher sd = null;
ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map =
mServices.get(context);
// 1. 判斷緩存
if (map != null) {
...
sd = map.get(c); // 有緩存
}
if (sd == null) {
// @ 2. 沒緩存,直接創建 (根據回傳的接口進行創建)
if (executor != null) {
sd = new ServiceDispatcher(c, context, executor, flags);
} else {
sd = new ServiceDispatcher(c, context, handler, flags);
}
...
if (map == null) {
map = new ArrayMap<>();
mServices.put(context, map);
}
map.put(c, sd);
} else {
sd.validate(context, handler, executor);
}
// 3. 分析 LoadedApk.ServiceDispatcher#getIServiceConnection
return sd.getIServiceConnection();
}
}
}
```
* **ServiceDispatcher 類**:是 LoadedApk 的靜態內部類,真正給 AMS 回調的 Binder 是 **InnerConnection 類**,當 AMS 回調時會呼叫 `connected` 方法
1. 創建 IServiceConnection.Stub 給 AMS 回調
2. AMS 回調實,會創建 `RunConnection` 對象,讓 `Handler` or `Executor` 呼叫
* App 端 IServiceConnection 服務實作,類關係圖
> 
```java=
// LoadedApk.java
public final class LoadedApk {
static final class ServiceDispatcher {
private final ServiceDispatcher.InnerConnection mIServiceConnection;
private static class InnerConnection extends IServiceConnection.Stub {
// 弱引用
final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;
InnerConnection(LoadedApk.ServiceDispatcher sd) {
mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
}
// AMS 在呼叫時會使用 connected 方法
public void connected(ComponentName name, IBinder service, boolean dead)
throws RemoteException {
LoadedApk.ServiceDispatcher sd = mDispatcher.get();
if (sd != null) {
// @ 分析 connected
sd.connected(name, service, dead);
}
}
}
// 建構式
ServiceDispatcher(ServiceConnection conn,
Context context, Handler activityThread, int flags) {
// 創建 InnerConnection 對象
mIServiceConnection = new InnerConnection(this);
... 省略部分參數
}
IServiceConnection getIServiceConnection() {
// 實現類實作 IServiceConnection.Stub
return mIServiceConnection;
}
public void connected(ComponentName name, IBinder service, boolean dead) {
// 如果有設定 Executor 就通過 Executor 通知
if (mActivityExecutor != null) {
// 通過 Executor
mActivityExecutor.execute(new RunConnection(name, service, 0, dead));
} else if (mActivityThread != null) {
// @ 透過 Handler (ActivityThread#H 類)
mActivityThread.post(new RunConnection(name, service, 0, dead));
} else {
doConnected(name, service, dead);
}
}
}
}
```
> 
### AMS 透過 Binder 回調 - ServiceConnection
* **RunConnection 類**:是一個 Runnable,以目前狀況來說是使用 Handler,這個 Runnable 會傳到 ActivityThread#H 類 (也是個 Handler) 讓它回調
```java=
// LoadedApk#ServiceDispatcher#RunConnection.java
private final class RunConnection implements Runnable {
// 建構式
RunConnection(ComponentName name, IBinder service, int command, boolean dead) {
mName = name;
mService = service;
mCommand = command;
mDead = dead;
}
public void run() {
// 目前傳入的 mCommand = 0
if (mCommand == 0) {
// @ 分析 doConnected 方法
doConnected(mName, mService, mDead);
} else if (mCommand == 1) {
doDeath(mName, mService);
}
}
final ComponentName mName;
final IBinder mService;
final int mCommand;
final boolean mDead;
}
```
* ServiceDispatcher#**doConnected** 方法:對 App 傳入的 ServiceConnection 接口進行回調,並有多個狀況如下
| 是否同名 Server cache | 目標 IBinder 狀態 | 說明 |處理行為 |
| -------- | -------- | -------- | - |
| Y | 比對相同 | 已連線 | 直接返回,不多作處理 |
| Y | 比對不相同 | 已連線,但取得不同 IBinder 代理 | **^1.^ 先斷線 `onServiceDisconnected`**,**^2.^ 再次連線 `onServiceConnected`** |
| N | IBinder 不為空 | 第一次連線 | 連線呼叫 `onServiceConnected` |
```java=
// LoadedApk#ServiceDispatcher.java
private final ServiceConnection mConnection;
public void doConnected(ComponentName name, IBinder service, boolean dead) {
ServiceDispatcher.ConnectionInfo old;
ServiceDispatcher.ConnectionInfo info;
synchronized (this) {
...
old = mActiveConnections.get(name);
// 1. 判斷是否已經綁定 && 是同一個 Binder
if (old != null && old.binder == service) {
// Huh, already have this one. Oh well!
return;
}
if (service != null) {
// 開始連線新 Service
info = new ConnectionInfo();
info.binder = service;
info.deathMonitor = new DeathMonitor(name, service);
try {
service.linkToDeath(info.deathMonitor, 0);
mActiveConnections.put(name, info);
} catch (RemoteException e) {
// This service was dead before we got it... just
// don't do anything with it.
mActiveConnections.remove(name);
return;
}
} else {
// Service disconnect 從緩存中移除
mActiveConnections.remove(name);
}
if (old != null) {
old.binder.unlinkToDeath(old.deathMonitor, 0);
}
}
// 2. 如果是舊連結,呼叫 onServiceDisconnected 斷開 但 不同 IBinder
if (old != null) {
mConnection.onServiceDisconnected(name);
}
if (dead) {
mConnection.onBindingDied(name);
} else {
// If there is a new viable service, it is now connected.
if (service != null) {
// 3. 呼叫 onServiceConnected (回到使用者接口)
mConnection.onServiceConnected(name, service);
} else {
mConnection.onNullBinding(name);
}
}
}
```
下圖是正常回調
> 
### [**AMS**](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/services/core/java/com/android/server/am/ActivityManagerService.java) - 啟動、綁定 Service 過程
:::info
目前進入 AMS 進程,處理 Service 服務綁定
:::
* 客戶端 APP 透過 ContextImpl#bindService,來觸發 AMS#**bindIsolatedService** 方法,綁定 APP & Service
1. 檢查 Service Intent 必須要有 FileDescriptors
2. 呼叫 ActiveServices#bindServiceLocked 方法
```java=
// ActivityManagerService.java
// 存活的 Servcie 列表
final ActiveServices mServices;
public int bindIsolatedService(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, IServiceConnection connection, int flags, String instanceName,
String callingPackage, int userId) throws TransactionTooLargeException {
enforceNotIsolatedCaller("bindService");
// 1. 檢查 intent
// intent 必須要有 FileDescriptors
if (service != null && service.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
// 檢查客戶端包名
if (callingPackage == null) {
throw new IllegalArgumentException("callingPackage cannot be null");
}
... 省略部分
// 函數取名中有 Lock 的原因,對呼叫線程堵塞
synchronized(this) {
// @ 2. 呼叫 ActiveServices#bindServiceLocked
return mServices.bindServiceLocked(caller, token, service,
resolvedType, connection, flags, instanceName, callingPackage, userId);
}
}
```
:::warning
函數取名中有 Lock 的原因,對呼叫線程堵塞
:::
* [**ActiveServices**](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/am/ActiveServices.java)#bindServiceLocked 方法
1. 檢查呼叫者 進程是否存在 (可能已被 kill),若不存在則拋出
2. 透過 **retrieveServiceLocked 方法**:**去 PKMS 中找到對應的 Service 訊息**
:::success
* App 端設定的 `BIND_AUTO_CREATE` Flag
若有設置該 Flag,就會透過 `bringUpServiceLocked` 方法 **自動創建該 Service** (之後分析)
:::
3. 透過 **requestServiceBindingLocked 方法**:要求綁定 Service,這個函數包含了第一次綁定 & 重新綁定的功能 (差異在最後一個參數)
```java=
// ActiveServices.java
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, final IServiceConnection connection, int flags,
String instanceName, String callingPackage, final int userId)
throws TransactionTooLargeException {
// 呼叫者 Pid、Uid
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
// 1. 透過 AMS 尋找呼叫者進程
final ProcessRecord callerApp = mAm.getRecordForAppLOSP(caller);
if (callerApp == null) {
// 拋出錯誤
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + callingPid
+ ") when binding service " + service);
}
...
//2. 搜尋 PKMS 中的 Service
ServiceLookupResult res =
retrieveServiceLocked(service, instanceName, resolvedType, callingPackage,
callingPid, callingUid, userId, true,
callerFg, isBindExternal, allowInstant);
// 若沒有 res 代表沒有該 Service
if (res == null) {
return 0;
}
// 判斷其中的 ServiceRecord
if (res.record == null) {
return -1;
}
ServiceRecord s = res.record;
...
try {
...
// 自動啟動 Service
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
s.lastActivity = SystemClock.uptimeMillis();
needOomAdj = true;
// 自動啟動 Service
if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
permissionsReviewRequired, packageFrozen, true) != null) {
mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_BIND_SERVICE);
return 0;
}
}
...
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
// Service 連接紀錄
ConnectionRecord c = new ConnectionRecord(b, activity,
connection, flags, clientLabel, clientIntent,
callerApp.uid, callerApp.processName, callingPackage);
... 調整 OomAdj 數值
// 判斷 Service 進程是否啟動 (app 就是 ProcessReocrd)
if (s.app != null && b.intent.received) {
try {
// 透過使用者傳入的接口進行進行回掉
// 上個小節有分析過 (conn 就是 IServiceConnect),最終會呼叫到 onServiceConnected
c.conn.connected(s.name, b.intent.binder, false);
} /* 省略 catch */
// 如果當前應用進程是第一個與 Service 進程綁定的
// && 有調用過 doRebind 方法
if (b.intent.apps.size() == 1 && b.intent.doRebind) {
// 最後一個參數表示,是否重新綁定
// @ 分析 requestServiceBindingLocked 方法
requestServiceBindingLocked(s, b.intent, callerFg, true);
}
} else if (!b.intent.requested) { // 應用端沒有綁定過
// 最後一個參數表示,是否重新綁定
requestServiceBindingLocked(s, b.intent, callerFg, false);
}
...
} finally {
Binder.restoreCallingIdentity(origId);
}
return 1;
}
```
* **[ActiveServices](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/am/ActiveServices.java)#requestServiceBindingLocked 方法**:針對傳入的 **ServiceRecord** 來進行判斷、操作
1. 檢查 ServiceRecord 指定的進程是否起動 (檢查 ProcessRecord & IApplicationThread 是否存在),也就是檢查 Service 是否啟動
2. 目前假設 Service 目標進程已經啟動,那就會透過該進程內實現的 IApplicationThread#requestServiceBindingLocked 回到目標進程內
```java=
// ActiveServices.java
private final boolean requestServiceBindingLocked(ServiceRecord r,
IntentBindRecord i,
boolean execInFg,
boolean rebind) throws TransactionTooLargeException {
// 檢查 ProcessRecord & 其中的 Service#IApplicationThread 是否存在
if (r.app == null || r.app.getThread() == null) {
// If service is not currently running, can't yet bind.
return false;
}
... Bebug 訊息
// 用戶端沒綁定過 or 呼叫 Service 要重新綁定
if ((!i.requested || rebind) && i.apps.size() > 0) {
try {
// 通知 IApplicationThread (App 端實線) 的 scheduleBindService 方法
// 分析 scheduleBindService 方法
r.app.getThread().scheduleBindService(r, i.intent.getIntent(), rebind,
r.app.mState.getReportedProcState());
if (!rebind) {
i.requested = true;
}
i.hasBound = true;
i.doRebind = false;
} /* 省略 catch */
}
return true;
}
```
:::info
* 從這邊可以看出 Service 呼叫 onBind 方法的時機是,尚未連接、重新綁定
:::
> 
### App 進程 - Service#onBind 呼叫
:::info
目前在使用者呼叫的 Service 進程
:::
* [**ActivityThread**](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/ActivityThread.java) - scheduleBindService 進入 Service 所在進程做綁定
1. 創建 BindServiceData 對象,儲存 `ServiceRecord` & `Intent` 訊息
2. Server 進程收到 Binder 通訊通知後,透過 Handler 對主線程發送 `BIND_SERVICE` 訊息
3. 主線程接收到 `BIND_SERVICE` 消息後,呼叫 handleBindService 方法,如果是第一次綁定就 ^1.^ 呼叫該 Service 的 **onBind** 方法,再接著 ^2.^ 呼叫 AMS#**publishService** 方法
```java=
// ActivityThread.java
private class ApplicationThread extends IApplicationThread.Stub {
... 省略其他方法
public final void scheduleBindService(IBinder token, Intent intent,
boolean rebind, int processState) {
...
// 1. 創建 BindServiceData 對象,儲存 ServiceRecord & Intent
BindServiceData s = new BindServiceData();
// token 就是 ServiceRecord
s.token = token;
s.intent = intent;
s.rebind = rebind;
... Debug 訊息
// 2. 對主線程發送 BIND_SERVICE 訊息
sendMessage(H.BIND_SERVICE, s);
}
}
class H extends Handler {
public static final int BIND_SERVICE = 121;
public void handleMessage(Message msg) {
... 省略其他 case
case BIND_SERVICE:
... 省略 trace
handleBindService((BindServiceData)msg.obj);
break;
}
}
private void handleBindService(BindServiceData data) {
// token 就是 ServiceRecord
CreateServiceData createData = mServicesData.get(data.token);
// 查看緩存
// mServices 在 handleCreateService 時就會被添加
Service s = mServices.get(data.token);
// 若取不到,則代表 Service 尚未啟動
if (s != null) {
try {
...
try {
if (!data.rebind) {
// 3. 第一次綁定就呼叫 onBind、AMS#publishService 方法
IBinder binder = s.onBind(data.intent);
// publishService 代表 Server 已經啟動
ActivityManager.getService().publishService(
data.token, data.intent, binder);
} else {
// 重新綁定則呼叫 onRebind
s.onRebind(data.intent);
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
}
try {
// 通知 AMS 該 Service 正在運行中
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_START, data.startId, res);
} /* 省略 catch */
} /* 省略 catch */
}
}
```
> 
### [Service](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/services/core/java/com/android/server/am/ActivityManagerService.java) 發布 - 通知所有使用者
* 若 Service 進程啟動第一次啟動,就會呼叫 AMS#publishService 方法
```java=
// ActivityManagerService.java
// token 就是 BindServiceData
public void publishService(IBinder token, Intent intent, IBinder service) {
// Refuse possible leaked file descriptors
if (intent != null && intent.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
synchronized(this) {
if (!(token instanceof ServiceRecord)) {
throw new IllegalArgumentException("Invalid service token");
}
// 分析 publishServiceLocked 方法
mServices.publishServiceLocked((ServiceRecord)token, intent, service);
}
}
```
* [**ActiveServices**](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/am/ActiveServices.java)#publishServiceLocked 透過 ServiceRecord 取得所有與該 Serice 有連接的 APP 端,並逐一進行通知
:::success
* ServiceRecord 會記得所有與該 Service 連接的 APP
:::
```java=
// ActiveServices.java
// token 就是 BindServiceData
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
final long origId = Binder.clearCallingIdentity();
try {
... debug 訊息
if (r != null) {
...
// 取得該 Service 的綁定訊息
IntentBindRecord b = r.bindings.get(filter);
if (b != null && !b.received) {
b.binder = service;
b.requested = true;
b.received = true;
// 透過 ServiceRecord 取得,所有與 該 Service 連線的紀錄
ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = r.getConnections();
for (int conni = connections.size() - 1; conni >= 0; conni--) {
ArrayList<ConnectionRecord> clist = connections.valueAt(conni);
// 遍歷每個連線紀錄
for (int i=0; i<clist.size(); i++) {
// 當前連線紀錄
ConnectionRecord c = clist.get(i);
...
try {
// conn 是 IServiceConnect
// 分析 IServiceConnect#connected 方法
c.conn.connected(r.name, service, false);
} /* 省略 catch */
}
}
}
...
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
```
## 啟動 Service
分析啟動 Service 的另外一種方式 Context#startService
```java=
public class TestService extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent(this, MyService.class);
startService(intent);
}
}
```
### [ContextImpl](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/ContextImpl.java) startService 呼叫 AMS
* 透過 ActivityManager 作為代理類訪問 AMS 的 startService 方法,這邊可以看出來 startService 並沒有回調,但是 **可以通過返回 ComponentName 判斷是否啟動成功**
```java=
// ContextImpl.java
@Override
public ComponentName startService(Intent service) {
...
return startServiceCommon(service, false, mUser);
}
private ComponentName startServiceCommon(Intent service, boolean requireForeground,
UserHandle user) {
try {
validateServiceIntent(service);
service.prepareToLeaveProcess(this);
// 透過代理類訪問 AMS#startService
ComponentName cn = ActivityManager.getService().startService(
mMainThread.getApplicationThread(), service,
service.resolveTypeIfNeeded(getContentResolver()), requireForeground,
getOpPackageName(), getAttributionTag(), user.getIdentifier());
if (cn != null) {
// 沒有權限去啟動 Service
if (cn.getPackageName().equals("!")) {
throw new SecurityException(
"Not allowed to start service " + service
+ " without permission " + cn.getClassName());
} else if (cn.getPackageName().equals("!!")) {
// 無法啟動 Service
throw new SecurityException(
"Unable to start service " + service
+ ": " + cn.getClassName());
} else if (cn.getPackageName().equals("?")) {
// 不允許啟動 Service
throw ServiceStartNotAllowedException.newInstance(requireForeground,
"Not allowed to start service " + service + ": " + cn.getClassName());
}
}
... 省略部分
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
```
> 
### [AMS](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/services/core/java/com/android/server/am/ActivityManagerService.java) - 啟動 Service 過程
* ActivityManagerService#startService
1. 檢查 Intent 是否包含 FileDescriptors
2. 呼叫 ActiveServices#startServiceLocked 方法
```java=
// ActivityManagerService.java
final ActiveServices mServices;
@Override
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType, boolean requireForeground, String callingPackage,
String callingFeatureId, int userId)
throws TransactionTooLargeException {
// 檢查 Intent
if (service != null && service.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
// 檢查 Package
if (callingPackage == null) {
throw new IllegalArgumentException("callingPackage cannot be null");
}
... debug 訊息
synchronized(this) {
// 取得呼叫者的 pid & uid
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
ComponentName res;
try {
// mServices 類型是 ActiveServices
// 分析 startServiceLocked 方法
res = mServices.startServiceLocked(caller, service,
resolvedType, callingPid, callingUid,
requireForeground, callingPackage, callingFeatureId, userId);
} finally {
Binder.restoreCallingIdentity(origId);
}
return res;
}
}
```
* [**ActiveServices**](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/am/ActiveServices.java)
1. 檢查目前裝置 (同樣是透過 PKMS) 是否有指定 Service,查看 retrieveServiceLocked 方法
2. 找到指定 ServiceRecord
```java=
// ActiveServices.java
// caller 是呼叫者
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
int callingPid, int callingUid, boolean fgRequired,
String callingPackage, @Nullable String callingFeatureId, final int userId,
boolean allowBackgroundActivityStarts, @Nullable IBinder backgroundActivityStartsToken)
throws TransactionTooLargeException {
... 省略部分
// 1. 檢查目前裝置是否有指定 Service,查看 retrieveServiceLocked 方法
ServiceLookupResult res =
retrieveServiceLocked(service, null, resolvedType, callingPackage,
callingPid, callingUid, userId, true, callerFg, false, false);
// 找不到 Service
if (res == null) {
return null;
}
...
ServiceRecord r = res.record;
... 省略部分
// 2. 找到的 ServiceRecord 就是用於描述一個 Service
return startServiceInnerLocked(r, service, callingUid, callingPid, fgRequired, callerFg,
allowBackgroundActivityStarts, backgroundActivityStartsToken);
}
```
* ServiceRecord 用於描述一個 Service,類似於 ActivityRecord 描述一個 Activity
| APP 端描述 | AMS 中描述 |
| -------- | -------- |
| Activity | ActivityRecord |
| Service | ServiceRecord |
### [ActiveServices](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/am/ActiveServices.java) - 透過 PKMS 找 Service
* AMS 尋找 Service 的方法是 `ServiceLoopupResult`,**目的是要找到 ServiceRecord**,而 **找到 ServiceRecord 主要透過 ++PKMS++**
```java=
// ActiveServices.java
private ServiceLookupResult retrieveServiceLocked(Intent service,
String instanceName, String resolvedType, String callingPackage,
int callingPid, int callingUid, int userId,
boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal,
boolean allowInstant) {
// 每個 Service 都會轉為 ServiceRecord
ServiceRecord r = null;
ServiceMap smap = getServiceMapLocked(userId);
// 組裝 ComponentName
final ComponentName comp;
if (instanceName == null) { // 目前傳入的 instanceName 是 null
comp = service.getComponent();
} else {
// 透過 Component 組裝 ComponentName
final ComponentName realComp = service.getComponent();
if (realComp == null) {
throw new IllegalArgumentException("Can't use custom instance name '" + instanceName
+ "' without expicit component in Intent");
}
comp = new ComponentName(realComp.getPackageName(),
realComp.getClassName() + ":" + instanceName);
}
// 判斷 ComponentName 結果
if (comp != null) {
r = smap.mServicesByInstanceName.get(comp);
}
... 省略部分
if (r == null) {
try {
...
// 透過 PackageManager 找對對應的 Service
ResolveInfo rInfo = mAm.getPackageManagerInternal().resolveService(service,
resolvedType, flags, userId, callingUid);
ServiceInfo sInfo = rInfo != null ? rInfo.serviceInfo : null;
if (sInfo == null) {
// 如果 PKMS 都找不到就直接返回 null
Slog.w(TAG_SERVICE, "Unable to start service " + service + " U=" + userId +
": not found");
return null;
}
... 省略部分
} catch (RemoteException ex) {
// 在同進程,所以一定不會發生
}
}
if (r != null) {
... 省略部分
return new ServiceLookupResult(r, null);
}
return null;
}
// Key 是 callingUser(int)
final SparseArray<ServiceMap> mServiceMap = new SparseArray<>();
private ServiceMap getServiceMapLocked(int callingUser) {
// 嘗試取得緩存
ServiceMap smap = mServiceMap.get(callingUser);
if (smap == null) {
// 重新創建一個 Service 紀錄對象
smap = new ServiceMap(mAm.mHandler.getLooper(), callingUser);
mServiceMap.put(callingUser, smap);
}
return smap;
}
final class ServiceMap extends Handler {
final int mUserId;
// 呼叫過的 Service 都會記錄起來
final ArrayMap<ComponentName, ServiceRecord> mServicesByInstanceName = new ArrayMap<>();
... 省略部分
}
```
* mServiceMap 主要是透過 User 作為 Key,儲存相對應的 ServiceMap,內部又有儲存 ComponentName & ServiceRecord,當 User 呼叫過相同的 Service 就可以透過它取得 Service 的緩存
### Service 啟動入口 - [startServiceInnerLocked](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/am/ActiveServices.java)
```java=
// ActiveServices.java
final ActivityManagerService mAm;
ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
// 這也是 Function 有 Locked 關鍵字的關係
synchronized (mAm.mProcessStats.mLock) {
final ServiceState stracker = r.getTracker();
if (stracker != null) {
stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
}
}
r.callStart = false;
final int uid = r.appInfo.uid;
final String packageName = r.name.getPackageName();
final String serviceName = r.name.getClassName();
... 省略部分
// 透過 bringUpServiceLocked 啟動 Service
String error = bringUpServiceLocked(r, service.getFlags(), callerFg,
false /* whileRestarting */,
false /* permissionsReviewRequired */,
false /* packageFrozen */,
true /* enqueueOomAdj */);
...
return r.name;
}
```
| 函數名 | 功能 |
| -------- | -------- |
| bringUpServiceLocked | 判斷 Process,若設為啟動則透過 AMS 喚醒 |
| bringDownServiceLocked | 取得連接該 Service 的所有連線,關閉 Service |
:::success
* Function 有 Lock 關鍵字
* 在呼叫這個 Function 時,該 Function 會透過 synchronized 來 Locked 呼叫者線程 (必須順序處理該函數)
:::
* bringUpServiceLocked 判斷 Process 狀態,若 Service 應在要所在的進程沒有啟動則透過 AMS 啟動目標 Process
```java=
// ActiveServices.java
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
boolean whileRestarting, boolean permissionsReviewRequired, boolean packageFrozen,
boolean enqueueOomAdj)
throws TransactionTooLargeException {
... 省略判空、Log 訊息
// 該 Service 是否隔離
final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
// 獲取 Service 要運行在哪個進程
final String procName = r.processName;
HostingRecord hostingRecord = new HostingRecord("service", r.instanceName);
ProcessRecord app;
if (!isolated) {
// 1. 嘗試透過 AMS 取得 ProcessRecord 對象
app = mAm.getProcessRecordLocked(procName, r.appInfo.uid);
if (app != null) { // ProcessRecord 存在
final IApplicationThread thread = app.getThread();
final int pid = app.getPid();
final UidRecord uidRecord = app.getUidRecord();
if (thread != null) {
try {
app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode,
mAm.mProcessStats);
// 2. realStartServiceLocked 真正啟動 Service
realStartServiceLocked(r, app, thread, pid, uidRecord, execInFg,
enqueueOomAdj);
return null;
} /* 省略 catch*/
}
}
} else {
... 省略部分
}
if (app == null && !permissionsReviewRequired && !packageFrozen) {
// 目標 process 不存在就透過 AMS#startProcessLocked 啟動
if ((app = mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
hostingRecord, ZYGOTE_POLICY_FLAG_EMPTY, false, isolated)) == null) {
... 啟動失敗
// 關閉 Servcie
bringDownServiceLocked(r, enqueueOomAdj);
return msg;
}
if (isolated) {
r.isolatedProc = app;
}
}
... 省略部分
}
```
:::info
* 目標 process 不存在
* 透過 AMS#**startProcessLocked** 目標進程啟動
:::
以下是 Service 進程存在的情況
> 
### 真正創建 Service - [realStartServiceLocked](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/am/ActiveServices.java)
* 透過 ProcessRecord 中的 thread (也就是 AcitivtyThread 內的 ApplicationThread 對象) 呼叫 scheduleCreateService 方法
```java=
// ActiveServices.java
private void realStartServiceLocked(ServiceRecord r, ProcessRecord app,
IApplicationThread thread, int pid, UidRecord uidRecord, boolean execInFg,
boolean enqueueOomAdj) throws RemoteException {
// 設定目標 Process
r.setProcess(app, thread, pid, uidRecord);
... 省略部分
// 取得目標進程內所有的 Services
final ProcessServiceRecord psr = app.mServices;
final boolean newService = psr.startService(r);
...
// 透過 AMS 調整 OOM_ADJ 的數值
mAm.enqueueOomAdjTargetLocked(app);
mAm.updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_START_SERVICE);
boolean created = false;
try {
...
final int uid = r.appInfo.uid;
final String packageName = r.name.getPackageName();
final String serviceName = r.name.getClassName();
...
// thread 是 App 端的 IApplicationThread
thread.scheduleCreateService(r,
r.serviceInfo,
mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
app.mState.getReportedProcState());
...
created = true;
} /* 省略 catch、finally */
... 省略部分
}
```
* [**ActivityThread**](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/ActivityThread.java) - APP 端處理 scheduleCreateService,會透過 MainHandler 傳送 CREATE_SERVICE 到主線程,之後就會呼叫到 handleCreateService 方法
> 
```java=
// ActivityThread.java
private class ApplicationThread extends IApplicationThread.Stub {
public final void scheduleCreateService(IBinder token,
ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
updateProcessState(processState, false);
// 將需要的資料封裝成 CreateServiceData 類
CreateServiceData s = new CreateServiceData();
s.token = token; // Token 就是 ServiceRecord 類
s.info = info; // 包有 Service 的 class name ... 等等訊息
s.compatInfo = compatInfo;
// 透過 Hander 傳送 CREATE_SERVICE 訊息
sendMessage(H.CREATE_SERVICE, s);
}
}
class H extends Handler {
public static final int CREATE_SERVICE = 114;
public void handleMessage(Message msg) {
switch (msg.what) {
case CREATE_SERVICE:
...
handleCreateService((CreateServiceData)msg.obj);
break;
}
}
}
@UnsupportedAppUsage
private void handleCreateService(CreateServiceData data) {
Service service = null;
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
try {
Application app = packageInfo.makeApplication(false, mInstrumentation);
...
// 透過 LoadedApk 創建 Servcie 對象
// getAppFactory() => 創建的是 AppComponentFactory 對象
service = packageInfo.getAppFactory()
.instantiateService(cl, data.info.name, data.intent);
// 創建對應的 ContextImpl 對象
ContextImpl context = ContextImpl.getImpl(service
.createServiceBaseContext(this, packageInfo));
// 呼叫 service#attach 方法
service.attach(context, this, data.info.name, data.token, app,
ActivityManager.getService());
// 呼叫 onCreate 方法
service.onCreate();
try {
// 通知 AMS,Service 創建成功
// data.token 就是 ServiceRecord
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} /* 省略 catch */
}
```
1. 透過 [**LoadedApk**](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/LoadedApk.java) 創建 Service 對象
```java=
// LoadedApk.java
private AppComponentFactory mAppComponentFactory;
public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo,
CompatibilityInfo compatInfo, ClassLoader baseLoader,
boolean securityViolation, boolean includeCode, boolean registerPackage) {
mActivityThread = activityThread;
// Default ApplicationInfo
mApplicationInfo = new ApplicationInfo();
...
// 透過 createAppFactory 方法創建 Factory
mAppComponentFactory = createAppFactory(mApplicationInfo, mBaseClassLoader);
}
public AppComponentFactory getAppFactory() {
return mAppComponentFactory;
}
private AppComponentFactory createAppFactory(ApplicationInfo appInfo, ClassLoader cl) {
// 若有自訂 Factory 則透過反射創建
if (mIncludeCode && appInfo.appComponentFactory != null && cl != null) {
try {
return (AppComponentFactory)
cl.loadClass(appInfo.appComponentFactory).newInstance();
} /* 省略部分 */
}
// 預設 Factory
return AppComponentFactory.DEFAULT;
}
```
2. 預設工廠 [**AppComponentFactory**](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/AppComponentFactory.java):透過 ClassLoader#loadClass 讀取指定 Class,並透過反射創建指定類
```java=
// AppComponentFactory.java
public class AppComponentFactory {
... 省略部分
public @NonNull Service instantiateService(@NonNull ClassLoader cl,
@NonNull String className, @Nullable Intent intent)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
// 透過 ClassLoader#loadClass 讀取指定 Class,並透過反射創建該類
return (Service) cl.loadClass(className).newInstance();
}
// 預設 Factory
public static final AppComponentFactory DEFAULT = new AppComponentFactory();
}
```
> 
## Appendix & FAQ
:::info
:::
###### tags: `Android Framework`