目錄
- 1.前言
- 2.正文
- 2.1 小例子
- 2.2 代碼分析
- 2.2.1 ContextWrapper.getContentResolver() 方法
- 2.2.2 ContextImpl.getContentResolver() 方法
- 2.2.3 ContentResolver.query() 方法
- 2.2.4 ContentResolver.acquireUnstableProvider() 方法
- 2.2.5 ApplicationContentResolver.acquireUnstableProvider() 方法
- 2.2.6 ActivityThread.acquireProvider() 方法
- 2.2.6.1 ActivityThread.acquireExistingProvider() 方法
- 2.2.6.2 ActivityManagerNative.getDefault() 方法
- 2.2.7 ActivityManagerProxy.getContentProvider() 方法
- 2.2.8 ActivityManagerNative.onTransact() 方法
- 2.2.9 ActivityManagerService.getContentProvider() 方法
- 2.2.10 ActivityManagerService.getContentProviderImpl() 方法
- 2.2.10.1 ProviderMap 類
- 2.2.10.2 ContentProviderRecord.canRunHere() 方法
- 2.2.11 ActivityManagerService.startProcessLocked() 方法
- 2.2.11.1 ActivityManagerService.newProcessRecordLocked() 方法
- 2.2.12 ActivityManagerService.startProcessLocked() 方法
- 2.2.13 Process.start() 方法
- 2.2.14 ActivityThread.main() 方法
- 2.2.15 ActivityThread.attach() 方法
- 2.2.16 ActivityManagerService.attachApplication() 方法
- 2.2.17 ActivityManagerService.attachApplicationLocked() 方法
- 2.2.17.1 ActivityManagerService.generateApplicationProvidersLocked() 方法
- 2.2.18 ApplicationThreadProxy.bindApplication() 方法
- 2.2.19 ApplicationThreadNative.onTransact() 方法
- 2.2.20 ApplicationThread.bindApplication() 方法
- 2.2.21 H.handleMessage() 方法
- 2.2.22 ActivityThread.handleBindApplication() 方法
- 2.2.23 ActivityThread.installContentProviders() 方法
- 2.2.24 ActivityThread.installProvider() 方法
- 2.2.24.1 ContentProvider.attachInfo() 方法
- 2.2.24.2 ActivityThread.installProviderAuthoritiesLocked() 方法
- 2.2.25 ActivityManagerService.publishContentProviders() 方法
- 2.2.26 ActivityManagerService.getContentProviderImpl() 方法跳出回圈等待 provider 發布
- 2.2.27 ActivityThread.acquireProvider() 方法
- 2.2.27.1 ActivityThread.installProvider() 方法
- 2.2.27.2 ActivityThread.installProviderAuthoritiesLocked() 方法
- 2.2.28 ContentProviderProxy.query() 方法
- 2.2.29 ContentProviderNative.onTransact() 方法
- 2.2.30 Transport.query() 方法
- 2.2.31 BookProvider.query() 方法
- 3.最后
- 4.參考
1.前言
ContentProvider (內容提供者)是 Android 的四大組件之一,是一種內容共享型組件,它提供了資料的統一訪問格式,通過單一的 ContentResolver 介面把資料提供給其他應用,只有在多個應用之間分享資料時,ContentProvider 才是必須的,
由 ContentResolver 介面發起的insert(增加)、delete(洗掉)、update(更新)、query(查詢)資料操作會發給 ContentProvider 對應的 insert(增加)、delete(洗掉)、update(更新)、query(查詢)方法,
我們一般認為,內容提供者的資料來源是資料庫,但實際上,內容提供者對底層的資料存盤方式沒有任何要求,可以使用資料庫,也可以使用普通的檔案,也可以使用記憶體中的一個物件,還可以使用網路資料,
換句話說,ContentProvider 提供了一種抽象,隔離了資料的使用方和資料的提供方,這樣即便資料的提供方修改了資料存盤實作,也不會影響到依賴資料訪問的其他現有應用(即資料的使用方),具體而言,內容提供者使用資料庫作為資料來源,當它把資料來源更改為 SharedPreferences 時,資料的使用方是不會受到影響的,資料的使用方是感知不到的,
在實際開發中,我們使用別人提供的內容提供者的時候比較多,比如使用 Android 系統中自帶的電話簿(ContactsContract)獲取聯系人串列,使用 Android 系統中自帶的 MediaStore(媒體庫)獲取媒體資訊(如 MediaStore.Audio 音頻資料,MediaStore.Video 視頻資料,MediaStore.Files 檔案資料,MediaStore.Images 圖片資料),Android 框架實作的部分內容提供程式位于 android.provider 包下,
而自己開發內容提供者給別人用比較少,一個應用是使用 ContentProvider 在 library 里直接獲取背景關系,不必再由主工程傳遞背景關系給 library 了(不過,谷歌不推薦這樣做),
本文主要包括:
- 演示一個使用了
ContentProvider的小例子,這也是我們本次要分析的場景; ContentProvider的跨行程啟動程序,這樣我們就可以知道為什么ContentProvider的onCreate()方法要先于Application的onCreate()方法執行;ContentProvider的資料操作方法呼叫程序,這里以query(查詢)方法為例來說明,這樣可以知道由ContentResolver的query方法發起的查詢操作如何最終交由ContentProvider的query方法來執行,
2.正文
2.1 小例子
例子部分請查看BookProvider,
本文不打算講解這個例子了,因為本文已經很長了,
這里說明一下分析的場景:BookProvider 位于一個單獨的行程里面;在客戶端呼叫 ContentResolver 的 query 方法時,BookProvider 所處的行程還未啟動,因此需要先開啟目標行程,
2.2 代碼分析
2.2.1 ContextWrapper.getContentResolver() 方法
@Override
public ContentResolver getContentResolver() {
return mBase.getContentResolver();
}
這里的 ContextWrapper其實是裝飾器設計模式的應用了,類結構圖如下所示:

呼叫裝飾類 ContextWrapper 的 getContentResolver()方法,內部真正呼叫的是核心實作類 ContextImpl 的 getContentResolver()方法,所以 mBase 實際上是一個 ContextImpl 型別的物件,
2.2.2 ContextImpl.getContentResolver() 方法
class ContextImpl extends Context {
private final ApplicationContentResolver mContentResolver;
private ContextImpl(ContextImpl container, ActivityThread mainThread,
LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted,
Display display, Configuration overrideConfiguration) {
...
// 初始化 ApplicationContentResolver 物件,
mContentResolver = new ApplicationContentResolver(this, mainThread, user);
}
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
private static final class ApplicationContentResolver extends ContentResolver {
private final ActivityThread mMainThread;
private final UserHandle mUser;
public ApplicationContentResolver(
Context context, ActivityThread mainThread, UserHandle user) {
super(context);
mMainThread = Preconditions.checkNotNull(mainThread);
mUser = Preconditions.checkNotNull(user);
}
@Override
protected IContentProvider acquireProvider(Context context, String auth) {
return mMainThread.acquireProvider(context,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), true);
}
... // 省略一些重寫的方法
}
}
可以看到,最侄訓傳的是一個 mContentResolver 物件,它的型別是 ApplicationContentResolver,它是在 ContextImpl 的構造方法中被初始化的,
在初始化ApplicationContentResolver物件時,傳入了 ActivityThread 物件,并且 ApplicationContentResolver 物件內部持有了 ActivityThread 物件,
ApplicationContentResolver是繼承于 ContentResolver 抽象類的具體類,它是 ContextImpl 的私有嵌套內部類,
讓我們用 UML 圖來表示它們之間的關系:

