真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Android7.0中ContentProvider組件的作用是什么

這篇文章將為大家詳細(xì)講解有關(guān)Android7.0中ContentProvider組件的作用是什么,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。

創(chuàng)新互聯(lián)建站服務(wù)項(xiàng)目包括青羊網(wǎng)站建設(shè)、青羊網(wǎng)站制作、青羊網(wǎng)頁(yè)制作以及青羊網(wǎng)絡(luò)營(yíng)銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢(shì)、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,青羊網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到青羊省份的部分城市,未來相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!

作為Android的四大組件之一,ContentProvider作為進(jìn)程之間靜態(tài)數(shù)據(jù)傳遞的重要手段,其在系統(tǒng)級(jí)別的應(yīng)用中起了重大的作用。毫無疑問,ContentProvider核心機(jī)制之一也是Binder,但是和其它3大組件又有區(qū)別。因?yàn)镃ontentProvider涉及數(shù)據(jù)的增刪查改,當(dāng)數(shù)據(jù)量比較大的時(shí)候,繼續(xù)用Parcel做容器效率會(huì)比較低,因此它還使用了匿名共享內(nèi)存的方式。

但是有一個(gè)問題是,ContentProvider的提供者進(jìn)程不再存活時(shí),其他進(jìn)程通過Provider讀一個(gè)非常簡(jiǎn)單的數(shù)據(jù)時(shí),都需要先把提供者進(jìn)程啟動(dòng)起來(除非指定multiprocess=true),這對(duì)用戶是相當(dāng)不友好的。又因?yàn)槠涫情g接通過db進(jìn)行數(shù)據(jù)操作,所以效率也遠(yuǎn)不如直接操作db。因此在用戶app中,不是很建議經(jīng)常使用ContentProvider。不過對(duì)于系統(tǒng)級(jí)的app,它統(tǒng)一了數(shù)據(jù)操作的規(guī)范,利是遠(yuǎn)大于弊的。

ContentProvider發(fā)布

當(dāng)進(jìn)程第一次啟動(dòng)時(shí)候會(huì)調(diào)用handleBindApplication

if (!data.restrictedBackupMode) {
        if (!ArrayUtils.isEmpty(data.providers)) {
          installContentProviders(app, data.providers);
        }
      }

當(dāng)xml中有provider時(shí),進(jìn)行provider的發(fā)布

final ArrayList results =
      new ArrayList();
    for (ProviderInfo cpi : providers) {
      IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
          false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
      if (cph != null) {
        cph.noReleaseNeeded = true;
        results.add(cph);
      }
    }
    try {
      ActivityManagerNative.getDefault().publishContentProviders(
        getApplicationThread(), results);
    } catch (RemoteException ex) {
    }

@installProvider(這個(gè)方法先簡(jiǎn)單過一下,后面會(huì)繼續(xù)說)

final java.lang.ClassLoader cl = c.getClassLoader();
        localProvider = (ContentProvider)cl.
          loadClass(info.name).newInstance();
        provider = localProvider.getIContentProvider();

@installProviderAuthoritiesLocked

for (String auth : auths) {
      final ProviderKey key = new ProviderKey(auth, userId);
      final ProviderClientRecord existing = mProviderMap.get(key);
      if (existing != null) {
      } else {
        mProviderMap.put(key, pcr);
      }
    }

這里兩步把ProviderInfo通過installProvider轉(zhuǎn)換成ContentProvider的Binder對(duì)象IContentProvider,并放于ContentProviderHolder中。并根據(jù)auth的不同,把發(fā)布進(jìn)程的ProviderClientRecord保存在一個(gè)叫mProviderMap的成員變量中,方便第二次調(diào)用同一個(gè)ContentProvider時(shí),無需重新到AMS中去查詢。

AMS @publishContentProviders

final int N = providers.size();
      for (int i = 0; i < N; i++) {
        ContentProviderHolder src = providers.get(i);
        ...
        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);
          }
          int launchingCount = mLaunchingProviders.size();
          int j;
          boolean wasInLaunchingProviders = false;
          for (j = 0; j < launchingCount; j++) {
            if (mLaunchingProviders.get(j) == dst) {
              mLaunchingProviders.remove(j);
              wasInLaunchingProviders = true;
              j--;
              launchingCount--;
            }
          }
          if (wasInLaunchingProviders) {
            mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
          }
          ...
        }
      }

