官方對Service
的描述是:java
Service
是一種可在後臺執行長時間運行操做而不提供界面的應用組件。服務可由其餘應用組件啓動,並且即便用戶切換到其餘應用,服務仍將在後臺繼續運行。此外,組件可經過綁定到服務與之進行交互,甚至是執行進程間通訊IPC
。android
在Service
使用上,應用須要在AndroidManifest.xml
文件中經過標籤<service/>
來聲明一個Service
,標籤的屬性以下:數據庫
<service android:description="string resource" android:directBootAware=["true" | "false"] android:enabled=["true" | "false"] android:exported=["true" | "false"] android:foregroundServiceType=["connectedDevice" | "dataSync" | "location" | "mediaPlayback" | "mediaProjection" | "phoneCall"] android:icon="drawable resource" android:isolatedProcess=["true" | "false"] android:label="string resource" android:name="string" android:permission="string" android:process="string" >
. . .
</service>
複製代碼
<service/>
標籤屬性比較簡單,大部分和前面介紹的<application/>
的屬性重複,關於標籤的描述你們能夠參考官網:<service/>
標籤數組
咱們重點記錄下特殊的幾個屬性:安全
android:directBootAware
:服務是否支持直接啓動,即其是否能夠在用戶解鎖設備以前運行
android:process
:指定將運行服務的進程的名稱。
<application>
元素的process
屬性可爲全部組件設置不一樣的默認進程名稱process
屬性替換默認值,將應用散佈到多個進程中:
)開頭,則系統會在須要時建立應用專用的新進程,而且服務會在該進程中運行android:isolatedProcess
:若是設置爲true
,則此服務將在與系統其他部分隔離的特殊進程下運行。都是建立新的進程,那麼android:process
和android:isolatedProcess
的區別是什麼呢?markdown
android:process
屬性時,服務啓動後進程信息以下: u0_a50 7889 3220 1114656 94464 0 0 S lee.hua.skills_android
u0_a50 8358 3220 1078100 58700 0 0 S lee.hua.skills_android:newProcess
複製代碼
user
是同樣的,都是u0_a50
Android
在新進程的權限和接口調用方面未作限制android:isolatedProcess
屬性時,服務啓動後進程信息以下: u0_a50 7889 3220 1114656 94464 0 0 S lee.hua.skills_android
u0_i0 8358 3220 1078100 58700 0 0 S lee.hua.skills_android:newProcess
複製代碼
user
不一樣了,查看uid
分別是:(u0_a50)10050
和(u0_i0)99000
Android
中定義的isolated
進程的起始號爲99000
Android
對isolated
進程的接口調用作了一些限制
bindService()
的進程若是是isolated
進程的話會拋出安全異常後面Service
的啓動代碼中會涉及到isolated
相關的邏輯,咱們先看下Service
的生命週期網絡
Service
的生命週期和Activity
相似,Service
也有聲明週期,可是和Activity
的聲明週期相比,Service
的要簡單許多。數據結構
官方圖示以下:
app
startService()
建立的服務的生命週期bindService()
建立的服務的生命週期Service
生命週期(從建立到銷燬)可遵循如下任一路徑:ide
startService()
startService()
時建立,而後無限期運行,且必須經過調用stopSelf()
來自行中止運行。stopService()
來中止此服務。服務中止後,系統會將其銷燬。bindService()
bindService()
時建立。而後,客戶端經過IBinder
接口與服務進行通訊。unbindService()
關閉鏈接。Service
有兩種運行模式
API 26
或更高版本,當應用自己未在前臺運行時,系統會對運行後臺服務施加限制關於生命週期的回調函數就不細講了,比較簡單,你們能夠參考官網:Service
基礎知識
而在ActivityThread
的成員變量mServices
中保存了應用中全部Service
對象,定義以下:
final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();
複製代碼
對於Service
類來講,重要的成員變量以下:
public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {
// 引用 ActivityThread 對象
private ActivityThread mThread = null;
// Service 的類名
private String mClassName = null;
// service 的惟一標識符
private IBinder mToken = null;
// 引用 Application
private Application mApplication = null;
}
複製代碼
Service
的管理類AMS 中對於Service
的管理是經過ActiveServices
類來進行的。ActiveServices
類的構造方法以下:
public ActiveServices(ActivityManagerService service) {
mAm = service;
int maxBg = 0;
try {
maxBg = Integer.parseInt(SystemProperties.get("ro.config.max_starting_bg", "0"));
} catch(RuntimeException e) {
}
mMaxStartingBackground = maxBg > 0
? maxBg : ActivityManager.isLowRamDeviceStatic() ? 1 : 8;
}
複製代碼
構造方法很簡單,只是從系統屬性ro.config.max_starting_bg
中讀取了容許後臺容許的Service
的最大數量。
ActiveServices
類中還有一些重要的成員變量,以下所示:
// How long we wait for a service to finish executing.
static final int SERVICE_TIMEOUT = 20*1000;
// How long we wait for a service to finish executing.
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;
// How long the startForegroundService() grace period is to get around to
// calling startForeground() before we ANR + stop it.
static final int SERVICE_START_FOREGROUND_TIMEOUT = 10*1000;
final ActivityManagerService mAm;
// 系統運行運行的最大Service數量
final int mMaxStartingBackground;
final SparseArray<ServiceMap> mServiceMap = new SparseArray<>();
final ArrayMap<IBinder, ArrayList<ConnectionRecord>> mServiceConnections = new ArrayMap<>();
final ArrayList<ServiceRecord> mPendingServices = new ArrayList<>();
final ArrayList<ServiceRecord> mRestartingServices = new ArrayList<>();
final ArrayList<ServiceRecord> mDestroyingServices = new ArrayList<>();
複製代碼
mServiceMap
是一個SparseArray
類型的數組,索引是用戶Id
Service
記錄存放在數組元素中,每一個元素是一個ServiceMap
ServiceMap
中存儲了某個用戶全部的ServiceRecord
對象Service
在AMS
中對應的數據結構就是ServiceRecord
類mServiceConnections
儲存的是全部鏈接記錄ConnectionRecord
的對象
Service
時傳遞的信息mServiceConnections
記錄了AMS
和使用服務的進程之間的聯繫mPendingServices
保存的是正在等待進程啓動的Service
AMS
會先去啓動服務所在的進程,這個時間可能比較長Service
的信息保存在mPendingServices
中mRestartingServices
保存的是正在等待從新啓動進程的Service
Service
所在的進程崩潰了,會把該Service
加入到mRestartingServices
中,準備從新啓動進程mDestroyingServices
保存的是正在等待進程銷燬的Service
Service
保存在mDestroyingServices
中Context
提供了兩個接口來啓動Service
,分別是startService()
和bindService()
。定義以下:
public abstract ComponentName startService(Intent service);
public abstract boolean bindService(@RequiresPermission Intent service, @NonNull ServiceConnection conn, @BindServiceFlags int flags);
複製代碼
從接口定義能夠看出,bindService()
須要傳遞一個回調接口ServiceConnection
來接收Binder
對象。
這兩個接口最後調用的是AMS
的startService()
和bindService()
,兩個方法的實現差很少,bindService()
更復雜一些。
後面以bindService()
方法爲例進行分析,在此以前,咱們先看下啓動過程的時序圖:
AMS
中bindService()
的代碼以下:
public int bindService(IApplicationThread caller, IBinder token, Intent service, String resolvedType, IServiceConnection connection, int flags, String callingPackage, int userId) throws TransactionTooLargeException {
// 檢查調用進程是不是isolate Process,若是是拋出安全異常
enforceNotIsolatedCaller("bindService");
// 檢查啓動Service的Intent中是否包含文件描述符
if (service != null && service.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
// 檢查 callingPackage 是否爲空
if (callingPackage == null) {
throw new IllegalArgumentException("callingPackage cannot be null");
}
synchronized(this) {
// 調用 ActiveService 類的 bindServiceLocked 方法
return mServices.bindServiceLocked(caller, token, service,
resolvedType, connection, flags, callingPackage, userId);
}
}
複製代碼
ActiveService.bindServiceLocked
ActiveService
類的bindServiceLocked()
方法以下:
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service, String resolvedType, final IServiceConnection connection, int flags, String callingPackage, final int userId) throws TransactionTooLargeException {
......// 省略一些權限和錯誤檢查
// 建立 ServiceRecord 對象
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType, callingPackage, Binder.getCallingPid(),
Binder.getCallingUid(), userId, true, callerFg, isBindExternal, allowInstant);
......// 省略 res 的異常檢查
ServiceRecord s = res.record;
......
try {
// 將 service 從 mRestartingServices 列表中移除
if (unscheduleServiceRestartLocked(s, callerApp.info.uid, false)) {
......
}
......
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
s.lastActivity = SystemClock.uptimeMillis();
// 經過 bringUpServiceLocked 啓動 Service 所在的進程
if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
permissionsReviewRequired) != null) {
// 請注意,這裏作了退出操做
return 0;
}
}
if (s.app != null) {
......
// 調整進程的優先級
mAm.updateLruProcessLocked(s.app, s.app.hasClientActivities
|| s.app.treatLikeActivity, b.client);
mAm.updateOomAdjLocked(s.app, true);
}
if (s.app != null && b.intent.received) {
// 若是進程已經啓動而且綁定過了,直接調用 IServiceConnection 的 connected()方法
// 此方法最終會調用 ServiceConnection 的 onServiceConnected() 方法
try {
c.conn.connected(s.name, b.intent.binder, false);
} catch (Exception e) {
......
}
if (b.intent.apps.size() == 1 && b.intent.doRebind) {
// Intent 參數中要求從新綁定的狀況,執行 requestServiceBindingLocked()
requestServiceBindingLocked(s, b.intent, callerFg, true);
}
} else if (!b.intent.requested) {
// 尚未進行綁定,執行 requestServiceBindingLocked() 方法
requestServiceBindingLocked(s, b.intent, callerFg, false);
}
......
}
return 1;
}
複製代碼
bindServiceLocked()
方法中:
Service
所在的進程尚未啓動,會執行bringUpServiceLocked()
來啓動進程requestServiceBindingLocked()
方法來綁定服務ActiveService.bringUpServiceLocked
bringUpServiceLocked()
方法的主要做用是啓動Service
,核心代碼以下:
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg, boolean whileRestarting, boolean permissionsReviewRequired) throws TransactionTooLargeException {
......
// 判斷是否要在進程隔離下運行
final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
final String procName = r.processName;
String hostingType = "service";
ProcessRecord app;
if (!isolated) {
// 非 isolated 進程
app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
+ " app=" + app);
if (app != null && app.thread != null) {
// 若是進程已經啓動,執行 realStartServiceLocked()
try {
app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode, mAm.mProcessStats);
realStartServiceLocked(r, app, execInFg);
return null;
} ...
}
} else {
// isolated 進程,isolatedProc 第一次爲 null
app = r.isolatedProc;
......
}
// 若是進程尚未啓動,執行 startProcessLocked() 啓動進程
if (app == null && !permissionsReviewRequired) {
if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
hostingType, r.name, false, isolated, false)) == null) {
......
// 進程啓動失敗,返回異常信息
return msg;
}
if (isolated) {
// 給 isolatedProc 賦值
r.isolatedProc = app;
}
}
......
if (!mPendingServices.contains(r)) {
// 將 Service 加入 mPendingServices
mPendingServices.add(r);
}
......
return null;
}
複製代碼
調用bringUpServiceLocked()
方法時
isolated
屬性爲false
,那麼調用realStartServiceLocked()
進入到下一步AMS
的startProcessLocked()
來啓動進程Service
,將其加入到mPendingServices
集合中新啓動進程在哪裏調用的realStartServiceLocked()
來啓動Service
的呢?
按照進程的啓動邏輯,啓動完會執行AMS
的attachApplicationLocked()
方法,而後調用ActiveServices
的attachApplicationLocked()
方法,方法以下:
class ActivityThread{
public static void main(String[] args) {
...
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
...
}
private void attach(boolean system, long startSeq) {
...
final IActivityManager mgr = ActivityManager.getService();
try {
mgr.attachApplication(mAppThread, startSeq);
}
...
}
}
class ActivityManagerService{
public final void attachApplication(IApplicationThread thread, long startSeq) {
synchronized (this) {
...
attachApplicationLocked(thread, callingPid, callingUid, startSeq);
...
}
}
private final boolean attachApplicationLocked(IApplicationThread thread, int pid, int callingUid, long startSeq) {
...
// Find any services that should be running in this process...
if (!badApp) {
// 調用 ActiveService 的 attachApplicationLocked 方法
didSomething |= mServices.attachApplicationLocked(app, processName);
}
...
return true;
}
}
class ActiveService{
boolean attachApplicationLocked(ProcessRecord proc, String processName) throws RemoteException {
boolean didSomething = false;
if (mPendingServices.size() > 0) {
ServiceRecord sr = null;
for (int i=0; i<mPendingServices.size(); i++) {
...
// 此處再一次執行了 realStartServiceLocked()
realStartServiceLocked(sr, proc, sr.createdFromFg);
...
}
}
...
return didSomething;
}
}
複製代碼
這樣,就和bringUpServiceLocked()
的邏輯銜接起來了。
ActiveService.realStartServiceLocked
realStartServiceLocked()
方法的調用流程:
private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException {
...
// 調用 scheduleCreateService 在服務進程中建立 Service 對象
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);
...
// 要求服務進程綁定Binder服務
requestServiceBindingsLocked(r, execInFg);
...
}
private final void requestServiceBindingsLocked(ServiceRecord r, boolean execInFg) throws TransactionTooLargeException {
for (int i=r.bindings.size()-1; i>=0; i--) {
IntentBindRecord ibr = r.bindings.valueAt(i);
if (!requestServiceBindingLocked(r, ibr, execInFg, false)) {
break;
}
}
}
private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i, boolean execInFg, boolean rebind) throws TransactionTooLargeException {
...
if ((!i.requested || rebind) && i.apps.size() > 0) {
try {
...
// 調用 scheduleBindService 綁定服務
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
r.app.repProcState);
...
} ...
}
return true;
}
複製代碼
realStartServiceLocked()
主要的工做就是調用兩個方法,分別是
ApplicationThread
的scheduleCreateService()
requestServiceBindingsLocked()
方法
ApplicationThread
的scheduleBindService()
方法前面介紹過,ApplicationThread
的schedule*
方法其實都是發送相應的處理消息到ActivityThread
中,對應的實現方法分別爲handleCreateService()
和handleBindService()
這兩個方法,咱們來看下:
private void handleCreateService(CreateServiceData data) {
...
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
java.lang.ClassLoader cl = packageInfo.getClassLoader();
// 建立 Service 對象
service = packageInfo.getAppFactory()
.instantiateService(cl, data.info.name, data.intent);
} catch (Exception e) {
...
}
try {
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
...
// 調用 Service 的 onCreate() 方法
service.onCreate();
mServices.put(data.token, service);
...
} catch (Exception e) {
...
}
}
private void handleBindService(BindServiceData data) {
Service s = mServices.get(data.token);
if (s != null) {
try {
data.intent.setExtrasClassLoader(s.getClassLoader());
data.intent.prepareToEnterProcess();
try {
if (!data.rebind) {
// 調用 Service 的 onBind() 方法
IBinder binder = s.onBind(data.intent);
// 調用 AMS 的 publishService 來執行 onServiceConnected() 回調通知
ActivityManager.getService().publishService(
data.token, data.intent, binder);
} else {
// 調用 Service 的 onRebind() 方法
s.onRebind(data.intent);
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
}
ensureJitEnabled();
} catch (RemoteException ex) {
...
}
} catch (Exception e) {
...
}
}
}
複製代碼
上面的兩個handle*
方法把Service
啓動的聲明周期函數都調用完成了。
須要注意的是AMS
的publishService()
方法,該方法會調用ActiveServices
的publishServiceLocked()
方法,實現以下:
class ActivityManagerService{
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
...
ConnectionRecord c = clist.get(i);
// 執行 IServiceConnection 的 connected() 方法
c.conn.connected(r.name, service, false);
...
}
}
final class ConnectionRecord {
...
// IServiceConnection.aidl 的服務類的實如今 LoadedApk 中
final IServiceConnection conn;
}
class LoadedApk{
private static class InnerConnection extends IServiceConnection.Stub {
final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;
InnerConnection(LoadedApk.ServiceDispatcher sd) {
mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
}
public void connected(ComponentName name, IBinder service, boolean dead) throws RemoteException {
// 此處省略一些中間調用過程
doConnected();
}
}
public void doConnected(ComponentName name, IBinder service, boolean dead) {
...
// 調用 onServiceConnected 方法通知 caller
mConnection.onServiceConnected(name, service);
...
}
}
複製代碼
到這裏,bindService()
的過程就結束了,整體而言,Service
部分的啓動仍是比較簡潔的,畢竟對Service
來講只有啓動和中止兩種操做。
ContentProvider
並無生命週期,也沒有狀態的變化。
AMS
中經過ContentProviderRecord
類來記錄ContentProvider
信息,用ContentProviderConnection
類來記錄進程中鏈接ContentProvider
的信息。
AMS
的成員變量mProviderMap
保存了系統中全部ContentProvider
的記錄。
同時在ProcessRecord
中也經過成員變量pubProviders
來記錄進程中全部建立的ContentProvider
,使用成員變量conProviders
來記錄進程中全部使用的ContentProvider
。定義以下:
// class (String) -> ContentProviderRecord
final ArrayMap<String, ContentProviderRecord> pubProviders = new ArrayMap<>();
// All ContentProviderRecord process is using
final ArrayList<ContentProviderConnection> conProviders = new ArrayList<>();
複製代碼
ContentProvider
ContentProvider
用來提供數據的統一訪問方式,若是某個應用中有數據須要提供給其餘應用訪問,不管數據是存儲在數據庫中,仍是儲存在文件中,甚至是網絡中,均可以經過ContentProvider
提供的接口來訪問。官方傳送門:ContentProvider
ContentProvider
的使用建立一個ContentProvider
組件只須要繼承ContentProvider
類便可。子類必須實現父類定義的用於數據增、刪、改、查的抽象接口,分別是:
Uri
插入ContentValues
對象中的數據。原型以下: public abstract Uri insert (Uri uri, ContentValues values) 複製代碼
Uri
刪除selection
條件所匹配的所有記錄。原型以下: public abstract int delete (Uri uri, String selection, String[] selectionArgs) 複製代碼
Uri
修改selection
條件所匹配的所有記錄。原型以下: public abstract int update (Uri uri, ContentValues values, String selection, String[] selectionArgs) 複製代碼
Uri
查詢selection
條件所匹配的所有記錄,並能夠指定排序方式。原型以下: public abstract Cursor query (Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) 複製代碼
上面的接口中都使用了Uri
來表示數據,Uri
前面已經介紹了,但ContentProvider
不能接收任意的Uri
,它指定了Uri
的格式。
以下爲一個簡單示例:
content://android.launcher.settings.provider/app/3
複製代碼
這個Uri
分爲4部分:
content://
authority
:android.launcher.settings.provider
用來標識一個惟一的ContentProvider
Android
軟件包名稱爲com.example.<appname>
,ContentProvider
的受權應該設置爲com.example.<appname>.provider
path
:app
表示數據的類型,由ContentProvider
內部定義
content://com.example.app.provider/table1
:匹配一個名爲table1
的表content://com.example.app.provider/table2/dataset1
:匹配一個名爲dataset1
的表content://com.example.app.provider/*
匹配ContentProvider
中的任何內容id
:最後的代號3
表示數據的第幾項
content://com.example.app.provider/table3/6
,會匹配table3
中對應6
所標識行的內容#
,如content://com.example.app.provider/table3/#
用來匹配任意id
關於Uri
的詳細部分能夠參考官網:內容Uri
模式
爲了讓其餘應用能夠訪問ContentProvider
,必須在AndroidManifest
文件中使用<provider/>
標籤聲明。<provider/>
標籤訂義以下:
<provider android:authorities="list" android:directBootAware=["true" | "false"] android:enabled=["true" | "false"] android:exported=["true" | "false"] android:grantUriPermissions=["true" | "false"] android:icon="drawable resource" android:initOrder="integer" android:label="string resource" android:multiprocess=["true" | "false"] android:name="string" android:permission="string" android:process="string" android:readPermission="string" android:syncable=["true" | "false"] android:writePermission="string" >
. . .
</provider>
複製代碼
官網的使用指南在這裏
<provider/>
標籤的大部分屬性和<application/>
標籤的屬性相同,咱們這裏只簡單介紹下<provider/>
標籤特有的一些屬性:
authorities
:鑑權字符串列表。每種鑑權字符串表示一種ContentProvider
提供的數據。多個鑑權字符串之間用分號分割。
Java
樣式的命名慣例(如com.example.provider.cartoonprovider
)。ContentProvider
子類的名稱。grantUriPermissions
:用來設置可否容許原本沒有權限訪問本組件數據的其餘應用經過受權的方式訪問。例如:
Email
應用收到一個帶有附件的郵件時,應用可能須要調用其餘應用來瀏覽附件,經過受權的方式可讓沒有權限的應用讀取附件的數據Intent
中添加標記FLAG_GRANT_READ_URI_PERMISSION
和FLAG_GRANT_WRITE_URI_PERMISSION
實現true
,或者在<grant-uri-permission/>
中定義相關權限initOrder
:指定ContentProvider
的初始化順序,數據類型爲整型,數值越大越早建立multiprocess
:用來設置該ContentProvider
可否在其餘應用中建立。默認值爲false
true
,則每一個應用進程都有本身的內容提供程序對象。這樣能夠減小進程間通訊的開銷false
,則應用的進程僅共享一個內容提供程序對象。ContentProvider
的定義ContentProvider
類的定義很是簡單,除了幾個保存屬性值的成員變量外,最重要的就是下面代碼中的mTransport
變量:
public abstract class ContentProvider implements ComponentCallbacks2 {
...
private Transport mTransport = new Transport();
...
}
複製代碼
mTransport
變量的類型是Transport
,它是定義在ContentProvider
中的一個內部類,繼承關係圖以下:
從繼承關係就能夠看出Transport
實際上是一個Binder
服務類,它實現了IContentProvider
接口。而其餘組件對ContentProvider
的操做就是經過Transport
提供的接口來完成的
在ActivityThread
中管理ContentProvider
的成員變量有4個,分別是:
final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap
= new ArrayMap<ProviderKey, ProviderClientRecord>();
final ArrayMap<IBinder, ProviderRefCount> mProviderRefCountMap
= new ArrayMap<IBinder, ProviderRefCount>();
final ArrayMap<IBinder, ProviderClientRecord> mLocalProviders
= new ArrayMap<IBinder, ProviderClientRecord>();
final ArrayMap<ComponentName, ProviderClientRecord> mLocalProvidersByName
= new ArrayMap<ComponentName, ProviderClientRecord>();
複製代碼
這些變量的類型都是ArrayMap
,它們儲存的都是ProviderClientRecord
的引用,對象ProviderClientRecord
中保存了ContentProvider
的信息
mProviderMap
保存的是包括本地對象和引用對象在內的全部ContentProvider
對象mProviderRefCountMap
中保存的是對其餘進程中的ContentProvider
對象的引用計數對象,其中保存着被引用對象的IContentProvider
對象mLocalProviders
只保存本地的ContentProvider
對象mLocalProvidersByName
保存的和mLocalProviders
中的同樣,只是能夠經過ComponentName
來查找ContentProvider
對象ContentProvider
一般應用中先經過Context
的getContentResolver()
方法獲得應用中的ContentResolver
對象,而後再調用它的acquireProvider()
方法來獲取IContentProvider
。
acquireProvider()
方法對於應用來講是隱藏的,對於普通應用來講可使用acquireContentProviderClient()
方法來獲取ContentResolver
對象,不過,最後都會調用到acquireProvider()
方法。
ContentResolver
是一個抽象類,真正的實現是ApplicationContentResolver
類,這個類在ContextImpl.java
中定義,acquireProvider()
方法的實現也很簡單,以下所示:
private static final class ApplicationContentResolver extends ContentResolver {
private final ActivityThread mMainThread;
...
@Override
protected IContentProvider acquireProvider(Context context, String auth) {
return mMainThread.acquireProvider(context,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), true);
}
...
}
複製代碼
acquireProvider()
方法只是調用了ActivityThread
的acquireProvider()
方法:
public final IContentProvider acquireProvider( Context c, String auth, int userId, boolean stable) {
// 先查詢已建立的 ContentProvider 集合
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if (provider != null) {
return provider; // 若是已存在,直接返回
}
ContentProviderHolder holder = null;
try {
synchronized (getGetProviderLock(auth, userId)) {
// 調用 AMS 的 getContentProvider 獲得一個 ContentProviderHolder 對象
holder = ActivityManager.getService().getContentProvider(
getApplicationThread(), auth, userId, stable);
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
if (holder == null) {
return null; // 獲取失敗直接返回
}
// 安裝得到的 ContentProvider 對象
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}
複製代碼
ActivityThread
的acquireProvider()
方法:
acquireExistingProvider()
,這個方法將檢查mProviderMap
中是否已經建立了相應的ContentProvider
對象AMS
的getContentProvider()
方法來獲得一個ContentProviderHolder
對象ContentProviderHolder
對象不爲null
,執行installProvider()
安裝這個ContentProvider
咱們看下AMS
的getContentProvider()
方法:
public final ContentProviderHolder getContentProvider( IApplicationThread caller, String name, int userId, boolean stable) {
// 確保不是 isolated 進程
enforceNotIsolatedCaller("getContentProvider");
if (caller == null) {
String msg = "null IApplicationThread when getting content provider "
+ name;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
return getContentProviderImpl(caller, name, null, stable, userId);
}
複製代碼
AMS
的getContentProvider()
方法在簡單檢查參數後,調用了內部的getContentProviderImpl()
方法:
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller, String name, IBinder token, boolean stable, int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
boolean providerRunning = false;
synchronized(this) {
long startTime = SystemClock.uptimeMillis();
ProcessRecord r = null;
if (caller != null) {
r = getRecordForAppLocked(caller);
if (r == null) {
// 若是調用者的進程不存在,拋出異常
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when getting content provider " + name);
}
}
...
// 檢查 ContentProvider 是否已經發布
cpr = mProviderMap.getProviderByName(name, userId);
...
if (providerRunning) {
// ContentProvider 已經發布的狀況
cpi = cpr.info;
String msg;
// 權限檢查
if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser))
!= null) {
throw new SecurityException(msg);
}
if (r != null && cpr.canRunHere(r)) {
// ContentProvider 所在的進程已經存在,而且設置了能夠在調用者的進程建立
// 建立ContentProviderHolder對象
ContentProviderHolder holder = cpr.newHolder(null);
// 並設置一個空的 provider,這樣客戶進程會從新建立一個 ContentProvider
holder.provider = null;
return holder;
}
...
// ContentProvider 已經存在,增長引用計數
conn = incProviderCountLocked(r, cpr, token, stable);
... // 省略進程優先級調整部分
}
if (!providerRunning) {
// ContentProvider 沒有發佈的狀況
try {
// 從 PMS 中獲取 ContentProvider 信息
cpi = AppGlobals.getPackageManager().
resolveContentProvider(name,
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
} catch (RemoteException ex) {
}
if (cpi == null) {
return null;
}
...// 省略一些參數的處理
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
// 先從 mProviderMap 根據 ComponentName 查找 ContentProvider
cpr = mProviderMap.getProviderByClass(comp, userId);
// 此時若是 ContentProvider 爲 null,能夠理解爲第一次啓動的狀況
final boolean firstClass = cpr == null;
if (firstClass) {
...
try {
// 從 PMS 獲取 ContentProvider 所在應用的信息
ApplicationInfo ai =
AppGlobals.getPackageManager().
getApplicationInfo(
cpi.applicationInfo.packageName,
STOCK_PM_FLAGS, userId);
if (ai == null) {
Slog.w(TAG, "No package info for content provider "
+ cpi.name);
return null;
}
ai = getAppInfoForUser(ai, userId);
// 建立一個新的 ContentProviderRecord 對象
cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
} ...
}
if (r != null && cpr.canRunHere(r)) {
// 若是 ContentProviderRecord 能安裝在調用者進程中,返回一個空的 ContentProvider
return cpr.newHolder(null);
}
// 檢測請求的 ContentProvider 是否在 mLaunchingProviders集合中
final int N = mLaunchingProviders.size();
int i;
for (i = 0; i < N; i++) {
if (mLaunchingProviders.get(i) == cpr) {
break;
}
}
// 若是請求的 ContentProvider 不在 mLaunchingProviders 集合中,啓動它
if (i >= N) {
try {
...
// 獲取 ContentProvider 所在進程的 ProcessRecord
ProcessRecord proc = getProcessRecordLocked(
cpi.processName, cpr.appInfo.uid, false);
if (proc != null && proc.thread != null && !proc.killed) {
// 若是對應的進程已經存在
if (!proc.pubProviders.containsKey(cpi.name)) {
// 將 ContentProvider 保存到 pubProviders 中
proc.pubProviders.put(cpi.name, cpr);
try {
// 調用進程的方法來安裝 ContentProvider
proc.thread.scheduleInstallProvider(cpi);
} catch (RemoteException e) {
}
}
} else {
// 若是對應的進程沒有啓動,先啓動進程
proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0, "content provider",
new ComponentName(cpi.applicationInfo.packageName,
cpi.name), false, false, false);
if (proc == null) {
Slog.w(TAG, "Unable to launch app "
+ cpi.applicationInfo.packageName + "/"
+ cpi.applicationInfo.uid + " for provider "
+ name + ": process is bad");
return null;
}
}
cpr.launchingApp = proc;
// 將 ContentProvider 添加到 mLaunchingProviders 集合中
mLaunchingProviders.add(cpr);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
if (firstClass) {
// 第一次啓動,保存到 mProviderMap 中,key 爲 ComponentName
mProviderMap.putProviderByClass(comp, cpr);
}
// 添加到 mProviderMap 中,key 爲 String
mProviderMap.putProviderByName(name, cpr);
// 增長 ContentProvider 的引用計數
conn = incProviderCountLocked(r, cpr, token, stable);
if (conn != null) {
// 設置等待狀態爲 true
conn.waiting = true;
}
}
...
}
// 掛起調用者線程,直到 ContentProvider 安裝完成
final long timeout = SystemClock.uptimeMillis() + CONTENT_PROVIDER_WAIT_TIMEOUT;
synchronized (cpr) {
while (cpr.provider == null) {
if (cpr.launchingApp == null) {
...
return null;
}
try {
...
if (conn != null) {
conn.waiting = true;
}
// 掛起等待
cpr.wait(wait);
if (cpr.provider == null) {
...
return null;
}
} catch (InterruptedException ex) {
} finally {
if (conn != null) {
conn.waiting = false;
}
}
}
}
return cpr != null ? cpr.newHolder(conn) : null;
}
複製代碼
getContentProviderImpl()
方法雖然有點長,但邏輯仍是比較容易理解的,方法中分兩種狀況進行了處理:
ContentProvider
已經發布了
cpr.canRunHere(r)
判斷這個ContentProvider
是否能夠運行在請求者的進程中
ContentProvider
返回,而是將返回的ContentProviderHolder
對象的provider
對象會設置爲null
ContentProvider
計數等數據的處理cpr.newHolder(conn)
canRunHere()
方法判斷的條件之一就是provider
是否設置了multiprocess
屬性並且兩個進程有相同的uid
ContentProvider
沒有發佈
ContentProvider
可否在調用者的進程中建立startProcessLocked()
方法來啓動進程scheduleInstallProvider()
方法來安裝ContentProvider
scheduleInstallProvider()
方法僅僅是發送一個INSTALL_PROVIDER
的消息ContentProvider
添加到mLaunchingProviders
和mProviderMap
集合中
ContentProvider
還未安裝完成ContentProvider
安裝完成AMS
中能夠用多種參數從mProviderMap
中查找ContentProvider
。所以,在儲存數據時會調用putProviderByName()
、putProviderByClass()
等方法把ContentProvider
對象加入,方便之後查找
ContentProvider
前面提到ActivityThread
的scheduleInstallProvider()
方法會發送一個INSTALL_PROVIDER
的消息來開始ContentProvider
的安裝過程。
消息的處理方法是handleInstallProvider()
方法,這個方法又會調用installContentProviders()
方法,代碼以下:
private void installContentProviders( Context context, List<ProviderInfo> providers) {
final ArrayList<ContentProviderHolder> results = new ArrayList<>();
// 循環調用 installProvider() 方法來建立應用全部的 ContentProvider
for (ProviderInfo cpi : providers) {
...
ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}
try {
// 調用 AMS 的 publishContentProviders 方法來進行註冊
ActivityManager.getService().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
複製代碼
installContentProviders()
方法主要作了兩件事情:
installProvider()
方法來安裝ContentProvider
ContentProvider
對象註冊到AMS
中咱們分別來看下
ActivityThread.installProvider
installProvider()
方法以下:
private ContentProviderHolder installProvider(Context context, ContentProviderHolder holder, ProviderInfo info, boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;
IContentProvider provider;
if (holder == null || holder.provider == null) {
// holder.provider 爲空的狀況,說明要新建相關的 ContentProvider
// 先建立 provider 對象
Context c = null;
ApplicationInfo ai = info.applicationInfo;
...// 省略大量的 Context 獲取邏輯
try {
// 採用反射的方式獲取建立 ContentProvider
final java.lang.ClassLoader cl = c.getClassLoader();
LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
if (packageInfo == null) {
// System startup case.
packageInfo = getSystemContext().mPackageInfo;
}
// instantiateProvider 方法其實就是執行了 cl.loadClass(className).newInstance() 操做
// 若是 class 當前進程中不存在,localProvider 就爲空了
localProvider = packageInfo.getAppFactory()
.instantiateProvider(cl, info.name);
// 獲得 ContentProvider 的 Binder 對象
provider = localProvider.getIContentProvider();
if (provider == null) {
// Binder 對象獲取失敗
return null;
}
// localProvider 初始化後執行此方法,最後會執行 ContentProvider 的 onCreate 方法
localProvider.attachInfo(c, info);
} catch (java.lang.Exception e) {
...
return null;
}
} else {
// 不爲空,無需新建,直接使用
provider = holder.provider;
}
ContentProviderHolder retHolder;
synchronized (mProviderMap) {
IBinder jBinder = provider.asBinder();
if (localProvider != null) {
// localProvider 不爲空的狀況
ComponentName cname = new ComponentName(info.packageName, info.name);
// 根據名稱查找 ContentProvider 是否已經存在
ProviderClientRecord pr = mLocalProvidersByName.get(cname);
if (pr != null) {
// pr 不爲空
// 說明已經有人搶先一步建立了,直接使用
provider = pr.mProvider;
} else {
// 不然新建一個 ContentProviderHolder 對象
holder = new ContentProviderHolder(info);
holder.provider = provider;
holder.noReleaseNeeded = true;
// 經過 installProviderAuthoritiesLocked 方法新建 ProviderClientRecord 對象
pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
// 並將pr添加到 mLocalProviders 集合中
mLocalProviders.put(jBinder, pr);
mLocalProvidersByName.put(cname, pr);
}
retHolder = pr.mHolder;
} else {
// localProvider 爲空說明安裝的是對其餘進程中的 ContentProvider 的引用
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc != null) {
// 引用對象已經存在的狀況
// noReleaseNeeded=true 表示能夠永久安裝
// android 經過noReleaseNeeded 和 stable 組合來肯定引用是否從新建立
// 感受此處可先忽略,重點了解流程
if (!noReleaseNeeded) {
// 增長引用計數
incProviderRefLocked(prc, stable);
try {
// 在 AMS 中 移除舊的
ActivityManager.getService().removeContentProvider(
holder.connection, stable);
} catch (RemoteException e) {
//do nothing content provider object is dead any way
}
}
} else {
// 引用對象不存在,建立一個新的引用對象,並添加到集合中
ProviderClientRecord client = installProviderAuthoritiesLocked(
provider, localProvider, holder);
...
mProviderRefCountMap.put(jBinder, prc);
}
retHolder = prc.holder;
}
}
return retHolder;
}
複製代碼
須要注意installProvider()
方法的返回值類型,ContentProviderHolder
類的主要包含了ContentProvider
的IContentProvider
對象以及ProviderInfo
對象,簡要結構以下:
public class ContentProviderHolder implements Parcelable {
public final ProviderInfo info;
public IContentProvider provider;
...
}
複製代碼
而對於installProvider()
方法的調用來講,源碼中只有兩個調用鏈:
一個是*()
->installContentProviders()
->installProvider()
scheduleInstallProvider()
方法就是走的這個ContentProviderHolder
參數是空的localProvider
不爲空ProviderClientRecord
對象的分支另外一個是*()
->acquireProvider()
->installProvider()
通常狀況ContentProviderHolder
參數不爲空
這會致使localProvider
爲空
所以會執行建立ProviderRefCount
引用對象的分支
AMS.publishContentProviders()
方法以下:
public final void publishContentProviders(IApplicationThread caller, List<ContentProviderHolder> providers) {
if (providers == null) {
// 空集合檢查
return;
}
// 對 isolated 標記的進程進行限制
enforceNotIsolatedCaller("publishContentProviders");
synchronized (this) {
// 獲得發佈 ContentProvider 的進程的 ProcessRecord 對象
final ProcessRecord r = getRecordForAppLocked(caller);
if (r == null) {
throw new SecurityException(...;
}
...
final int N = providers.size();
for (int i = 0; i < N; i++) {
ContentProviderHolder src = providers.get(i);
if (src == null || src.info == null || src.provider == null) {
// 對要發佈的 ContentProvider 進行檢查,異常狀況直接跳過
continue;
}
// 查看要發佈ContentProvider進程的 pubProviders 集合
ContentProviderRecord dst = r.pubProviders.get(src.info.name);
if (dst != null) {
// 若是集合中存在該 ContentProvider 的相關信息
// 將其添加到 AMS 的 mProviderMap 中,共兩個接口
ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
// 接口1:putProviderByClass
mProviderMap.putProviderByClass(comp, dst);
String names[] = dst.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
// 接口2:putProviderByName
// 這個主要是按照 authorities 屬性的內容進行存放
mProviderMap.putProviderByName(names[j], dst);
}
int launchingCount = mLaunchingProviders.size();
int j;
boolean wasInLaunchingProviders = false;
for (j = 0; j < launchingCount; j++) {
// 將 ContentProvider 從 mLaunchingProviders中移除
if (mLaunchingProviders.get(j) == dst) {
mLaunchingProviders.remove(j);
wasInLaunchingProviders = true;
j--;
launchingCount--;
}
}
if (wasInLaunchingProviders) {
// 結束超時檢測機制
mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
}
synchronized (dst) {
dst.provider = src.provider;
dst.proc = r;
// 還記得 getContentProviderImpl() 方法中最後的 wait() 方法麼
// 在這裏就會通知喚醒等待的線程
dst.notifyAll();
}
...
}
}
}
}
複製代碼
publishContentProviders()
會先在調用者的進程中查找ContentProvider
的信息,而後進一步完善ContentProviderRecord
數據
authorities
屬性進行關聯putProviderByClass()
增長ComponentName
映射前面的
getContentProviderImpl()
方法中,在檢測到ContentProviderRecord
的provider
對象爲空時會執行ContentProviderRecord
對象的wait()
方法掛起線程
而publishContentProviders()
方法在數據完善後,會經過notifyAll()
來喚醒對應ContentProviderRecord
對象所在的全部線程(PS:同步作的不錯,就是繞的圈有點大哇)