可以看到,ApplicationContentResolver 實作了基類 ContentResolver 的一系列 acquireXXX 以及 releaseXXX 的方法,而方法內部的實作則是委托給 ActivityThread 物件的相應方法來完成,
2.2.3 ContentResolver.query() 方法
- Uri uri, Uri.parse(“content://com.wzc.chapter_9.bookprovider/books”)
- String[] projection, null
- String selection, null
- String[] selectionArgs, null
- String sortOrder, null
public final Cursor query(Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder) {
return query(uri, projection, selection, selectionArgs, sortOrder, null);
}
呼叫多載的 query() 方法:
- Uri uri, Uri.parse(“content://com.wzc.chapter_9.bookprovider/books”)
- String[] projection, null
- String selection, null
- String[] selectionArgs, null
- String sortOrder, null
- CancellationSignal cancellationSignal, null
public final Cursor query(final Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder,
CancellationSignal cancellationSignal) {
// 獲取 IContentProvider 物件,見【2.2.4】,
IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
return null;
}
Cursor qCursor = null;
try {
ICancellationSignal remoteCancellationSignal = null;
if (cancellationSignal != null) { // cancellationSignal 為 null,所以不會進入此分支
...
}
try {
// 獲取 Cursor 物件,見【2.2.28】
qCursor = unstableProvider.query(mPackageName, uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);
} catch (DeadObjectException e) {
// 省略例外的代碼部分
}
if (qCursor == null) {
return null;
}
qCursor.getCount();
// 把 Cursor 物件包裝成 CursorWrapperInner 物件回傳.
CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, acquireProvider(uri));
qCursor = null;
return wrapper;
} catch (RemoteException e) {
return null;
} finally {
if (qCursor != null) {
qCursor.close();
}
if (unstableProvider != null) {
releaseUnstableProvider(unstableProvider);
}
}
}
2.2.4 ContentResolver.acquireUnstableProvider() 方法
- Uri uri, Uri.parse(“content://com.wzc.chapter_9.bookprovider/books”)
public static final String SCHEME_CONTENT = "content";
public final IContentProvider acquireUnstableProvider(Uri uri) {
if (!SCHEME_CONTENT.equals(uri.getScheme())) { // uri.getScheme() 是 "content",不會進入此分支
return null;
}
String auth = uri.getAuthority(); // auth 等于 "com.wzc.chapter_9.bookprovider"
if (auth != null) { // 進入此分支
return acquireUnstableProvider(mContext, uri.getAuthority());
}
return null;
}
呼叫多載的 acquireUnstableProvider() 方法:
protected abstract IContentProvider acquireUnstableProvider(Context c, String name);
這是一個抽象方法,它的實作是在 ContextImpl.ApplicationContentResolver 里面,
這里出現了 IContentProvider 介面,這是一個用于跨行程通信的介面,與之相關的 binder 客戶端與服務端類 UML 圖如下:

比較疑惑的是,這里回傳的 IContentProvider 物件到底是個什么物件,是 ContentProviderProxy 物件,還是 ContentProviderNative 物件呢?
我們現在還不得而知,但是我們可以通過Debug:
getContentResolver().acquireUnstableProvider(BookStore.Books.CONTENT_URI);
得到:

很明顯,回傳的是一個 ContentProviderProxy 物件,也就是說,是一個 binder 客戶端物件,
那么,客戶端行程就是通過這個 binder 客戶端物件,經過 binder 驅動,發起遠程呼叫,從 binder 服務端那里獲取到資料的,
而由上面的類圖可知,IContentProvider 介面對應的 binder 服務端物件是 ContentProvider 的內部類 Transport,
這樣就構成了 binder 跨行程通信的客戶端和服務端了,
2.2.5 ApplicationContentResolver.acquireUnstableProvider() 方法
- Context c, ContextImpl 物件
- String auth, “com.wzc.chapter_9.bookprovider”
@Override
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
return mMainThread.acquireProvider(c,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), false);
}
本次的 auth 經過 ContentProvider.getAuthorityWithoutUserId(auth) 后,仍為 "com.wzc.chapter_9.bookprovider",
resolveUserIdFromAuthority(auth) 的結果是 0,
2.2.6 ActivityThread.acquireProvider() 方法
- Context c, ContextImpl 物件
- String auth, “com.wzc.chapter_9.bookprovider”
- int userId, 0
- boolean stable, false
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
// 獲取已經存在的 IContentProvider 物件,見【2.2.6.1】
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if (provider != null) { // 本次分析 provider 為 null,所以不會進入此分支了,
// 如果可以獲取到已經存在的 IContentProvider 物件,就直接回傳了,
return provider;
}
IActivityManager.ContentProviderHolder holder = null;
try {
// 向 AMS 請求獲取 ContentProviderHolder 物件
// ActivityManagerNative.getDefault() 的回傳 ActivityManagerProxy 物件,分析見 【2.2.6.2】
// ActivityManagerProxy.getContentProvider() 分析,見【2.2.7】
holder = ActivityManagerNative.getDefault().getContenProvider(
getApplicationThread(), auth, userId, stable)
} catch (RemoteException ex) {
}
if (holder == null) {
// 如果 AMS 回傳的 ContentProviderHolder 物件為 null,則直接回傳 null,
return null;
}
// 安裝 provider 將會增加參考計數,并且打破競爭中的任何聯系,
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}
該方法的主要作用:
- 嘗試獲取已經存在的
IContentProvider物件; - 如果 1 獲取不到
IContentProvider物件,再向 AMS 請求獲取ContentProviderHolder物件; - 如果 2 獲取的
ContentProviderHolder物件不為null,則呼叫安裝 provider 的方法,
2.2.6.1 ActivityThread.acquireExistingProvider() 方法
- Context c, ContextImpl 物件
- String auth, “com.wzc.chapter_9.bookprovider”
- int userId, 0
- boolean stable, false
// mProviderMap 以 ProviderKey 為鍵,以 ProviderClientRecord 為值
final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap
= new ArrayMap<ProviderKey, ProviderClientRecord>();
final ArrayMap<IBinder, ProviderRefCount> mProviderRefCountMap
= new ArrayMap<IBinder, ProviderRefCount>();
public final IContentProvider acquireExistingProvider(
Context c, String auth, int userId, boolean stable) {
synchronized (mProviderMap) {
// 把 auth 和 userId 封裝為 ProviderKey 物件,
final ProviderKey key = new ProviderKey(auth, userId);
// 從 mProviderMap 查找對應 ProviderKey 的 ProviderClientRecord 物件
final ProviderClientRecord pr = mProviderMap.get(key);
if (pr == null) {
// 本次分析,獲取的 pr 為 null,所以直接在這里回傳了,
return null;
}
// ###下面的代碼,本次分析不會走到了###,
IContentProvider provider = pr.mProvider;
IBinder jBinder = provider.asBinder();
if (!jBinder.isBinderAlive()) {
// provider 的宿主行程已經死亡,不能使用這個 binder 了,
handleUnstableProviderDiedLocked(jBinder, true);
return null;
}
// 如果我們有一個 ProviderRefCount 物件,就僅僅增加參考計數;
// 否則,說明這個 provider 沒有被參考計數,也不需要被釋放,
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc != null) {
// 增加參考計數
incProviderRefLocked(prc, stable);
}
return provider;
}
}
mProviderMap 是一個 ArrayMap 物件,是 ActivityThread 的一個成員變數,表示在該行程記錄的 provider 資訊,不過,目前它還是一個空的 ArrayMap 物件,
mProviderMap 是一個重要的集合,我們繪制下它的結構圖如下:

