亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

ContentProvider客戶端處理provider邏輯分析

 更新時(shí)間:2022年10月21日 09:07:55   作者:大胃粥  
這篇文章主要為大家介紹了ContentProvider客戶端處理provider邏輯分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

前面一篇文章分析了 AMS 端處理 provider 的邏輯,請讀者務(wù)必仔細(xì)閱讀前面一篇文章,否則看本文,你可能有很多疑惑。

以查詢 provider 為例來分析客戶端是如何處理 provider,它調(diào)用的是 ContentResolver#query()

// ContentResolver.java
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
        @Nullable String[] projection, @Nullable Bundle queryArgs,
        @Nullable CancellationSignal cancellationSignal) {
    Objects.requireNonNull(uri, "uri");
    // ApplicationContentResolver 的 mWrapped 為 null
    try {
        if (mWrapped != null) {
            return mWrapped.query(uri, projection, queryArgs, cancellationSignal);
        }
    } catch (RemoteException e) {
        return null;
    }
    // 1. 獲取 unstable provider
    IContentProvider unstableProvider = acquireUnstableProvider(uri);
    if (unstableProvider == null) {
        return null;
    }
    IContentProvider stableProvider = null;
    Cursor qCursor = null;
    try {
        long startTime = SystemClock.uptimeMillis();
        // 獲取取消操作的接口
        ICancellationSignal remoteCancellationSignal = null;
        if (cancellationSignal != null) {
            cancellationSignal.throwIfCanceled();
            remoteCancellationSignal = unstableProvider.createCancellationSignal();
            cancellationSignal.setRemote(remoteCancellationSignal);
        }
        try {
            // 2. 執(zhí)行操作
            qCursor = unstableProvider.query(mContext.getAttributionSource(), uri, projection,
                    queryArgs, remoteCancellationSignal);
        } catch (DeadObjectException e) {
            // 處理 unstable provider 進(jìn)程掛掉的情況
            // 通知 AMS,provider 進(jìn)程掛掉了
            unstableProviderDied(unstableProvider);
            // 獲取 stable provider,再次嘗試獲取數(shù)據(jù)
            stableProvider = acquireProvider(uri);
            if (stableProvider == null) {
                return null;
            }
            qCursor = stableProvider.query(mContext.getAttributionSource(), uri, projection,
                    queryArgs, remoteCancellationSignal);
        }
        if (qCursor == null) {
            return null;
        }
        // Force query execution.  Might fail and throw a runtime exception here.
        qCursor.getCount();
        long durationMillis = SystemClock.uptimeMillis() - startTime;
        maybeLogQueryToEventLog(durationMillis, uri, projection, queryArgs);
        // 注意,這里最終還是從 stable provider 獲取 provider 接口
        final IContentProvider provider = (stableProvider != null) ? stableProvider
                : acquireProvider(uri);
        final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
        stableProvider = null;
        qCursor = null;
        // 3. 返回?cái)?shù)據(jù)
        return wrapper;
    } catch (RemoteException e) {
        return null;
    } finally {
        // ...
    }
}

縱觀整個(gè) provider 的查詢過程,其實(shí)就是三步

  • 獲取 provider。
  • 從獲取到的 provider 執(zhí)行查詢操作。
  • 返回查詢的結(jié)果。

我們注意到,代碼中出現(xiàn)了兩種 provider,unstable provider 和 stable provider。這兩者的區(qū)別是,如果 provider 進(jìn)程掛掉了,對于 stable provider,會(huì)殺死客戶端進(jìn)程,而 unstable 不會(huì)。這個(gè)我們會(huì)在后面分析。

現(xiàn)在我們要抓住重點(diǎn),來分析如何獲取 provider 。unstable provider 和 stable provider 的獲取方式其實(shí)是一樣的,本文只分析獲取 unstbale provider。

1. 獲取 provider

對于 app 進(jìn)程來說,ContentResolver 接口的實(shí)現(xiàn)類為 ApplicationContentResolver,獲取 unstable provider 的操作最終會(huì)調(diào)用 ApplicationContentResolver#acquireUnstableProvider()

//ContextImpl.java
class ContextImpl {
    private static final class ApplicationContentResolver extends ContentResolver {
        private final ActivityThread mMainThread;
        @Override
        protected IContentProvider acquireUnstableProvider(Context c, String auth) {
            return mMainThread.acquireProvider(c,
                    ContentProvider.getAuthorityWithoutUserId(auth),
                    resolveUserIdFromAuthority(auth), false);
        }
    }
}