可以看到,AMS會(huì)遍歷所有的ContentProviderHolder,然后調(diào)用mProviderMap把信息保存起來,這塊接下來說。保存好之后,先去看看之前是不是已經(jīng)有l(wèi)aunch過的,如果已經(jīng)有l(wèi)aunch過的,不再重復(fù)launch。再說說這個(gè)mProviderMap,這個(gè)和ActivityThread中的mProviderMap不太一樣,這個(gè)是一個(gè)成員實(shí)例,非真正的map。看看putProviderByClass和putProviderByName。

ProviderMap@putProviderByClass

if (record.singleton) {
      mSingletonByClass.put(name, record);
    } else {
      final int userId = UserHandle.getUserId(record.appInfo.uid);
      getProvidersByClass(userId).put(name, record);
    }

ProviderMap@putProviderByName

if (record.singleton) {
      mSingletonByName.put(name, record);
    } else {
      final int userId = UserHandle.getUserId(record.appInfo.uid);
      getProvidersByName(userId).put(name, record);
    }

可以看到,發(fā)布的Provider實(shí)際會(huì)根據(jù)class或authority存在不同的map中。如果是單例,則分別存到相應(yīng)的mSingleton map中,否則就根據(jù)userId存到相應(yīng)的map中。這樣發(fā)布的過程就完成了,其他進(jìn)程需要使用的時(shí)候?qū)?huì)在AMS按需讀取。

ContentReslover跨進(jìn)程數(shù)據(jù)操作

當(dāng)我們跨進(jìn)程調(diào)用數(shù)據(jù)時(shí)候,會(huì)先調(diào)用獲取用戶進(jìn)程的ContentResolver

context.getContentResolver().query(uri, ...);
 public ContentResolver getContentResolver() {
    return mContentResolver;
  }

而這個(gè)ContentResolver在每個(gè)進(jìn)程中都存在有且唯一的實(shí)例,其在ContextImpl構(gòu)造函數(shù)中就已經(jīng)初始化了,其初始化的實(shí)際對(duì)象是ApplicationContentResolver。

mContentResolver = new ApplicationContentResolver(this, mainThread, user);

這個(gè)ContentResolver是活在調(diào)用者進(jìn)程中的,它是作為一個(gè)類似橋梁的作用。以插入為例:

ContentResolver@insert

IContentProvider provider = acquireProvider(url);
    if (provider == null) {
      throw new IllegalArgumentException("Unknown URL " + url);
    }
    try {
      long startTime = SystemClock.uptimeMillis();
      Uri createdRow = provider.insert(mPackageName, url, values);
      ...
      return createdRow;
    } catch (RemoteException e) {
      return null;
    } finally {
      releaseProvider(provider);
    }

問題就轉(zhuǎn)化成了,拿到其他進(jìn)程的ContentProvider的Binder對(duì)象,有了binder對(duì)象就可以跨進(jìn)程調(diào)用其方法了。

ContentResolver@acquireProvider

if (!SCHEME_CONTENT.equals(uri.getScheme())) {
      return null;
    }
    final String auth = uri.getAuthority();
    if (auth != null) {
      return acquireProvider(mContext, auth);
    }

校驗(yàn)其URI,其scheme必須為content。

ApplicationContentResolver@acquireProvider

protected IContentProvider acquireProvider(Context context, String auth) {
      return mMainThread.acquireProvider(context,
          ContentProvider.getAuthorityWithoutUserId(auth),
          resolveUserIdFromAuthority(auth), true);
    }

這里面有個(gè)特別的函數(shù)會(huì)傳遞一個(gè)true的參數(shù)給ActivityThread,這意味本次連接是stable的。那stable和非stable的區(qū)別是什么呢?這么說吧:

Stable provider:若使用過程中,provider要是掛了,你的進(jìn)程也必掛。

Unstable provider:若使用過程中,provider要是掛了,你的進(jìn)程不會(huì)掛。但你會(huì)收到一個(gè)DeadObjectException的異常,可進(jìn)行容錯(cuò)處理。

繼續(xù)往下。

