概念
作为四大组件之一(Activity , Service , BroadcastReciver , ContentProvider),为我们提供了不同进程甚至是不同应用之间共享数据的机制。
启动性能
ContentProvider的onCreate()在Application.onCreate()之前调用,而且都是在main线程创建的。因此,自定义ContentProvider的构造函数,静态代码块,onCreate()都尽量不要做耗时操作,否则会拖慢启动速度。
ContentProvider提供的query, insert, delete, update都是在Binder线程中执行的。可能存在多线程并发的问题,因此方法内部要做好线程同步。
稳定性
ContentProvider在进行跨进程数据传递时,利用了 Binder 和 匿名共享内存 机制。概述:通过Binder传递CursorWindow对象内部的匿名共享内存的fd(文件描述符)。这样的话数据不需要跨进程,而是在不同的进程中通过fd来操作同一块匿名内存。
Android的Binder传输是有大小限制的,一般来说是 1~2M
安全性
如果在xml中注册ContentProvider时,android:exported="true"
,若支持执行SQL语句时就需要注意SQL注入的问题;
如果我们传入的参数是一个文件路径,然后返回文件的内容,这个时候要检验合法性,否则整个应用的私有数据都有可能泄露。
使用
- 自定义ContentProvider
public class CubeContentProvider extends ContentProvider {
@Override
public boolean onCreate() {
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
return null;
}
@Override
public String getType(Uri uri) {
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
return Uri.parse(Cube.AUTHORITY + getCloudValue(values));
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
- 注册ContentProvider
android:authorities: ContentProvider的唯一标识
android:permission:外部应用访问时,必须声明该权限
android:multiprocess=“true”:会在每一个进程创建一个ContentProvider实例,会出现多实例问题。没有必要设置该属性
<provider
android:authorities="xxx.CubeContentProvider"
android:name="xxx.CubeContentProvider"
android:permission = "xxx"
android:process=":provider"/>
- 访问数据
mContext.getContentResolver().insert(uri,null)
问题
- AMS如何启动ContentProvider?
AMS通过startProcessLocked()启动ContentProvider所在的进程的时候,会启动ContentProvider;在进程没有启动的时候,由于执行ContentProvider中的任意CRUD方法,也会触发进程和ContentProvider的启动。
class ActivityThread
public final IContentProvider acquireProvider(...){
// 检测ContentProvider是否已经在mProviderMap中存在
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if (provider != null) {
return provider;
}
// 通过AMS启动ContentProvider
ContentProviderHolder holder = null;
try {
synchronized (getGetProviderLock(auth, userId)) {
holder = ActivityManager.getService().getContentProvider(
getApplicationThread(), auth, userId, stable);
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
// 修改引用计数
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}
public static void main(String[] args){
// 在main线程中,准备一个消息通知机制
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
}
private void attach(boolean system, long startSeq) {
****
final IActivityManager mgr = ActivityManager.getService();
try {
// 最终会执行到bindApplication
mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
****
}
public final void bindApplication(***){
AppBindData data = new AppBindData();
***
省略代码
***
sendMessage(H.BIND_APPLICATION, data);
// 之后执行到handleBindApplication(data);
}
// 完成ContentProvider的创建启动以及调用Application,onCreate()
private void handleBindApplication(AppBindData data) {
// 创建ContextImpl和Instrumentation
final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
appContext.getClassLoader(), false, true, false);
final ContextImpl instrContext = ContextImpl.createAppContext(this, pi);
try {
final ClassLoader cl = instrContext.getClassLoader();
mInstrumentation = (Instrumentation)
cl.loadClass(data.instrumentationName.getClassName()).newInstance();
} catch (Exception e) {
throw new RuntimeException(
"Unable to instantiate instrumentation "
+ data.instrumentationName + ": " + e.toString(), e);
}
final ComponentName component = new ComponentName(ii.packageName, ii.name);
mInstrumentation.init(this, instrContext, appContext, component,
data.instrumentationWatcher, data.instrumentationUiAutomationConnection);
// 创建Application对象
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
// 启动当前进程的ContentProvider并调用其onCreate()
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
installContentProviders(app, data.providers);
// For process that contains content providers, we want to
// ensure that the JIT is enabled "at some point".
mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
}
}
}
// 调用Application.onCreatr()
mInstrumentation.callApplicationOnCreate(app);