原來最終是交給 ActivityThread 來獲取 provider

// ActivityThread.java
public final IContentProvider acquireProvider(
        Context c, String auth, int userId, boolean stable) {
    // 從本地獲取
    final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
    if (provider != null) {
        return provider;
    }
    ContentProviderHolder holder = null;
    // 合成一個(gè) KEY
    final ProviderKey key = getGetProviderKey(auth, userId);
    try {
        synchronized (key) {
            // 1. 獲取 ActivityManagerService 獲取
            holder = ActivityManager.getService().getContentProvider(
                    getApplicationThread(), c.getOpPackageName(), auth, userId, stable);
            // 2. 等待 provider 發(fā)布完成
            // holder != null 表示 provider 存在 
            // holder.provider == null 表示 provider 正在發(fā)布中
            // holder.mLocal 為 false,表示 provider 不是安裝在客戶端
            if (holder != null && holder.provider == null && !holder.mLocal) {
                synchronized (key.mLock) {
                    // 2.1 超時(shí)等等 provider 發(fā)布
                    // 超時(shí)時(shí)間一般為 20s
                    key.mLock.wait(ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS);
                    // 這里可能因?yàn)槌瑫r(shí)被喚醒,獲取的數(shù)據(jù)為空
                    // 也可以是因?yàn)閜rovider發(fā)布完成,被AMS喚醒,holder 為AMS返回的數(shù)據(jù)
                    holder = key.mHolder;
                }
                // 2.2 確認(rèn)是否是超時(shí)喚醒
                if (holder != null && holder.provider == null) {
                    // probably timed out
                    holder = null;
                }
            }
        }
    } 
    // ...
    // 這里記錄了獲取provider失敗的日志
    if (holder == null) {
        if (UserManager.get(c).isUserUnlocked(userId)) {
            Slog.e(TAG, "Failed to find provider info for " + auth);
        } else {
            Slog.w(TAG, "Failed to find provider info for " + auth + " (user not unlocked)");
        }
        return null;
    }
    // 3. 成功從服務(wù)端獲取 provider,本地安裝它
    holder = installProvider(c, holder, holder.info,
            true /*noisy*/, holder.noReleaseNeeded, stable);
    return holder.provider;
}

客戶端獲取 provider 的過程大致分為如下幾步

  • 從 AMS 獲取 provider。
  • 如果 provider 還是發(fā)布的過程中,那么就超時(shí)等待它發(fā)布完成。 但是等待是有時(shí)間限制的,大約為 20s。超時(shí)等待的過程中被喚醒,有兩種可能,一種是因?yàn)槌瑫r(shí)了,另外一種是因?yàn)?provider 成功發(fā)布,AMS 喚醒了客戶端。因此需要判斷到底是哪一種情況,檢測的條件是被喚醒后,是否獲取到 provider binder,也就是 holder.provider。詳見【1.1 等待 provider 發(fā)布
  • 從 AMS 成功獲取到 provider 后,那就在本地“安裝”。這個(gè)方法的命令起的并不是很好,如果成功從 AMS 獲取到 provider,其實(shí)這里的邏輯是保存數(shù)據(jù)。而如果 AMS 通知客戶端,provider 可以安裝在客戶端進(jìn)程中,客戶端會(huì)在這個(gè)方法中創(chuàng)建 ContentProvider 對象并保存,這才叫安裝。詳見【1.2 安裝 provider

1.1 等待 provider 發(fā)布

從前面的文章可知,當(dāng) provider 發(fā)布超時(shí) 或者 成功發(fā)布時(shí),都會(huì)調(diào)用 ContentProviderRecord#onProviderPublishStatusLocked(boolean status) 來通知客戶端 provider 的發(fā)布狀態(tài)。參數(shù) status 如果為 true,表示發(fā)布成功,如果為 false,表示發(fā)布超時(shí)。