ActivityThread@acquireProvider

 final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
    if (provider != null) {
      return provider;
    }

    IActivityManager.ContentProviderHolder holder = null;
    try {
      holder = ActivityManagerNative.getDefault().getContentProvider(
          getApplicationThread(), auth, userId, stable);
    } catch (RemoteException ex) {
    }
    if (holder == null) {
      return null;
    }

    holder = installProvider(c, holder, holder.info,
        true /*noisy*/, holder.noReleaseNeeded, stable);
    return holder.provider;

這里面分了三步,1、尋找自身進(jìn)程的緩存,有直接返回。 2、緩存沒有的話,尋找AMS中的Provider。3、InstallProvider,又到了這個(gè)方法。怎么個(gè)install法?還是一會(huì)兒再說。

@acquireExistingProvider (尋找自身緩存)

synchronized (mProviderMap) {
      final ProviderKey key = new ProviderKey(auth, userId);
      final ProviderClientRecord pr = mProviderMap.get(key);
      if (pr == null) {
        return null;
      }
      IContentProvider provider = pr.mProvider;
      IBinder jBinder = provider.asBinder();
      ...
      ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
      if (prc != null) {
        incProviderRefLocked(prc, stable);
      }
      return provider;

這一步就是讀取我們發(fā)布時(shí)提到的mProviderMap中的緩存。當(dāng)provider記錄存在,且進(jìn)程存活的情況下,則在provider引用計(jì)數(shù)不為空時(shí)則繼續(xù)增加引用計(jì)數(shù)。

緩存不存在,則去AMS中找

AMS@getContentProviderImpl

ContentProviderRecord cpr;
cpr = mProviderMap.getProviderByName(name, userId);
if (providerRunning){
  if (r != null && cpr.canRunHere(r)) {
          ContentProviderHolder holder = cpr.newHolder(null);
          holder.provider = null;
          return holder;
        }
}
 public boolean canRunHere(ProcessRecord app) {
    return (info.multiprocess || info.processName.equals(app.processName))
        && uid == app.info.uid;
  }

Provider是提供保護(hù)數(shù)據(jù)的接入訪問的。一般情況下,不同進(jìn)程的訪問只能通過IPC來進(jìn)行,但那是有些情況是可以允許訪問者在自己的進(jìn)程中創(chuàng)建本地Provider來進(jìn)行訪問的。

這種情況是在UID必須相同的前提下,要么同一進(jìn)程,要么provider設(shè)定了multiprocess為true。

if (!providerRunning) {
      cpi = AppGlobals.getPackageManager().resolveContentProvider(name,
          STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
      ...
      ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
      cpr = mProviderMap.getProviderByClass(comp, userId);
      if (r != null && cpr.canRunHere(r)) {
        return cpr.newHolder(null);
      }
      ProcessRecord proc = getProcessRecordLocked(
              cpi.processName, cpr.appInfo.uid, false);

          if (proc != null && proc.thread != null) {
            if (!proc.pubProviders.containsKey(cpi.name)) {
              proc.pubProviders.put(cpi.name, cpr);
              proc.thread.scheduleInstallProvider(cpi);
            }
          } else {
            proc = startProcessLocked(cpi.processName,
                cpr.appInfo, false, 0, "content provider",
                new ComponentName(cpi.applicationInfo.packageName,
                    cpi.name), false, false, false);
          }
        } 
      }
      mProviderMap.putProviderByName(name, cpr);
    }

這塊步驟比較多,挑重點(diǎn)就是,先從AMS的ProviderMap對(duì)象中獲取AMS緩存。獲得后如果Provider沒有l(wèi)aunch,則AMS通知其進(jìn)程install其provider。如果進(jìn)程不存在,則新孵化一個(gè)進(jìn)程。

@InstallProvider

回到第三步中的installProvider

private IActivityManager.ContentProviderHolder installProvider(Context context,
      IActivityManager.ContentProviderHolder holder, ProviderInfo info,
      boolean noisy, boolean noReleaseNeeded, boolean stable)

可以看到,這個(gè)方法里面有6個(gè)參數(shù),其中包含ContentProviderHolder、ProviderInfo、noReleaseNeeded,這幾個(gè)很重要的參數(shù)。

ContentProviderHolder:當(dāng)參數(shù)為空的時(shí)候,說明緩存為空,也就意味著是進(jìn)程啟動(dòng)的時(shí)候調(diào)用發(fā)布provider。當(dāng)緩存不為空的時(shí)候,還得做一些處理。

