版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
【移动应用开发技术】Android7.0中ContentProvider组件的作用是什么
这篇文章将为大家详细讲解有关Android7.0中ContentProvider组件的作用是什么,文章内容质量较高,因此在下分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。作为Android的四大组件之一,ContentProvider作为进程之间静态数据传递的重要手段,其在系统级别的应用中起了重大的作用。毫无疑问,ContentProvider核心机制之一也是Binder,但是和其它3大组件又有区别。因为ContentProvider涉及数据的增删查改,当数据量比较大的时候,继续用Parcel做容器效率会比较低,因此它还使用了匿名共享内存的方式。但是有一个问题是,ContentProvider的提供者进程不再存活时,其他进程通过Provider读一个非常简单的数据时,都需要先把提供者进程启动起来(除非指定multiprocess=true),这对用户是相当不友好的。又因为其是间接通过db进行数据操作,所以效率也远不如直接操作db。因此在用户app中,不是很建议经常使用ContentProvider。不过对于系统级的app,它统一了数据操作的规范,利是远大于弊的。ContentProvider发布当进程第一次启动时候会调用handleBindApplicationif
(!data.restrictedBackupMode)
{
if
(!ArrayUtils.isEmpty(viders))
{
installContentProviders(app,
viders);
}
}当xml中有provider时,进行provider的发布final
ArrayList<IActivityManager.ContentProviderHolder>
results
=
new
ArrayList<IActivityManager.ContentProviderHolder>();
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(这个方法先简单过一下,后面会继续说)final
java.lang.ClassLoader
cl
=
c.getClassLoader();
localProvider
=
(ContentProvider)cl.
loadClass().newInstance();
provider
=
localProvider.getIContentProvider();@installProviderAuthoritiesLockedfor
(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转换成ContentProvider的Binder对象IContentProvider,并放于ContentProviderHolder中。并根据auth的不同,把发布进程的ProviderClientRecord保存在一个叫mProviderMap的成员变量中,方便第二次调用同一个ContentProvider时,无需重新到AMS中去查询。AMS@publishContentProvidersfinal
int
N
=
providers.size();
for
(int
i
=
0;
i
<
N;
i++)
{
ContentProviderHolder
src
=
providers.get(i);
...
ContentProviderRecord
dst
=
r.pubProviders.get();
if
(dst
!=
null)
{
ComponentName
comp
=
new
ComponentName(.packageName,
);
mProviderMap.putProviderByClass(comp,
dst);
String
names[]
=
.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会遍历所有的ContentProviderHolder,然后调用mProviderMap把信息保存起来,这块接下来说。保存好之后,先去看看之前是不是已经有launch过的,如果已经有launch过的,不再重复launch。再说说这个mProviderMap,这个和ActivityThread中的mProviderMap不太一样,这个是一个成员实例,非真正的map。看看putProviderByClass和putProviderByName。ProviderMap@putProviderByClassif
(record.singleton)
{
mSingletonByClass.put(name,
record);
}
else
{
final
int
userId
=
UserHandle.getUserId(record.appInfo.uid);
getProvidersByClass(userId).put(name,
record);
}ProviderMap@putProviderByNameif
(record.singleton)
{
mSingletonByName.put(name,
record);
}
else
{
final
int
userId
=
UserHandle.getUserId(record.appInfo.uid);
getProvidersByName(userId).put(name,
record);
}可以看到,发布的Provider实际会根据class或authority存在不同的map中。如果是单例,则分别存到相应的mSingletonmap中,否则就根据userId存到相应的map中。这样发布的过程就完成了,其他进程需要使用的时候将会在AMS按需读取。ContentReslover跨进程数据操作当我们跨进程调用数据时候,会先调用获取用户进程的ContentResolvercontext.getContentResolver().query(uri,
...);
public
ContentResolver
getContentResolver()
{
return
mContentResolver;
}而这个ContentResolver在每个进程中都存在有且唯一的实例,其在ContextImpl构造函数中就已经初始化了,其初始化的实际对象是ApplicationContentResolver。mContentResolver
=
new
ApplicationContentResolver(this,
mainThread,
user);这个ContentResolver是活在调用者进程中的,它是作为一个类似桥梁的作用。以插入为例:ContentResolver@insertIContentProvider
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);
}问题就转化成了,拿到其他进程的ContentProvider的Binder对象,有了binder对象就可以跨进程调用其方法了。ContentResolver@acquireProviderif
(!SCHEME_CONTENT.equals(uri.getScheme()))
{
return
null;
}
final
String
auth
=
uri.getAuthority();
if
(auth
!=
null)
{
return
acquireProvider(mContext,
auth);
}校验其URI,其scheme必须为content。ApplicationContentResolver@acquireProviderprotected
IContentProvider
acquireProvider(Context
context,
String
auth)
{
return
mMainThread.acquireProvider(context,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth),
true);
}这里面有个特别的函数会传递一个true的参数给ActivityThread,这意味本次连接是stable的。那stable和非stable的区别是什么呢?这么说吧:Stableprovider:若使用过程中,provider要是挂了,你的进程也必挂。Unstableprovider:若使用过程中,provider要是挂了,你的进程不会挂。但你会收到一个DeadObjectException的异常,可进行容错处理。继续往下。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,
,
true
/*noisy*/,
holder.noReleaseNeeded,
stable);
return
vider;这里面分了三步,1、寻找自身进程的缓存,有直接返回。2、缓存没有的话,寻找AMS中的Provider。3、InstallProvider,又到了这个方法。怎么个install法?还是一会儿再说。@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;这一步就是读取我们发布时提到的mProviderMap中的缓存。当provider记录存在,且进程存活的情况下,则在provider引用计数不为空时则继续增加引用计数。缓存不存在,则去AMS中找AMS@getContentProviderImplContentProviderRecord
cpr;
cpr
=
mProviderMap.getProviderByName(name,
userId);
if
(providerRunning){
if
(r
!=
null
&&
cpr.canRunHere(r))
{
ContentProviderHolder
holder
=
cpr.newHolder(null);
vider
=
null;
return
holder;
}
}
public
boolean
canRunHere(ProcessRecord
app)
{
return
(info.multiprocess
||
cessName.equals(cessName))
&&
uid
==
.uid;
}Provider是提供保护数据的接入访问的。一般情况下,不同进程的访问只能通过IPC来进行,但那是有些情况是可以允许访问者在自己的进程中创建本地Provider来进行访问的。这种情况是在UID必须相同的前提下,要么同一进程,要么provider设定了multiprocess为true。if
(!providerRunning)
{
cpi
=
AppGlobals.getPackageManager().resolveContentProvider(name,
STOCK_PM_FLAGS
|
PackageManager.GET_URI_PERMISSION_PATTERNS,
userId);
...
ComponentName
comp
=
new
ComponentName(cpi.packageName,
);
cpr
=
mProviderMap.getProviderByClass(comp,
userId);
if
(r
!=
null
&&
cpr.canRunHere(r))
{
return
cpr.newHolder(null);
}
ProcessRecord
proc
=
getProcessRecordLocked(
cessName,
cpr.appInfo.uid,
false);
if
(proc
!=
null
&&
proc.thread
!=
null)
{
if
(!proc.pubProviders.containsKey())
{
proc.pubProviders.put(,
cpr);
proc.thread.scheduleInstallProvider(cpi);
}
}
else
{
proc
=
startProcessLocked(cessName,
cpr.appInfo,
false,
0,
"content
provider",
new
ComponentName(cpi.applicationInfo.packageName,
),
false,
false,
false);
}
}
}
mProviderMap.putProviderByName(name,
cpr);
}这块步骤比较多,挑重点就是,先从AMS的ProviderMap对象中获取AMS缓存。获得后如果Provider没有launch,则AMS通知其进程install其provider。如果进程不存在,则新孵化一个进程。@InstallProvider回到第三步中的installProviderprivate
IActivityManager.ContentProviderHolder
installProvider(Context
context,
IActivityManager.ContentProviderHolder
holder,
ProviderInfo
info,
boolean
noisy,
boolean
noReleaseNeeded,
boolean
stable)可以看到,这个方法里面有6个参数,其中包含ContentProviderHolder、ProviderInfo、noReleaseNeeded,这几个很重要的参数。ContentProviderHolder:当参数为空的时候,说明缓存为空,也就意味着是进程启动的时候调用发布provider。当缓存不为空的时候,还得做一些处理。ProviderInfo:包含Provider的一些信息,不能为空。noReleaseNeeded:为true的时候Provider对于自身进程来说或系统的Provider,是永久install的,也就是不会被destory的。ContentProvider
localProvider
=
null;
IContentProvider
provider;
if
(holder
==
null
||
vider
==
null)
{
try
{
final
java.lang.ClassLoader
cl
=
c.getClassLoader();
localProvider
=
(ContentProvider)cl.
loadClass().newInstance();
provider
=
localProvider.getIContentProvider();
if
(provider
==
null)
{
return
null;
}
localProvider.attachInfo(c,
info);
}
catch
(java.lang.Exception
e)
{
}
}
else
{
provider
=
vider;
}这部分在发布的时候已经说了,缓存holder为null的时候,new一个实例。IActivityManager.ContentProviderHolder
retHolder;
synchronized
(mProviderMap)
{
IBinder
jBinder
=
provider.asBinder();
if
(localProvider
!=
null)
{
ComponentName
cname
=
new
ComponentName(info.packageName,
);
ProviderClientRecord
pr
=
mLocalProvidersByName.get(cname);
if
(pr
!=
null)
{
provider
=
pr.mProvider;
}
else
{
holder
=
new
IActivityManager.ContentProviderHolder(info);
vider
=
provider;
holder.noReleaseNeeded
=
true;
pr
=
installProviderAuthoritiesLocked(provider,
localProvider,
holder);
mLocalProviders.put(jBinder,
pr);
mLocalProvidersByName.put(cname,
pr);
}
retHolder
=
pr.mHolder;
}
else
{
...
}如果localProvider不等于null,则意味着是new一个实例的情况,这时候还是先去获取缓存,没有的话再真正地new一个ContentProviderHolder实例,并把通过installProviderAuthoritiesLocked方法把相关信息存入mProviderMap中,这个就是对应发布Provider提的那个方法。IActivityManager.ContentProviderHolder
retHolder;
synchronized
(mProviderMap)
{
...
}
else
{
ProviderRefCount
prc
=
mProviderRefCountMap.get(jBinder);
if
(prc
!=
null)
{
if
(!n
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 装饰合同补充协议版
- 追加合同协议书范本
- 外墙装修补充合同格式
- 团购协议合同范本在线编辑
- 窗帘购销合同模版
- 安全防护用品购销合同
- 食品添加剂可持续发展购销合同
- 建筑工程安装分包合同范本
- 2024南方鱼虾养殖收购合同范本
- 2024合同不履行的后果
- 变电系统运行过程中的跳闸故障原因及处理方法
- 2022年《中央企业合规管理办法》要点解读
- 一轮复习氯和其化合物公开课一等奖市优质课赛课获奖课件
- 项目部各岗位安全生产责任清单
- 武士与龙【经典绘本】
- 安丘山水水泥有限公司职业病危害风险分级报告
- 部编版小学语文四年级上册《希腊神话故事》测试题及答案
- 居民自建房经营业态不超过三种承诺书
- 工会换届报告 工会换届财务报告
- 铁路四显示自动闭塞-论文
- 中医内科学痞满专家讲座
评论
0/150
提交评论