// ContentProviderRecord.java
void onProviderPublishStatusLocked(boolean status) {
    final int numOfConns = connections.size();
    for (int i = 0; i < numOfConns; i++) {
        // 遍歷所有等待 provider 發(fā)布的客戶端連接
        final ContentProviderConnection conn = connections.get(i);
        if (conn.waiting && conn.client != null) {
            final ProcessRecord client = conn.client;
            // 記錄發(fā)布超時(shí)的日志
            if (!status) {
                // 從這里可以看出status為false時(shí),不一定表示發(fā)布超時(shí),還可能因?yàn)檫M(jìn)程掛掉了
                if (launchingApp == null) {
                    Slog.w(TAG_AM, "Unable to launch app "
                            + appInfo.packageName + "/"
                            + appInfo.uid + " for provider "
                            + info.authority + ": launching app became null");
                    EventLogTags.writeAmProviderLostProcess(
                            UserHandle.getUserId(appInfo.uid),
                            appInfo.packageName,
                            appInfo.uid, info.authority);
                } else {
                    Slog.wtf(TAG_AM, "Timeout waiting for provider "
                            + appInfo.packageName + "/"
                            + appInfo.uid + " for provider "
                            + info.authority
                            + " caller=" + client);
                }
            }
            // 通知客戶端
            final IApplicationThread thread = client.getThread();
            if (thread != null) {
                try {
                    thread.notifyContentProviderPublishStatus(
                            newHolder(status ? conn : null, false),
                            info.authority, conn.mExpectedUserId, status);
                } catch (RemoteException e) {
                }
            }
        }
        conn.waiting = false;
    }
}

很簡單,通過遍歷所有等待 provider 發(fā)布的客戶端連接,然后通過客戶端 attach 的 thread 來通知它們。

// ActivityThread.java
public void notifyContentProviderPublishStatus(@NonNull ContentProviderHolder holder,
        @NonNull String authorities, int userId, boolean published) {
    final String auths[] = authorities.split(";");
    for (String auth: auths) {
        final ProviderKey key = getGetProviderKey(auth, userId);
        synchronized (key.mLock) {
            // 保存服務(wù)端傳過來的數(shù)據(jù)
            key.mHolder = holder;
            // 喚醒等待provider的線程
            key.mLock.notifyAll();
        }
    }
}

客戶端收到信息后,喚醒了等待的線程,誰在等待呢?這里是不是有點(diǎn)熟悉,其實(shí)就是前面分析獲取 provider 時(shí),超時(shí)等待,部分代碼如下

// ActivityThread.java
public final IContentProvider acquireProvider(
        Context c, String auth, int userId, boolean stable) {
    // ...
    try {
        synchronized (key) {
            holder = ActivityManager.getService().getContentProvider(
                    getApplicationThread(), c.getOpPackageName(), auth, userId, stable);
            // 等待 provider 發(fā)布完成
            if (holder != null && holder.provider == null && !holder.mLocal) {
                synchronized (key.mLock) {
                    // 超時(shí)等待
                    key.mLock.wait(ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS);
                    // 這里可能因?yàn)槌瑫r(shí)被喚醒,獲取的數(shù)據(jù)為空
                    // 也可以是因?yàn)閜rovider發(fā)布完成,被AMS喚醒,holder 為AMS返回的數(shù)據(jù)
                    holder = key.mHolder;
                }
                // 確認(rèn)是否是超時(shí)喚醒
                if (holder != null && holder.provider == null) {
                    // probably timed out
                    holder = null;
                }
            }
        }
    } 
    // ...
}

超時(shí)等待 provider 發(fā)布時(shí),如果一旦被喚醒,再次獲取 key.mHolder,因?yàn)槿绻晒Πl(fā)布,holder.provider 是不為空的,因?yàn)樗褪?provider binder,否則就是超時(shí)喚醒。

1.2 安裝 provider

客戶端如果成功從 AMS 獲取到 provider,那么就會(huì)安裝它,其實(shí)這里的操作是保存數(shù)據(jù),其實(shí)最主要的就是保存 provider 接口,同時(shí)也是保存 provider binder.