ProviderInfo:包含Provider的一些信息,不能為空。

noReleaseNeeded:為true的時(shí)候Provider對(duì)于自身進(jìn)程來說或系統(tǒng)的Provider,是永久install的,也就是不會(huì)被destory的。

ContentProvider localProvider = null;
    IContentProvider provider;
    if (holder == null || holder.provider == null) {
      try {
        final java.lang.ClassLoader cl = c.getClassLoader();
        localProvider = (ContentProvider)cl.
          loadClass(info.name).newInstance();
        provider = localProvider.getIContentProvider();
        if (provider == null) {
          return null;
        }
        localProvider.attachInfo(c, info);
      } catch (java.lang.Exception e) {
      }
    } else {
      provider = holder.provider;
    }

這部分在發(fā)布的時(shí)候已經(jīng)說了,緩存holder為null的時(shí)候,new一個(gè)實(shí)例。

IActivityManager.ContentProviderHolder retHolder;
    synchronized (mProviderMap) {
      IBinder jBinder = provider.asBinder();
      if (localProvider != null) {
        ComponentName cname = new ComponentName(info.packageName, info.name);
        ProviderClientRecord pr = mLocalProvidersByName.get(cname);
        if (pr != null) {
          provider = pr.mProvider;
        } else {
          holder = new IActivityManager.ContentProviderHolder(info);
          holder.provider = provider;
          holder.noReleaseNeeded = true;
          pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
          mLocalProviders.put(jBinder, pr);
          mLocalProvidersByName.put(cname, pr);
        }
        retHolder = pr.mHolder;
      } else {
        ...
      }

如果localProvider不等于null,則意味著是new一個(gè)實(shí)例的情況,這時(shí)候還是先去獲取緩存,沒有的話再真正地new一個(gè)ContentProviderHolder實(shí)例,并把通過installProviderAuthoritiesLocked方法把相關(guān)信息存入mProviderMap中,這個(gè)就是對(duì)應(yīng)發(fā)布Provider提的那個(gè)方法。

IActivityManager.ContentProviderHolder retHolder;
    synchronized (mProviderMap) {
      ...
      } else {
        ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
        if (prc != null) {
          if (!noReleaseNeeded) {
            incProviderRefLocked(prc, stable);
            try {
              ActivityManagerNative.getDefault().removeContentProvider(
                  holder.connection, stable);
            } 
          }
        } else {
          ProviderClientRecord client = installProviderAuthoritiesLocked(
              provider, localProvider, holder);
          if (noReleaseNeeded) {
            prc = new ProviderRefCount(holder, client, 1000, 1000);
          } else {
            prc = stable
                ? new ProviderRefCount(holder, client, 1, 0)
                : new ProviderRefCount(holder, client, 0, 1);
          }
          mProviderRefCountMap.put(jBinder, prc);
        }
        retHolder = prc.holder;
      }

如果localProvider等于空,也就意味著有holder緩存或者new時(shí)候出現(xiàn)的異常。那先從計(jì)數(shù)map中取緩存,如果緩存不為空(之前有過計(jì)數(shù)了),這時(shí)候如果設(shè)置了noReleaseNeeded,那就說明不需要計(jì)數(shù)。如果noReleaseNeeded為false,則把計(jì)數(shù)器數(shù)據(jù)轉(zhuǎn)移到一個(gè)新引用上,同時(shí)銷毀舊的。

如果緩存為空,說明之前沒有計(jì)數(shù)過。那還是先通過installProviderAuthoritiesLocked把信息保存到mProviderMap中。這時(shí)候如果noReleaseNeeded為true,把stable和非stable的數(shù)據(jù)都瞎設(shè)置了一個(gè)1000,反正用不到。。。否則就相應(yīng)的+1,并把計(jì)數(shù)器放入相應(yīng)的緩存中。最后再把holder返回。

再回到ContentResolver方法中,我們拿到了Provider的binder引用,就可以執(zhí)行相應(yīng)的方法了。

關(guān)于Android7.0中ContentProvider組件的作用是什么就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。


名稱欄目:Android7.0中ContentProvider組件的作用是什么
瀏覽路徑:http://weahome.cn/article/igphhp.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部