2.2.6.2 ActivityManagerNative.getDefault() 方法
// ActivityManagerNative 類:
static public IActivityManager getDefault() {
return gDefault.get();
}
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
IActivityManager am = asInterface(b);
return am;
}
};
// 因為這里客戶端和服務端不處于同一個行程,所以這個方法回傳的是 ActivityManagerProxy 物件,
static public IActivityManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IActivityManager in =
(IActivityManager)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}
return new ActivityManagerProxy(obj);
}
public abstract class Singleton<T> {
private T mInstance;
protected abstract T create();
public final T get() {
synchronized (this) {
if (mInstance == null) {
mInstance = create();
}
return mInstance;
}
}
}
這里采用的是區域單例技術,保證獲取到的 IActivityManager 物件總是一個物件,實際上是 ActivityManagerProxy 物件,
2.2.7 ActivityManagerProxy.getContentProvider() 方法
-
IApplicationThread caller, ActivityThread 的 ApplicationThread 物件 mApplicationThread,是 binder 服務端物件
-
String name, “com.wzc.chapter_9.bookprovider”
-
int userId, 0
-
boolean stable, false
class ActivityManagerProxy implements IActivityManager
{
public ContentProviderHolder getContentProvider(IApplicationThread caller,
String name, int userId, boolean stable) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
data.writeString(name);
data.writeInt(userId);
data.writeInt(stable ? 1 : 0);
// 見[【2.2.8】
mRemote.transact(GET_CONTENT_PROVIDER_TRANSACTION, data, reply, 0);
reply.readException();
int res = reply.readInt();
ContentProviderHolder cph = null;
if (res != 0) {
cph = ContentProviderHolder.CREATOR.createFromParcel(reply);
}
data.recycle();
reply.recycle();
return cph;
}
}
這個方法仍是在客戶端行程呼叫的,
mRemote.transact() 是客戶端行程發起 binder 通信的方法,經過 binder 驅動,最后會到 binder 服務端 ActivityManagerNative的 onTransact() 方法,
這里遠程呼叫回傳的 reply 值,最侄訓轉為一個 ContentProviderHolder 物件,回傳給客戶端呼叫者,從 ContentProviderHolder 的名字來看,它是 ContentProvider 的持有者或者說 ContentProviderHolder 的容器,
ContentProviderHolder 類是 IActivityManager 介面的嵌套內部類,
public static class ContentProviderHolder implements Parcelable {
public final ProviderInfo info;
public IContentProvider provider;
public IBinder connection;
public boolean noReleaseNeeded;
public ContentProviderHolder(ProviderInfo _info) {
info = _info;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
info.writeToParcel(dest, 0);
if (provider != null) {
dest.writeStrongBinder(provider.asBinder());
} else {
dest.writeStrongBinder(null);
}
dest.writeStrongBinder(connection);
dest.writeInt(noReleaseNeeded ? 1:0);
}
public static final Parcelable.Creator<ContentProviderHolder> CREATOR
= new Parcelable.Creator<ContentProviderHolder>() {
@Override
public ContentProviderHolder createFromParcel(Parcel source) {
return new ContentProviderHolder(source);
}
@Override
public ContentProviderHolder[] newArray(int size) {
return new ContentProviderHolder[size];
}
};
private ContentProviderHolder(Parcel source) {
info = ProviderInfo.CREATOR.createFromParcel(source);
// 這里拿到把 BinderProxy 物件,轉換成了 ContentProviderProxy 物件,
provider = ContentProviderNative.asInterface(
source.readStrongBinder());
connection = source.readStrongBinder();
noReleaseNeeded = source.readInt() != 0;
}
}
可以看到,ContentProviderHolder 是一個實作了 Parcelable 介面的類,所以它可以被序列化和反序列化,它封裝了 provider 在清單中注冊的資訊,ContentProviderProxy 物件等,
2.2.8 ActivityManagerNative.onTransact() 方法
public abstract class ActivityManagerNative extends Binder implements IActivityManager
{
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
switch (code) {
case GET_CONTENT_PROVIDER_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
// 獲取的是 ApplicationThreadProxy 物件
IApplicationThread app = ApplicationThreadNative.asInterface(b);
String name = data.readString();
int userId = data.readInt();
boolean stable = data.readInt() != 0;
// 呼叫到這里,進入 AMS,見【2.2.9】
ContentProviderHolder cph = getContentProvider(app, name, userId, stable);
reply.writeNoException();
if (cph != null) {
reply.writeInt(1);
cph.writeToParcel(reply, 0);
} else {
reply.writeInt(0);
}
return true;
}
}
}
}
這個方法是運行在服務端行程了,在這里是 system_server 行程,
ActivityManagerNative 是抽象類,getContentProvider() 是它的一個抽象方法,
ActivityManagerService 繼承了 ActivityManagerNative,實作了 getContentProvider() 這個抽象方法,
2.2.9 ActivityManagerService.getContentProvider() 方法
- IApplicationThread caller, ApplicationThreadProxy 物件
- String name, “com.wzc.chapter_9.bookprovider”
- int userId, 0
- boolean stable, false
@Override
public final ContentProviderHolder getContentProvider(
IApplicationThread caller, String name, int userId, boolean stable) {
enforceNotIsolatedCaller("getContentProvider");
if (caller == null) {
throw new SecurityException("");
}
// 見【2.2.10】
return getContentProviderImpl(caller, name, null, stable, userId);
}
該方法的主要作用:檢查 IApplicationThread caller 是不是為 null,通過檢查后,呼叫 getContentProviderImpl() 方法,
2.2.10 ActivityManagerService.getContentProviderImpl() 方法
- IApplicationThread caller, ApplicationThreadProxy 物件
- String name, “com.wzc.chapter_9.bookprovider”
- IBinder token, null
- boolean stable, false
- int userId, 0
public final class ActivityManagerService extends ActivityManagerNative {
// ProviderMap 結構,見【2.2.10.1】
final ProviderMap mProviderMap;
// mLaunchingProviders 記錄客戶端正在等待publish的ContentProviderRecord物件
// 應用當前正在被啟動,provider 在被發布后就會從這個串列里面移除掉,
final ArrayList<ContentProviderRecord> mLaunchingProviders
= new ArrayList<ContentProviderRecord>();
public ActivityManagerService(Context systemContext) {
mProviderMap = new ProviderMap(this);
}
private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, boolean stable, int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
synchronized(this) {
long startTime = SystemClock.elapsedRealtime();
ProcessRecord r = null;
if (caller != null) { // call 不為 null,進入此分支
// 獲取發起方的行程記錄 ProcessRecord 物件
r = getRecordForAppLocked(caller);
}
boolean checkCrossUser = true;
// 首先,檢查這個 provider 是否已經被發布了
// 本次分析,我們的 provider 還未發布,因此這里會回傳 null,cpr 為 null,
cpr = mProviderMap.getProviderByName(name, userId);
// userId 是 0,UserHandle.USER_OWNER 也是 0,所以不會進入此分支
if (cpr == null && userId != UserHandle.USER_OWNER) {
...
}
boolean providerRunning = cpr != null; // false,表示目標 provider 不存在,
if (providerRunning) { // providerRunning 為 false,不會進入此分支
...// 省略掉很多代碼,它們與本次分析無關,
}
boolean singleton;
if (!providerRunning) { // 進入此分支
try {
// 通過 PKMS 來決議清單檔案中的 provider 資訊,ProviderInfo 物件
cpi = AppGlobals.getPackageManager().
resolveContentProvider(name,
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
} catch (RemoteException ex) {
}
if (cpi == null) {
return null;
}
// 本次我們分析的是單實體 provider,因此 singleton 為 true,
singleton = isSingleton(cpi.processName, cpi.applicationInfo,
cpi.name, cpi.flags)
&& isValidSingletonCall(r.uid, cpi.applicationInfo.uid);
if (singleton) {
userId = UserHandle.USER_OWNER;
}
// getAppInfoForUser 獲取包含用戶資訊的 ApplicationInfo 物件,
cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);
String msg;
// 檢查發起方行程是否有權限獲取此 provider
if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, !singleton))
!= null) {
throw new SecurityException(msg);
}
...
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
// 根據 class,從 mProviderMap 里面獲取 ContentProviderRecord
// 本次分析,provider 還未發布,所以回傳 null,所以 cpr 為 null,
cpr = mProviderMap.getProviderByClass(comp, userId);
final boolean firstClass = cpr == null; // firstClass 為 true
if (firstClass) {
final long ident = Binder.clearCallingIdentity();
try {
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);
} catch (RemoteException ex) {
} finally {
Binder.restoreCallingIdentity(ident);
}
}
// 到這里,ContentProviderRecord cpr 物件一定不會為 null 了,
// canRunHere 判斷 ContentProvider 是否能運行在發起方所在行程
// 本次分析,canRunHere() 回傳 false,見【2.2.10.2】分析
if (r != null && cpr.canRunHere(r)) { // 不會進入此分支
return cpr.newHolder(null);
}
// 遍歷 mLaunchingProviders,如果有元素等于 cpr,就跳出回圈,
final int N = mLaunchingProviders.size();
int i;
for (i=0; i<N; i++) {
if (mLaunchingProviders.get(i) == cpr) {
break;
}
}
if (i >= N) { // i >= N,說明上面的回圈走完了,沒有中途跳出,也就是說,沒有發現有元素等于 cpr,
final long origId = Binder.clearCallingIdentity();
try {
try {
AppGlobals.getPackageManager().setPackageStoppedState(
cpr.appInfo.packageName, false, userId);
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
}
// 獲取 provider 所在行程(行程名字:"com.wzc.chapter_9.provider")的行程記錄物件
ProcessRecord proc = getProcessRecordLocked(
cpi.processName, cpr.appInfo.uid, false);
// 行程記錄存在且其 IApplicationThread 成員存在,則計劃安裝此provider,
// 本次分析,目標行程還沒有啟動,所以不會進入此分支
if (proc != null && proc.thread != null) {
proc.pubProviders.put(cpi.name, cpr);
try {
proc.thread.scheduleInstallProvider(cpi);
} catch (RemoteException e) {
}
} else {
// 否則,開啟對應的行程,見【2.2.11】
proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0, "content provider",
new ComponentName(cpi.applicationInfo.packageName,
cpi.name), false, false, false);
if (proc == null) {
return null;
}
}
// 把目標行程物件賦值給 cpr 的 launchingApp 成員變數
cpr.launchingApp = proc;
mLaunchingProviders.add(cpr);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
if (firstClass) {
mProviderMap.putProviderByClass(comp, cpr);
}
mProviderMap.putProviderByName(name, cpr);
conn = incProviderCountLocked(r, cpr, token, stable);
if (conn != null) {
conn.waiting = true;
}
}
}
// 回圈等待 provider 發布
// 本次分析,cpr.provider 為 null,因此會進入 while 回圈,
synchronized (cpr) {
while (cpr.provider == null) {
if (cpr.launchingApp == null) {
return null;
}
try {
if (conn != null) {
conn.waiting = true;
}
cpr.wait();
} catch (InterruptedException ex) {
} finally {
if (conn != null) {
conn.waiting = false;
}
}
}
}
return cpr != null ? cpr.newHolder(conn) : null;
}
}
該方法的主要作用:
- 嘗試從 AMS 持有的資料結構
mProviderMap中獲取到ContentProviderRecord物件; - 如果 1 無法拿到
ContentProviderRecord物件,說明 provider 還未發布,對發起方行程進行權限檢查等操作后,創建一個ContentProviderRecord物件; - 檢查 provider 是否可以運行在發起方行程里面,本次不可以;
- 判斷
ContentProviderRecord是否是客戶端在等待發布的,不是,則進入 5,是,則進入 6; - 判斷 provider 的目標行程是否存在,不存在,則需要去開啟對應的目標行程;
- 把
ContentProviderRecord存到mProviderMap資料結構中,增加 provider 參考計數; - 回圈等待 provider 發布,
2.2.10.1 ProviderMap 類
public final class ProviderMap {
private static final String TAG = "ProviderMap";
private final ActivityManagerService mAm;
// 全域集合:存放的是單實體的 provider 記錄
// 以 authority 為 key,以 CPR 為 value
private final HashMap<String, ContentProviderRecord> mSingletonByName
= new HashMap<String, ContentProviderRecord>();
// 以 class 為 key,以 CPR 為 value
private final HashMap<ComponentName, ContentProviderRecord> mSingletonByClass
= new HashMap<ComponentName, ContentProviderRecord>();
// 按用戶的userId,來管理對應的集合:存放的是多實體的 provider 記錄,
private final SparseArray<HashMap<String, ContentProviderRecord>> mProvidersByNamePerUser
= new SparseArray<HashMap<String, ContentProviderRecord>>();
private final SparseArray<HashMap<ComponentName, ContentProviderRecord>> mProvidersByClassPerUser
= new SparseArray<HashMap<ComponentName, ContentProviderRecord>>();
ProviderMap(ActivityManagerService am) {
mAm = am;
}
ContentProviderRecord getProviderByName(String name) {
return getProviderByName(name, -1);
}
ContentProviderRecord getProviderByName(String name, int userId) {
// 先去全域集合里面查找
ContentProviderRecord record = mSingletonByName.get(name);
if (record != null) {
return record;
}
// 再去當前用戶的集合里面查找
return getProvidersByName(userId).get(name);
}
ContentProviderRecord getProviderByClass(ComponentName name) {
return getProviderByClass(name, -1);
}
ContentProviderRecord getProviderByClass(ComponentName name, int userId) {
// 先去全域集合里面查找
ContentProviderRecord record = mSingletonByClass.get(name);
if (record != null) {
return record;
}
// 再去當前用戶的集合里面查找
return getProvidersByClass(userId).get(name);
}
void putProviderByName(String name, ContentProviderRecord record) {
if (record.singleton) {
mSingletonByName.put(name, record);
} else {
final int userId = UserHandle.getUserId(record.appInfo.uid);
getProvidersByName(userId).put(name, record);
}
}
void putProviderByClass(ComponentName name, ContentProviderRecord record) {
if (record.singleton) {
mSingletonByClass.put(name, record);
} else {
final int userId = UserHandle.getUserId(record.appInfo.uid);
getProvidersByClass(userId).put(name, record);
}
}
void removeProviderByName(String name, int userId) {
if (mSingletonByName.containsKey(name)) {
mSingletonByName.remove(name);
} else {
if (userId < 0) throw new IllegalArgumentException("Bad user " + userId);
HashMap<String, ContentProviderRecord> map = getProvidersByName(userId);
map.remove(name);
if (map.size() == 0) {
mProvidersByNamePerUser.remove(userId);
}
}
}
void removeProviderByClass(ComponentName name, int userId) {
if (mSingletonByClass.containsKey(name)) {
mSingletonByClass.remove(name);
} else {
if (userId < 0) throw new IllegalArgumentException("Bad user " + userId);
HashMap<ComponentName, ContentProviderRecord> map = getProvidersByClass(userId);
map.remove(name);
if (map.size() == 0) {
mProvidersByClassPerUser.remove(userId);
}
}
}
private HashMap<String, ContentProviderRecord> getProvidersByName(int userId) {
if (userId < 0) throw new IllegalArgumentException("Bad user " + userId);
final HashMap<String, ContentProviderRecord> map = mProvidersByNamePerUser.get(userId);
if (map == null) {
HashMap<String, ContentProviderRecord> newMap = new HashMap<String, ContentProviderRecord>();
mProvidersByNamePerUser.put(userId, newMap);
return newMap;
} else {
return map;
}
}
HashMap<ComponentName, ContentProviderRecord> getProvidersByClass(int userId) {
if (userId < 0) throw new IllegalArgumentException("Bad user " + userId);
final HashMap<ComponentName, ContentProviderRecord> map
= mProvidersByClassPerUser.get(userId);
if (map == null) {
HashMap<ComponentName, ContentProviderRecord> newMap
= new HashMap<ComponentName, ContentProviderRecord>();
mProvidersByClassPerUser.put(userId, newMap);
return newMap;
} else {
return map;
}
}
}
雖然這個類的名字叫 ProviderMap,但是它并不是一個 Map 的子類,這個類采用組合的方式,內部管理了 mSingletonByName ,mSingletonByClass,mProvidersByNamePerUser,mProvidersByClassPerUser四個集合物件,并定義了方法來實作元素的添加,獲取,洗掉操作,
思考一下:為什么要使用組合的方式而不是繼承的方式來實作 ProviderMap?
因為采用繼承的方式,就要受限于父類的介面;而這里要實作對單實體集合和多實體集合的分別管理,采用組合可以達到這種目的,
這個類在后面用的比較多,所以這里重點說明了一下它的結構,
2.2.10.2 ContentProviderRecord.canRunHere() 方法
- ProcessRecord app, 呼叫方行程記錄物件
final class ContentProviderRecord {
public final ProviderInfo info;
final int uid;
public boolean canRunHere(ProcessRecord app) {
return (info.multiprocess || info.processName.equals(app.processName))
&& uid == app.info.uid;
}
}
該方法的主要作用:判斷 provider 是否可以運行在呼叫方行程,需要滿足兩個條件:
- 條件一:provider 在清單檔案中設定了
android:multiprocess="true",或者 provider 的行程名字和呼叫方行程名字一致; - 條件二:provider 的 uid 和呼叫方行程的 uid 一致,
本次分析,不滿足條件一:provider 在清單檔案中沒有設定 android:multiprocess="true",且provider 的行程名字("com.wzc.chapter_9.provider")和呼叫方行程名字("com.wzc.chapter_9")不一致,所以這個方法回傳 false,即 provider 不可以運行在呼叫方行程,
2.2.11 ActivityManagerService.startProcessLocked() 方法
- String processName, “com.wzc.chapter_9.provider”
- ApplicationInfo info, 清單檔案中的 application 節點資訊
- boolean knownToBeDead, false
- int intentFlags, 為 0,
- String hostingType, “content provider”
- ComponentName hostingName, ContentProvider 的組件名物件,即包名 + 類名的組合
- boolean allowWhileBooting, false
- boolean isolated, false
- boolean keepIfLarge false
final ProcessRecord startProcessLocked(String processName,
ApplicationInfo info, boolean knownToBeDead, int intentFlags,
String hostingType, ComponentName hostingName, boolean allowWhileBooting,
boolean isolated, boolean keepIfLarge) {
return startProcessLocked(processName, info, knownToBeDead, intentFlags, hostingType,
hostingName, allowWhileBooting, isolated, 0 /* isolatedUid */, keepIfLarge,
null /* ABI override */, null /* entryPoint */, null /* entryPointArgs */,
null /* crashHandler */);
}
呼叫 startProcessLocked 的多載方法:
- String processName, “com.wzc.chapter_9.provider”
- ApplicationInfo info, 清單檔案中的 application 節點資訊
- boolean knownToBeDead, false
- int intentFlags, 為 0,
- String hostingType, “content provider”
- ComponentName hostingName, ContentProvider 的組件名物件,即包名 + 類名的組合
- boolean allowWhileBooting, false
- boolean isolated, false
- int isolatedUid, 0
- boolean keepIfLarge false
- String abiOverride, null
- String entryPoint, null
- String[] entryPointArgs, null
- Runnable crashHandler, null
final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName,
boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge,
String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
long startTime = SystemClock.elapsedRealtime();
ProcessRecord app;
if (!isolated) { // isolated 為 false,進入此分支
// 獲取行程名為 "com.wzc.chapter_9.provider" 的行程記錄物件,這里還沒有,所以回傳 null,
app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
} else {
...
}
if (app != null && app.pid > 0) { // app 為 null,不會進入此分支
...
}
String hostingNameStr = hostingName != null
? hostingName.flattenToShortString() : null;
if (!isolated) { // 進入此分支
if ((intentFlags&Intent.FLAG_FROM_BACKGROUND) != 0) { // intentFlags 為 0,不會進入此分支
...
} else { // 進入此分支
mProcessCrashTimes.remove(info.processName, info.uid);
if (mBadProcesses.get(info.processName, info.uid) != null) {
mBadProcesses.remove(info.processName, info.uid);
if (app != null) {
app.bad = false;
}
}
}
}
if (app == null) { // 進入此分支
// 創建新的 ProcessRecord 物件,見【2.2.11.1】,
app = newProcessRecordLocked(info, processName, isolated, isolatedUid);
app.crashHandler = crashHandler;
if (app == null) {
return null;
}
mProcessNames.put(processName, app.uid, app);
...
} else {
...
}
// 如果系統還沒有就緒,那么就推遲啟動行程直到系統就緒,
if (!mProcessesReady
&& !isAllowedWhileBooting(info)
&& !allowWhileBooting) {
if (!mProcessesOnHold.contains(app)) {
mProcessesOnHold.add(app);
}
return app;
}
// 啟動新行程,見【2.2.12】
startProcessLocked(
app, hostingType, hostingNameStr, abiOverride, entryPoint, entryPointArgs);
return (app.pid != 0) ? app : null;
}
2.2.11.1 ActivityManagerService.newProcessRecordLocked() 方法
- ApplicationInfo info, 清單檔案中的 application 節點資訊
- String customProcess, “com.wzc.chapter_9.provider”
- boolean isolated, false
- int isolatedUid, 0
final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
boolean isolated, int isolatedUid) {
String proc = customProcess != null ? customProcess : info.processName;
BatteryStatsImpl.Uid.Proc ps = null;
BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
int uid = info.uid;
if (isolated) { // isolated 為 false,所以不會進入此分支
...
}
return new ProcessRecord(stats, info, proc, uid);
}
該方法的作用:創建了一個 ProcessRecord 物件而已,
2.2.12 ActivityManagerService.startProcessLocked() 方法
- ProcessRecord app, 新創建的 ProcessRecord 物件
- String hostingType, “service”
- String hostingNameStr, Service 的組件名物件轉換成的字串,即"com.wzc.chapter_9/.provider.BookProvider"
- String abiOverride, null
- String entryPoint, null
- String[] entryPointArgs, null
private final void startProcessLocked(ProcessRecord app, String hostingType,
String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
long startTime = SystemClock.elapsedRealtime();
if (app.pid > 0 && app.pid != MY_PID) { // app.pid 為 0,不會進入此分支
...
}
mProcessesOnHold.remove(app);
updateCpuStats();
try {
int uid = app.uid;
int[] gids = null;
int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
if (!app.isolated) { // isolated 為 false,會進入此分支
...
}
...
boolean isActivityProcess = (entryPoint == null); // true
// entryPoint 是 null,所以 entryPoint 會被賦值為 "android.app.ActivityThread"
if (entryPoint == null) entryPoint = "android.app.ActivityThread";
// 開啟行程:請求 zygote 去開啟行程,見[2.2.13]
Process.ProcessStartResult startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
app.info.dataDir, entryPointArgs);
...
// 設定 ProcessRecord app 的成員變數
app.setPid(startResult.pid);
app.usingWrapper = startResult.usingWrapper;
app.removed = false;
app.killed = false;
app.killedByAm = false;
synchronized (mPidsSelfLocked) {
// 存盤新行程的 pid 和 ProcessRecord 物件鍵值對到 mPidsSelfLocked,
this.mPidsSelfLocked.put(startResult.pid, app);
if (isActivityProcess) {
Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
msg.obj = app;
mHandler.sendMessageDelayed(msg, startResult.usingWrapper
? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
}
}
} catch (RuntimeException e) {
...
}
}
2.2.13 Process.start() 方法
- final String processClass, “android.app.ActivityThread”
- final String niceName, “com.wzc.chapter_9.provider”
- int uid,
- int gid,
- int[] gids,
- int debugFlags,
- int mountExternal,
- int targetSdkVersion, 目標 sdk 版本
- String seInfo,
- String abi, 架構
- String instructionSet, 指令集
- String appDataDir,
- String[] zygoteArgs, null
public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String[] zygoteArgs) {
try {
// 通過 zygote 開啟行程
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
throw new RuntimeException(
"Starting VM process through Zygote failed", ex);
}
}
system_server 行程里,呼叫 Process.start() 方法,通過 socket 向 zygote 行程發送創建新行程的請求;
在 zygote 行程里面,在執行ZygoteInit.main()后便進入runSelectLoop()回圈體內,當有客戶端連接時便會執行ZygoteConnection.runOnce()方法,再經過層層呼叫后 fork 出新的應用行程;
新的行程創建后,執行handleChildProc方法,最后呼叫ActivityThread.main()方法,
這部分詳細請參考理解Android行程創建流程-袁輝輝,
本文會直接跳到 ActivityThread.main() 方法繼續分析,
2.2.14 ActivityThread.main() 方法
public static void main(String[] args) {
...
// 準備主執行緒的 Looper 物件
Looper.prepareMainLooper();
// 創建 ActivityThread 物件
ActivityThread thread = new ActivityThread();
// 呼叫 ActivityThread 物件的 attach 方法
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
// 運行訊息佇列
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
2.2.15 ActivityThread.attach() 方法
- boolean system, false,表示不是系統行程,而是普通應用行程
private void attach(boolean system) {
sCurrentActivityThread = this;
mSystemThread = system;
if (!system) { // system 為 false,進入此分支
...
RuntimeInit.setApplicationObject(mAppThread.asBinder());
// mgr 實際上是 ActivityManagerProxy 物件
final IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
// Ignore
}
...
} else {
...
}
...
}
2.2.16 ActivityManagerService.attachApplication() 方法
- IApplicationThread thread, ApplicationThreadProxy 物件
@Override
public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
attachApplicationLocked(thread, callingPid);
Binder.restoreCallingIdentity(origId);
}
}
2.2.17 ActivityManagerService.attachApplicationLocked() 方法
- IApplicationThread thread, ApplicationThreadProxy 物件
- int pid, provider 行程的 pid
// 存盤了所有運行的行程,以pid為鍵,以ProcessRecord為值
final SparseArray<ProcessRecord> mPidsSelfLocked = new SparseArray<ProcessRecord>();
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
ProcessRecord app;
if (pid != MY_PID && pid >= 0) {
synchronized (mPidsSelfLocked) {
// 根據 pid,獲取到之前存盤的 ProcessRecord 物件,不為 null,
app = mPidsSelfLocked.get(pid);
}
} else {
app = null;
}
if (app == null) { // 不會進入此分支
...
}
if (app.thread != null) { // app.thread 目前為 null,不會進入此分支
handleAppDiedLocked(app, true, true);
}
...
// mProcessesReady 為 true,所以這里 normalMode 為 true
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
// 獲取清單檔案中注冊的 provider 資訊
List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;
try {
...
ApplicationInfo appInfo = app.instrumentationInfo != null
? app.instrumentationInfo : app.info;
app.compat = compatibilityInfoForPackageLocked(appInfo);
if (profileFd != null) {
profileFd = profileFd.dup();
}
ProfilerInfo profilerInfo = profileFile == null ? null
: new ProfilerInfo(profileFile, profileFd, samplingInterval, profileAutoStop);
// 呼叫到這里,thread 是 ApplicationThreadProxy 物件,見【2.2.18】
thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(mConfiguration), app.compat, getCommonServicesLocked(),
mCoreSettingsObserver.getCoreSettingsLocked());
updateLruProcessLocked(app, false, null);
app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
} catch (Exception e) {
...
}
...// 省略與本次分析無關的代碼
return true;
}
2.2.17.1 ActivityManagerService.generateApplicationProvidersLocked() 方法
private final List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app) {
List<ProviderInfo> providers = null;
try {
// 獲取清單檔案中注冊的 provider 資訊串列
providers = AppGlobals.getPackageManager().
queryContentProviders(app.processName, app.uid,
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS);
} catch (RemoteException ex) {
}
int userId = app.userId;
if (providers != null) {
int N = providers.size();
app.pubProviders.ensureCapacity(N + app.pubProviders.size());
for (int i=0; i<N; i++) {
ProviderInfo cpi =
(ProviderInfo)providers.get(i);
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, userId);
if (cpr == null) {
cpr = new ContentProviderRecord(this, cpi, app.info, comp, singleton);
mProviderMap.putProviderByClass(comp, cpr);
}
// 把 provider 資訊添加到 ProcessRecord 的 pubProviders ArrayMap 里面,
app.pubProviders.put(cpi.name, cpr);
if (!cpi.multiprocess || !"android".equals(cpi.packageName)) {
app.addPackage(cpi.applicationInfo.packageName, cpi.applicationInfo.versionCode,
mProcessStats);
}
}
}
return providers;
}
2.2.18 ApplicationThreadProxy.bindApplication() 方法
public final void bindApplication(...) throws RemoteException {
...
mRemote.transact(BIND_APPLICATION_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
}
這個方法仍是在 system_server 行程(是 binder 客戶端行程)呼叫的,
mRemote.transact() 是 system_server 行程發起 binder 通信的方法,經過 binder 驅動,最后會到 provider 的目標行程(是 binder 服務端)ApplicationThreadNative的 onTransact() 方法,
2.2.19 ApplicationThreadNative.onTransact() 方法
public abstract class ApplicationThreadNative extends Binder
implements IApplicationThread {
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
case BIND_APPLICATION_TRANSACTION:
{
...
// 呼叫到這里,見【2.2.20】
bindApplication(packageName, info, providers, testName, profilerInfo, testArgs,
testWatcher, uiAutomationConnection, testMode, openGlTrace,
restrictedBackupMode, persistent, config, compatInfo, services, coreSettings);
return true;
}
}
}
這個方法是運行在 provider 的目標行程里的,
ApplicationThreadNative 是一個抽象類,而 bindApplication()是它的一個抽象方法,
ApplicationThread 是繼承于 ApplicationThreadNative 的具體類,它實作了 bindApplication() 方法,
ApplicationThread 類是 ActivityThread 的私有內部類,
2.2.20 ApplicationThread.bindApplication() 方法
public final void bindApplication(...) {
...
// 把資訊封裝在 AppBindData 里,通過 handler 發送到主執行緒,
AppBindData data = new AppBindData();
data.processName = processName;
data.appInfo = appInfo;
data.providers = providers;
...
sendMessage(H.BIND_APPLICATION, data);
}
這個方法目前是運行在 provider 的目標行程的 binder 執行緒池里面的,
2.2.21 H.handleMessage() 方法
public final class ActivityThread {
final H mH = new H();
private class H extends Handler {
public void handleMessage(Message msg) {
case BIND_APPLICATION:
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);
break;
}
}
}
這個方法目前是運行在 provider 的目標行程的主執行緒里面的,
2.2.22 ActivityThread.handleBindApplication() 方法
private void handleBindApplication(AppBindData data) {
...
// 獲取 LoadedApk 物件,賦值給 data.info
data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
// 來自 ProcessRecord 的 instrumentationClass,這個值只有在 AMS 的 startInstrumentation() 方法里賦值了
// 所以,本次分析 data.instrumentationName 為 null,不會進入 if 分支
if (data.instrumentationName != null) {
...
} else {
// 創建 Instrumentation 物件
mInstrumentation = new Instrumentation();
}
try {
// 創建 Application 物件
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
// don't bring up providers in restricted mode; they may depend on the
// app's custom Application class
if (!data.restrictedBackupMode) { // 非限制模式,進入 if 分支
// 獲取清單中注冊的 provider 資訊串列
List<ProviderInfo> providers = data.providers;
if (providers != null) {
// 安裝 content providers,見【2.2.23】
installContentProviders(app, providers);
}
}
try {
// 內部會呼叫 app.onCreate() 方法
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {}
} finally {}
}
該方法的主要作用:
- 創建
Instrumentation物件; - 創建
Application物件; - 安裝 content providers:創建 provider 物件,回呼了 provider 的
onCreate()方法,并存盤了 provider 資訊到對應的資料結構中; - 回呼
Application物件的onCreate()方法,
從這里可以知道,ContentProvider 的 onCreate() 方法在 Application 的 onCreate() 方法之前回呼,
而ContentProvider 的 onCreate() 方法是在主執行緒呼叫的,那么如果在 ContentProvider 的 onCreate() 方法里執行了耗時操作,勢必會推遲Application 的 onCreate() 方法的回呼,也就是說,推遲了應用的初始化操作了,對于用戶來說,就是應用啟動緩慢了,這多么不好啊,
所以,一定不要在 ContentProvider 的 onCreate() 方法里面執行耗時操作,
2.2.23 ActivityThread.installContentProviders() 方法
- Context context, provider 行程的 Application 物件
- List providers, 清單檔案中的 provider 資訊串列
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
final ArrayList<IActivityManager.ContentProviderHolder> results =
new ArrayList<IActivityManager.ContentProviderHolder>();
// 遍歷清單檔案中的 provider 資訊串列
for (ProviderInfo cpi : providers) {
// 安裝每一個 provider,本次分析我們只有一個 provider 待安裝,見【2.2.24】
IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}
try {
// 見【2.2.25】
// ActivityManagerNative.getDefault() 是一個 ActivityManagerProxy 物件,通過它,經過 binder 驅動,向 binder 服務端的 AMS 發起遠程呼叫,
ActivityManagerNative.getDefault().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
}
}
該方法的主要作用:
- 安裝 provider;
- 發布安裝結果:由 provider 目標行程向 system_server 行程的 AMS 發起遠程請求,
2.2.24 ActivityThread.installProvider() 方法
- Context context, provider 行程的 Application 物件
- IActivityManager.ContentProviderHolder holder, null
- ProviderInfo info, 清單檔案中的 provider 資訊
- boolean noisy, false
- boolean noReleaseNeeded, true
- boolean stable, true
// 在 2.2.6.2 中,我們繪制了這個集合的結構圖
final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap
= new ArrayMap<ProviderKey, ProviderClientRecord>();
final ArrayMap<ComponentName, ProviderClientRecord> mLocalProvidersByName
= new ArrayMap<ComponentName, ProviderClientRecord>();
private IActivityManager.ContentProviderHolder installProvider(Context context,
IActivityManager.ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;
IContentProvider provider;
if (holder == null || holder.provider == null) { // 進入此 if 分支
...
try {
final java.lang.ClassLoader cl = c.getClassLoader();
// 反射創建 ContentProvider 物件,本次分析創建的是 BookProvider 物件,
localProvider = (ContentProvider)cl.
loadClass(info.name).newInstance();
// 這里獲取的是 Transport 物件,是 IContentProvider 介面的 binder 服務端物件,
provider = localProvider.getIContentProvider();
// 把 Context 物件和 ContentProvider 相關聯,并回呼 BookProvider 物件的 onCreate() 方法,見【2.2.24.1】
localProvider.attachInfo(c, info);
} catch (java.lang.Exception e) {}
} else {
...
}
IActivityManager.ContentProviderHolder retHolder;
synchronized (mProviderMap) {
IBinder jBinder = provider.asBinder();
if (localProvider != null) { // 進入此 if 分支
ComponentName cname = new ComponentName(info.packageName, info.name);
ProviderClientRecord pr = mLocalProvidersByName.get(cname);
if (pr != null) {
provider = pr.mProvider;
} else { // 進入 else 分支
holder = new IActivityManager.ContentProviderHolder(info);
holder.provider = provider;
holder.noReleaseNeeded = true;
// 把 provider 存盤在 mProviderMap 里面,見【2.2.24.2】
pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
mLocalProviders.put(jBinder, pr);
mLocalProvidersByName.put(cname, pr);
}
retHolder = pr.mHolder;
} else {
...
}
}
return retHolder;
}
該方法的主要作用:
- 通過反射創建
ContentProvider物件; - 通過
ContentProvider.attachInfo()方法把Context物件和ContentProvider相關聯,并回呼BookProvider物件的onCreate()方法; - 存盤 provider 資訊到相關的資料結構中,
2.2.24.1 ContentProvider.attachInfo() 方法
private void attachInfo(Context context, ProviderInfo info, boolean testing) {
// 保證 mContext 物件只被設定一次,這樣 onCreate() 方法也是只會呼叫一次了,
if (mContext == null) {
mContext = context;
...
// 回呼 BookProvider 的 onCreate() 方法
ContentProvider.this.onCreate();
}
}
該方法的主要作用:把 Context 物件和 ContentProvider 相關聯,并回呼 BookProvider 物件的 onCreate() 方法,
2.2.24.2 ActivityThread.installProviderAuthoritiesLocked() 方法
- IContentProvider provider, ContentProvider 的 Transport 物件
- ContentProvider localProvider, BookProvider 物件
- IActivityManager.ContentProviderHolder holder,
private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider,
ContentProvider localProvider, IActivityManager.ContentProviderHolder holder) {
final String auths[] = PATTERN_SEMICOLON.split(holder.info.authority);
final int userId = UserHandle.getUserId(holder.info.applicationInfo.uid);
// 把 holder 封裝在 ProviderClientRecord 物件里面,
final ProviderClientRecord pcr = new ProviderClientRecord(
auths, provider, localProvider, holder);
for (String auth : auths) {
final ProviderKey key = new ProviderKey(auth, userId);
final ProviderClientRecord existing = mProviderMap.get(key);
if (existing != null) {
Slog.w(TAG, "Content provider " + pcr.mHolder.info.name
+ " already published as " + auth);
} else {
// 把對應的 ProviderKey 和 ProviderClientRecord 存盤在 mProviderMap 集合里面,
mProviderMap.put(key, pcr);
}
}
return pcr;
}
2.2.25 ActivityManagerService.publishContentProviders() 方法
- IApplicationThread caller, provider 目標行程的 ApplicationThread 物件
- List providers, ContentProviderHolder 串列
public final void publishContentProviders(IApplicationThread caller,
List<ContentProviderHolder> providers) {
if (providers == null) {
return;
}
synchronized (this) {
// 獲取 provider 目標行程的行程記錄物件,已存在,所以 r 不為 null,
final ProcessRecord r = getRecordForAppLocked(caller);
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) {
continue;
}
// r.pubProviders 是
// final ArrayMap<String, ContentProviderRecord> pubProviders
// = new ArrayMap<String, ContentProviderRecord>();
// src.info.name 是 provider 的類名稱
// 此處 dst 不為 null,因為在 2.2.17.1 中已經添加了 provider 資訊到這個集合里面了,
ContentProviderRecord dst = r.pubProviders.get(src.info.name);
if (dst != null) {
ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
mProviderMap.putProviderByClass(comp, dst);
String names[] = dst.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
mProviderMap.putProviderByName(names[j], dst);
}
...
synchronized (dst) {
// 這行代碼非常關鍵,把 ContentProviderHolder src 的 provider 物件賦值給
// ContentProviderRecord dst 的 provider 變數,
dst.provider = src.provider;
// 把 provider 的目標行程記錄物件賦值給 ContentProviderRecord dst 的 proc 變數,
dst.proc = r;
dst.notifyAll();
}
}
}
}
}
2.2.26 ActivityManagerService.getContentProviderImpl() 方法跳出回圈等待 provider 發布
回到 2.2.10 的分析部分:
public final class ActivityManagerService extends ActivityManagerNative {
final ProviderMap mProviderMap;
final ArrayList<ContentProviderRecord> mLaunchingProviders
= new ArrayList<ContentProviderRecord>();
public ActivityManagerService(Context systemContext) {
mProviderMap = new ProviderMap(this);
}
private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, boolean stable, int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
...
// 回圈等待 provider 發布
// 到這里,cpr.provider 不為 null,因此會結束 while 回圈,
synchronized (cpr) {
while (cpr.provider == null) {
if (cpr.launchingApp == null) {
return null;
}
try {
if (conn != null) {
conn.waiting = true;
}
cpr.wait();
} catch (InterruptedException ex) {
} finally {
if (conn != null) {
conn.waiting = false;
}
}
}
}
// 把 cpr 封裝成 ContentProviderHolder 物件回傳
return cpr != null ? cpr.newHolder(conn) : null;
}
}
2.2.27 ActivityThread.acquireProvider() 方法
回到 2.2.6 的部分分析:
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
...
IActivityManager.ContentProviderHolder holder = null;
try {
// 這個方法會獲取到結果 ContentProviderHolder 物件
holder = ActivityManagerNative.getDefault().getContenProvider(
getApplicationThread(), auth, userId, stable)
} catch (RemoteException ex) {
}
if (holder == null) { // 不會進入此分支
// 如果 AMS 回傳的 ContentProviderHolder 物件為 null,則直接回傳 null,
return null;
}
// 安裝 provider 將會增加參考計數,并且打破競爭中的任何聯系,見【2.2.27.1】
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}
這個方法執行完畢,就獲得了 ContentProviderProxy 物件了,
2.2.27.1 ActivityThread.installProvider() 方法
雖然我們在 2.2.24 中也分析了這個方法,但是和這里是有區別的:
2.2.24 中的 installProvider方法是發生在 provider 目標行程的主執行緒;
這里的 installProvider 方法是發生在客戶端行程的主執行緒,
- Context context, ContextImpl 物件
- IActivityManager.ContentProviderHolder holder, 不為 null
- ProviderInfo info, 清單檔案中的 provider 資訊
- boolean noisy, true
- boolean noReleaseNeeded, true
- boolean stable, false
private IActivityManager.ContentProviderHolder installProvider(Context context,
IActivityManager.ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;
IContentProvider provider;
if (holder == null || holder.provider == null) { // 不會進入 if 分支
...
} else { // 命中 else 分支
provider = holder.provider;
}
IActivityManager.ContentProviderHolder retHolder;
synchronized (mProviderMap) {
IBinder jBinder = provider.asBinder();
if (localProvider != null) { // localProvider 為 null,不會進入 if 分支
...
} else { // 進入 else 分支
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc != null) { // prc 為 null,不會進入 if 分支
...
} else { // 進入 else 分支
ProviderClientRecord client = installProviderAuthoritiesLocked(
provider, localProvider, holder);
if (noReleaseNeeded) { // 進入 if 分支
prc = new ProviderRefCount(holder, client, 1000, 1000);
} else {
...
}
mProviderRefCountMap.put(jBinder, prc);
}
retHolder = prc.holder;
}
}
return retHolder;
}
該方法的作用:把 provider 資訊存入 mProviderMap 集合和 mProviderRefCountMap 集合中,
2.2.27.2 ActivityThread.installProviderAuthoritiesLocked() 方法
private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider,
ContentProvider localProvider, IActivityManager.ContentProviderHolder holder) {
final String auths[] = PATTERN_SEMICOLON.split(holder.info.authority);
final int userId = UserHandle.getUserId(holder.info.applicationInfo.uid);
final ProviderClientRecord pcr = new ProviderClientRecord(
auths, provider, localProvider, holder);
for (String auth : auths) {
final ProviderKey key = new ProviderKey(auth, userId);
final ProviderClientRecord existing = mProviderMap.get(key);
if (existing != null) {
Slog.w(TAG, "Content provider " + pcr.mHolder.info.name
+ " already published as " + auth);
} else {
mProviderMap.put(key, pcr);
}
}
return pcr;
}
該方法的作用:把 provider 資訊存入 mProviderMap 集合中,
2.2.28 ContentProviderProxy.query() 方法
final class ContentProviderProxy implements IContentProvider
{
public Cursor query(String callingPkg, Uri url, String[] projection, String selection,
String[] selectionArgs, String sortOrder, ICancellationSignal cancellationSignal)
throws RemoteException {
// 實體化 BulkCursorToCursorAdaptor 物件
BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
...
data.writeStrongBinder(adaptor.getObserver().asBinder());
data.writeStrongBinder(cancellationSignal != null ? cancellationSignal.asBinder() : null);
mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);
DatabaseUtils.readExceptionFromParcel(reply);
if (reply.readInt() != 0) {
BulkCursorDescriptor d = BulkCursorDescriptor.CREATOR.createFromParcel(reply);
adaptor.initialize(d);
} else {
adaptor.close();
adaptor = null;
}
return adaptor;
}
}
}
這個方法是運行在客戶端行程的,
mRemote.transact() 是 客戶端行程發起 binder 通信的方法,經過 binder 驅動,最后會到 provider 的目標行程(是 binder 服務端)ContentProviderNative的 onTransact() 方法,
2.2.29 ContentProviderNative.onTransact() 方法
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
case QUERY_TRANSACTION:
{
...
Cursor cursor = query(callingPkg, url, projection, selection, selectionArgs,
sortOrder, cancellationSignal);
if (cursor != null) {
CursorToBulkCursorAdaptor adaptor = null;
try {
adaptor = new CursorToBulkCursorAdaptor(cursor, observer,
getProviderName());
cursor = null;
BulkCursorDescriptor d = adaptor.getBulkCursorDescriptor();
adaptor = null;
reply.writeNoException();
reply.writeInt(1);
d.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} finally {}
} else {
reply.writeNoException();
reply.writeInt(0);
}
return true;
}
}
ContentProviderNative 是一個抽象類,query方法是它的一個抽象方法,
ContentProvider.Transport 是 ContentProviderNative 的具體實作類,
2.2.30 Transport.query() 方法
@Override
public Cursor query(String callingPkg, Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder,
ICancellationSignal cancellationSignal) {
validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
if (enforceReadPermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
return rejectQuery(uri, projection, selection, selectionArgs, sortOrder,
CancellationSignal.fromTransport(cancellationSignal));
}
final String original = setCallingPackage(callingPkg);
try {
// 這里呼叫的就是 BookProvider 的 query 方法了,
return ContentProvider.this.query(
uri, projection, selection, selectionArgs, sortOrder,
CancellationSignal.fromTransport(cancellationSignal));
} finally {
setCallingPackage(original);
}
}
2.2.31 BookProvider.query() 方法
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
final int code = MATCHER.match(uri);
if (code == CODE_BOOK_DIR || code == CODE_BOOK_ITEM) {
Context context = getContext();
if (context == null) {
return null;
}
BookDao bookDao = AppDatabase.getInstance(context).bookDao();
final Cursor cursor;
if (code == CODE_BOOK_DIR) {
cursor = bookDao.queryAll();
} else {
cursor = bookDao.queryById(ContentUris.parseId(uri));
}
cursor.setNotificationUri(context.getContentResolver(), uri);
return cursor;
} else {
throw new IllegalArgumentException("Unknown URI: " + uri);
}
}
3.最后
本文到這里就結束了,希望能夠幫助到大家,
關于本文需要討論的地方,可以在評論區留言討論,
4.參考
- 理解ContentProvider原理-袁輝輝
- ContentProvider參考計數-袁輝輝
- Android Cursor淺析
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/402093.html
標籤:其他
上一篇:Lua回到for回圈