private ContentProviderHolder installProvider(Context context,
        ContentProviderHolder holder, ProviderInfo info,
        boolean noisy, boolean noReleaseNeeded, boolean stable) {
    ContentProvider localProvider = null;
    IContentProvider provider;
    // 成功從 AMS 獲取 provider,下面兩個(gè)條件都是不成立 
    if (holder == null || holder.provider == null) {
        // ...
    } else {
        // 獲取 provider 接口,其實(shí)就是獲取 provider binder
        provider = holder.provider;
    }
    ContentProviderHolder retHolder;
    synchronized (mProviderMap) {
        // 從 provider 接口中獲取 binder 對象
        IBinder jBinder = provider.asBinder();
        if (localProvider != null) {
            // ...
        } else {
            ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
            if (prc != null) {
                // ...
            } else {
                // 1. 創(chuàng)建 provider 記錄,并保存
                ProviderClientRecord client = installProviderAuthoritiesLocked(
                        provider, localProvider, holder);
                // persistent app 的 provider 是不需要釋放的
                if (noReleaseNeeded) {
                    prc = new ProviderRefCount(holder, client, 1000, 1000);
                } else {
                    prc = stable
                            ? new ProviderRefCount(holder, client, 1, 0)
                            : new ProviderRefCount(holder, client, 0, 1);
                }
                // 2. 保存 provider 計(jì)數(shù)
                mProviderRefCountMap.put(jBinder, prc);
            }
            retHolder = prc.holder;
        }
    }
    return retHolder;
}
private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider,
        ContentProvider localProvider, ContentProviderHolder holder) {
    final String auths[] = holder.info.authority.split(";");
    final int userId = UserHandle.getUserId(holder.info.applicationInfo.uid);
    // ...
    //  創(chuàng)建一條 provider 記錄
    final ProviderClientRecord pcr = new ProviderClientRecord(
            auths, provider, localProvider, holder);
    // 一個(gè) ContentProvider 可以聲明多個(gè) authority
    for (String auth : auths) {
        final ProviderKey key = new ProviderKey(auth, userId);
        //  mProviderMap 保存
        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;
}

很簡單,就是用兩個(gè)數(shù)據(jù)結(jié)構(gòu)保存數(shù)據(jù)。

2. provider 實(shí)現(xiàn)多進(jìn)程實(shí)例

前面我們總是隱隱約約地提到,provider 可以安裝在客戶端進(jìn)程,那么什么樣的條件下,provider 可以安裝在客戶端進(jìn)程中? 前面一篇文章的分析中有提到過,現(xiàn)在展示出部分代碼

// ContentProviderHelper.java
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
        String name, IBinder token, int callingUid, String callingPackage, String callingTag,
        boolean stable, int userId) {
    // ...
    synchronized (mService) {
        // 獲取客戶端的進(jìn)程實(shí)例
        ProcessRecord r = null;
        if (caller != null) {
            r = mService.getRecordForAppLOSP(caller);
            if (r == null) {
                throw new SecurityException("Unable to find app for caller " + caller
                        + " (pid=" + Binder.getCallingPid() + ") when getting content provider "
                        + name);
            }
        }
        // ...
        // provider 正在運(yùn)行
        if (providerRunning) {
            cpi = cpr.info;
            if (r != null &amp;&amp; cpr.canRunHere(r)) {
                // This provider has been published or is in the process
                // of being published...  but it is also allowed to run
                // in the caller's process, so don't make a connection
                // and just let the caller instantiate its own instance.
                ContentProviderHolder holder = cpr.newHolder(null, true);
                // don't give caller the provider object, it needs to make its own.
                holder.provider = null;
                return holder;
            }
            // ...
        }
        // provider 沒有運(yùn)行
        if (!providerRunning) {
            // ...
            if (r != null &amp;&amp; cpr.canRunHere(r)) {
                // If this is a multiprocess provider, then just return its
                // info and allow the caller to instantiate it.  Only do
                // this if the provider is the same user as the caller's
                // process, or can run as root (so can be in any process).
                return cpr.newHolder(null, true);
            }
            // ...
        }
        // ...
    }
    // ...
}

可以看到,無論 provider 是否已經(jīng)運(yùn)行,都有機(jī)會(huì)在客戶端進(jìn)程中創(chuàng)建 provider 實(shí)例,而這個(gè)機(jī)會(huì)就在 ContentProviderRecord#canRunHere()

provider 已經(jīng)運(yùn)行,居然還可以運(yùn)行在客戶端進(jìn)程中,也就是在客戶端進(jìn)程中創(chuàng)建 ContentProvider 實(shí)例,這樣的設(shè)計(jì)又是為了什么呢?

public boolean canRunHere(ProcessRecord app) {
    // info 為 provider 信息,也就是在 AndroidManifest 中聲明的 provider 信息
    // provider 可以 運(yùn)行在客戶端進(jìn)程中的條件
    // 1. provider 所在的 app 的 uid 與客戶端 app 的 uid 相同
    // 2. provider 支持多進(jìn)程 或者 provider 的進(jìn)程名與客戶端 app 的進(jìn)程名相同
    return (info.multiprocess || info.processName.equals(app.processName))
            && uid == app.info.uid;
}

這里的條件可要看清楚了,首先 provider 所在 app 和 客戶端 app 的 uid 相同,其實(shí)就是下面這個(gè)玩意要一樣

<manifest 
    android:sharedUserId="">

然后,還需要 provider 支持多進(jìn)程,其實(shí)就是下面這個(gè)玩意

<provider 
    android:multiprocess="true"/>

如果 provider 不支持多進(jìn)程,只要 provider 的進(jìn)程名與客戶端 app 的進(jìn)程名一樣,provider 也是可以運(yùn)行在客戶端進(jìn)程中。那么 provider 進(jìn)程名是什么呢? provider 可以聲明自己的進(jìn)程名,如下

<provider
    android:process=""
   />

而如果 provider 沒有聲明自己的進(jìn)程名,那么 provider 進(jìn)程名取自 app 的進(jìn)程名。

現(xiàn)在 provider 怎樣運(yùn)行在客戶端進(jìn)程中,大家會(huì)玩了嗎?如果會(huì)玩了,那么繼續(xù)看下客戶端如何安裝 provider,這一次可就是真的安裝 provider 了

private ContentProviderHolder installProvider(Context context,
        ContentProviderHolder holder, ProviderInfo info,
        boolean noisy, boolean noReleaseNeeded, boolean stable) {
    ContentProvider localProvider = null;
    IContentProvider provider;
    // 此時(shí) holder.provider == null 是成立的
    if (holder == null || holder.provider == null) {
        // ...
        try {
            final java.lang.ClassLoader cl = c.getClassLoader();
            LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
            if (packageInfo == null) {
                // System startup case.
                packageInfo = getSystemContext().mPackageInfo;
            }
            // 1. 通過反射創(chuàng)建 ContentProvider 對象
            localProvider = packageInfo.getAppFactory()
                    .instantiateProvider(cl, info.name);
            // 獲取 provider 接口,其實(shí)就是獲取 provider binder
            provider = localProvider.getIContentProvider();
            if (provider == null) {
                return null;
            }
            // 2. 為 ContentProvider 對象保存 provider 信息,并且調(diào)用 ContentProvider#onCreate()
            localProvider.attachInfo(c, info);
        } catch (java.lang.Exception e) {
            // ...
        }
    } else {
        // ...
    }
    ContentProviderHolder retHolder;
    synchronized (mProviderMap) {
        if (DEBUG_PROVIDER) Slog.v(TAG, "Checking to add " + provider
                + " / " + info.name);
        IBinder jBinder = provider.asBinder();
        if (localProvider != null) {
            ComponentName cname = new ComponentName(info.packageName, info.name);
            ProviderClientRecord pr = mLocalProvidersByName.get(cname);
            if (pr != null) {
                // ...
            } else {
                // 本地創(chuàng)建 ContentProviderHolder
                holder = new ContentProviderHolder(info);
                // 保存 provider binder
                holder.provider = provider;
                // 本地安裝的 provider,不需要釋放
                holder.noReleaseNeeded = true;
                // 3. 創(chuàng)建 provider 記錄,并保存
                pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
                mLocalProviders.put(jBinder, pr);
                mLocalProvidersByName.put(cname, pr);
            }
            retHolder = pr.mHolder;
        } else {
            // ...
        }
    }
    return retHolder;
}

其實(shí)這一部分代碼在前面文章中已經(jīng)分析過,這里簡單介紹下過程

  • 客戶端自己創(chuàng)建 ContentProvider 對象,然后保存 provider 信息,并調(diào)用 ContentProvider#onCreate() 方法。
  • 創(chuàng)建 provider 記錄,也就是 ContentProviderRecord 對象,然后用數(shù)據(jù)結(jié)構(gòu)保存。

3. 兩種 provider 區(qū)別

前面我們提到過 unstable provider 和 stable provider 的區(qū)別,現(xiàn)在我們用代碼來解釋下這兩者的區(qū)別

假設(shè)我們通過 ActivityManager#forceStopPackage() 來殺掉 provider 進(jìn)程,在 AMS 的調(diào)用如下

public void forceStopPackage(final String packageName, int userId) {
    // ...
    try {
        IPackageManager pm = AppGlobals.getPackageManager();
        synchronized(this) {
            int[] users = userId == UserHandle.USER_ALL
                    ? mUserController.getUsers() : new int[] { userId };
            for (int user : users) {
                // ...
                if (mUserController.isUserRunning(user, 0)) {
                    // 殺掉進(jìn)程
                    forceStopPackageLocked(packageName, pkgUid, "from pid " + callingPid);
                    // 發(fā)送廣播
                    finishForceStopPackageLocked(packageName, pkgUid);
                }
            }
        }
    } finally {
        Binder.restoreCallingIdentity(callingId);
    }
}

最終調(diào)用如下代碼

final boolean forceStopPackageLocked(String packageName, int appId,
        boolean callerWillRestart, boolean purgeCache, boolean doit,
        boolean evenPersistent, boolean uninstalling, int userId, String reason) {
    // ...
    // 獲取 app 的所有 provider
    ArrayList<ContentProviderRecord> providers = new ArrayList<>();
    if (mCpHelper.getProviderMap().collectPackageProvidersLocked(packageName, null, doit,
            evenPersistent, userId, providers)) {
        if (!doit) {
            return true;
        }
        didSomething = true;
    }
    // 移除 provider
    for (i = providers.size() - 1; i >= 0; i--) {
        mCpHelper.removeDyingProviderLocked(null, providers.get(i), true);
    }
    // ...
}

不出意外,最終由 ContentProviderHelper 來移除 provider

boolean removeDyingProviderLocked(ProcessRecord proc, ContentProviderRecord cpr,
        boolean always) {
    // ...
    for (int i = cpr.connections.size() - 1; i >= 0; i--) {
        ContentProviderConnection conn = cpr.connections.get(i);
        // ...
        ProcessRecord capp = conn.client;
        final IApplicationThread thread = capp.getThread();
        conn.dead = true;
        // 1. 如有 stable provider 的客戶端
        if (conn.stableCount() > 0) {
            final int pid = capp.getPid();
            // 注意,要排除 persistent app 進(jìn)程,以及 system_server 進(jìn)程
            if (!capp.isPersistent() && thread != null
                    && pid != 0 && pid != ActivityManagerService.MY_PID) {
                // 殺掉客戶端進(jìn)程
                capp.killLocked(
                        "depends on provider " + cpr.name.flattenToShortString()
                        + " in dying proc " + (proc != null ? proc.processName : "??")
                        + " (adj " + (proc != null ? proc.mState.getSetAdj() : "??") + ")",
                        ApplicationExitInfo.REASON_DEPENDENCY_DIED,
                        ApplicationExitInfo.SUBREASON_UNKNOWN,
                        true);
            }
        } 
        // 2. 如果只有 unstable provider 客戶端
        else if (thread != null && conn.provider.provider != null) {
            try {
                // 通知客戶端移除數(shù)據(jù)
                thread.unstableProviderDied(conn.provider.provider.asBinder());
            } catch (RemoteException e) {
            }
            // In the protocol here, we don't expect the client to correctly
            // clean up this connection, we'll just remove it.
            cpr.connections.remove(i);
            if (conn.client.mProviders.removeProviderConnection(conn)) {
                mService.stopAssociationLocked(capp.uid, capp.processName,
                        cpr.uid, cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
            }
        }
    }
    // ...
}

看到了吧,對于 stable provider,如果 provider 進(jìn)程掛掉了,那么客戶端也會(huì)受牽連被殺掉。

而對于 unstable provider,如果 provier 進(jìn)程掛掉了,客戶端只是移除了保存了的數(shù)據(jù)而已,并不會(huì)被殺掉。

最后,我們再來看看文章開頭獲取 provider 時(shí)關(guān)于兩種 provider 代碼

// ContentResolver.java
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
        @Nullable String[] projection, @Nullable Bundle queryArgs,
        @Nullable CancellationSignal cancellationSignal) {
    // ...
    // 獲取 unstable provider
    IContentProvider unstableProvider = acquireUnstableProvider(uri);
    if (unstableProvider == null) {
        return null;
    }
    IContentProvider stableProvider = null;
    Cursor qCursor = null;
    try {
        // ...
        // 注意,這里獲取的 stable provider 并返回
        final IContentProvider provider = (stableProvider != null) ? stableProvider
                : acquireProvider(uri);
        final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
        stableProvider = null;
        qCursor = null;
        // 返回?cái)?shù)據(jù)
        return wrapper;
    } catch (RemoteException e) {
        return null;
    } finally {
        // ...
    }
}

我們可以看到,查詢的時(shí)候使用的是 unstable provier,但是返回的結(jié)果 Curosr 使用的是 stable provider。這說明了什么? 它說明了,在 Cursor 沒有被 close 之前,只要 provider 進(jìn)程掛掉了,那么客戶端也會(huì)受牽連,會(huì)被殺掉。

結(jié)束

以上就是ContentProvider客戶端處理provider邏輯分析的詳細(xì)內(nèi)容,更多關(guān)于ContentProvider客戶端provider的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論