ContentProvider使用与query流程分析
涉及文件:
frameworks\base\core\java\android\content\pm\PackageParser.java
frameworks\base\core\java\android\app\ContextImpl.java
frameworks\base\core\java\android\app\ActivityThread.java
frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java
frameworks\base\core\java\android\content\ContentResolver.java
frameworks\base\core\java\android\content\ContentProvider.java
contentProvider使用示例:
Manifest文件中定义provider,指定authorities,permission,权限还可以细分为readpermission和writepermission。
新建类继承自ContentProvider,重载query,insert,delete,update四个接口,分别实现数据库的增删改查
public class HealthProvider extends ContentProvider {private final static String TAG = "HealthProvider:";private DbHelper dbHelper;private static final UriMatcher matcher;static { matcher = new UriMatcher(UriMatcher.NO_MATCH); matcher.addURI(DbConstant.AUTHOR,DbConstant.TB_FATIGUE,DbConstant.CODE_FATIGUE); matcher.addURI(DbConstant.AUTHOR,DbConstant.TB_HEALTH_REPORT,DbConstant.CODE_HEALTH_REPORT); matcher.addURI(DbConstant.AUTHOR,DbConstant.TB_SLEEP,DbConstant.CODE_SLEEP); matcher.addURI(DbConstant.AUTHOR,DbConstant.TB_TIME_SET,DbConstant.CODE_TIME); matcher.addURI(DbConstant.AUTHOR,DbConstant.TB_SCREEN,DbConstant.CODE_SCREEN);}@Overridepublic boolean onCreate() { dbHelper = new DbHelper(getContext()); return true;}@Nullable@Overridepublic Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { String table = getType(uri); SQLiteDatabase database = dbHelper.getReadableDatabase(); if(table==null || database==null) { return null; } Cursor cursor = database.query(table,projection,selection,selectionArgs,null,null,sortOrder); return cursor;}@Nullable@Overridepublic String getType(@NonNull Uri uri) { String table = null; switch (matcher.match(uri)) { case DbConstant.CODE_FATIGUE: table = DbConstant.TB_FATIGUE; break; case DbConstant.CODE_HEALTH_REPORT: table = DbConstant.TB_HEALTH_REPORT; break; case DbConstant.CODE_SLEEP: table = DbConstant.TB_SLEEP; break; case DbConstant.CODE_TIME: table = DbConstant.TB_TIME_SET; break; case DbConstant.CODE_SCREEN: table = DbConstant.TB_SCREEN; break; default: break; } return table;}@Nullable@Overridepublic Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) { String table = getType(uri); SQLiteDatabase database = dbHelper.getWritableDatabase(); database.insert(table,null,contentValues); return uri;}@Overridepublic int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { String table = getType(uri); SQLiteDatabase database = dbHelper.getWritableDatabase(); int i = database.delete(table,selection,selectionArgs); return 0;}@Overridepublic int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String selection, @Nullable String[] selectionArgs) { String table = getType(uri); SQLiteDatabase database = dbHelper.getWritableDatabase(); int i = database.update(table,contentValues,selection,selectionArgs); return 0;}}
新建类继承自SQLiteOpenHelper,分别实现构造方法和onCreate(完成数据库新建操作)、onUpgrade(完成数据库升级)、onDowngrade(完成数据库的降级)
public class DbHelper extends SQLiteOpenHelper { public static String CREATE_FATIGUE="CREATE TABLE IF NOT EXISTS fatigue(" + "id INTEGER PRIMARY KEY," + "total_s INT DEFAULT 0," + "total_m INT DEFAULT 0," + "total_mi INT DEFAULT 0," + "total_f INT DEFAULT 0," + "unlock_s INT DEFAULT 0," + "unlock_m INT DEFAULT 0," + "unlock_mi INT DEFAULT 0," + "unlock_f INT DEFAULT 0," + "app_s INT DEFAULT 0," + "app_m INT DEFAULT 0," + "app_mi INT DEFAULT 0," + "app_f INT DEFAULT 0" + ");"; public static final String CREATE_SLEEP = "CREATE TABLE IF NOT EXISTS sleep_data(" + "id INTEGER PRIMARY KEY," + "start_time INTEGER DEFAULT 0," + "end_time INTEGER DEFAULT 0," + "sleep_time INTEGER DEFAULT 0," + "weekdata VARCHAR(50) DEFAULT NULL," + "modify_time LONG);"; public static final String CREATE_TIME = "CREATE TABLE IF NOT EXISTS time_data(" + "id INTEGER PRIMARY KEY," + "notice_enable INTEGER DEFAULT 1," + "device_oneday LONG DEFAULT 28800000," + "device_once LONG DEFAULT 7200000," + "app_once LONG DEFAULT 7200000" + ");"; public static final String CREATE_SCREEN = "CREATE TABLE IF NOT EXISTS screen_data(" + "id INTEGER PRIMARY KEY," + "modify_date LONG," + "screen_on LONG," + "screen_off LONG," + "screen_on_off_duration INTEGER" + ");"; public static final String DROP_FATIGUE = "drop table fatigue"; public static final String DROP_SLEEP = "drop table sleep_data"; public static final String DROP_TIME = "drop table time_data"; public static final String DROP_SCREEN = "drop table screen_table"; public DbHelper(Context cn) { super(cn, DbConstant.DATABASE, null, DbConstant.DB_VERSION); } public DbHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { super(context, DbConstant.DATABASE, factory, DbConstant.DB_VERSION); } @Override public void onCreate(SQLiteDatabase sqLiteDatabase) { sqLiteDatabase.execSQL(CREATE_FATIGUE); sqLiteDatabase.execSQL(CREATE_SLEEP); sqLiteDatabase.execSQL(CREATE_TIME); sqLiteDatabase.execSQL(CREATE_SCREEN); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL(DROP_FATIGUE); db.execSQL(DROP_SLEEP); db.execSQL(DROP_TIME); onCreate(db); } @Override public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL(DROP_FATIGUE); db.execSQL(DROP_SLEEP); db.execSQL(DROP_TIME); onCreate(db); }}
以上就完成了数据库的准备工作,需要使用数据库时如下调用即可:
ContentResolver cr = context.getContentResolver();Uri uri = Uri.parse(UriStr);//uri组成:content://+manifest中定义的authorities+/+数据库表名Cursor cursor = cr.query(uri,null,null,null,null);ArrayList list = new ArrayList<>();if(cursor == null) { return list;}while(cursor.moveToNext()) { SleepData data = new SleepData(); data.setStartTime(cursor.getInt(cursor.getColumnIndexOrThrow("start_time"))); data.setEndTime(cursor.getInt(cursor.getColumnIndexOrThrow("end_time"))); data.setSleepTime(cursor.getInt(cursor.getColumnIndexOrThrow("sleep_time"))); data.setDate(cursor.getString(cursor.getColumnIndexOrThrow("weekdata"))); data.setModifyTime(cursor.getLong(cursor.getColumnIndexOrThrow("modify_time"))); list.add(data);}
query调用流程如下图:
通过调用contentresolver获得需要读写的数据库的provider的代理对象,获取过程分为当前进程下的和跨进程的获取,如果跨进程,并且对应进程当前并没有在后台中,
则会启动该进程,然后再考虑从该进程的activitythread中获取安装的provider对象,然后在这个代理对象上进行增删改查的操作。
query方法如下:获得cursor指针主要查看ActivithThread中acquireProvider方法
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri, @Nullable String[] projection, @Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal) { Preconditions.checkNotNull(uri, "uri"); IContentProvider unstableProvider = acquireUnstableProvider(uri); if (unstableProvider == null) { return null; } IContentProvider stableProvider = null; Cursor qCursor = null; try { long startTime = SystemClock.uptimeMillis(); //...... try { qCursor = unstableProvider.query(mPackageName, uri, projection, queryArgs, remoteCancellationSignal); } catch (DeadObjectException e) { //如果进程不存在的情况下 unstableProviderDied(unstableProvider); stableProvider = acquireProvider(uri); if (stableProvider == null) { return null; } qCursor = stableProvider.query( mPackageName, 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); // Wrap the cursor object into CursorWrapperInner object. final IContentProvider provider = (stableProvider != null) ? stableProvider : acquireProvider(uri); final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider); stableProvider = null; qCursor = null; return wrapper; } catch (RemoteException e) { // Arbitrary and not worth documenting, as Activity // Manager will kill this process shortly anyway. return null; } finally { //...... }}
ApplicationContentResolver如下:
private static final class ApplicationContentResolver extends ContentResolver { private final ActivityThread mMainThread; public ApplicationContentResolver(Context context, ActivityThread mainThread) { super(context); mMainThread = Preconditions.checkNotNull(mainThread); } @Override protected IContentProvider acquireProvider(Context context, String auth) { return mMainThread.acquireProvider(context, ContentProvider.getAuthorityWithoutUserId(auth), resolveUserIdFromAuthority(auth), true); } @Override protected IContentProvider acquireUnstableProvider(Context c, String auth) { return mMainThread.acquireProvider(c, ContentProvider.getAuthorityWithoutUserId(auth), resolveUserIdFromAuthority(auth), false); } //......}
ActivityThread中acquireProvider如下:
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; try { //同步,保证先到先得 synchronized (getGetProviderLock(auth, userId)) { holder = ActivityManager.getService().getContentProvider( getApplicationThread(), auth, userId, stable); } } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } if (holder == null) { Slog.e(TAG, "Failed to find provider info for " + auth); return null; } // 安装该provider holder = installProvider(c, holder, holder.info, true /*noisy*/, holder.noReleaseNeeded, stable); return holder.provider;}
AMS中获取方法如下:
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller, String name, IBinder token, boolean stable, int userId) { ContentProviderRecord cpr; ContentProviderConnection conn = null; ProviderInfo cpi = null; synchronized(this) { //无法根据caller查找到对应app,则抛出security异常 boolean checkCrossUser = true; checkTime(startTime, "getContentProviderImpl: getProviderByName"); // 首先检查该content provider是否已发布 cpr = mProviderMap.getProviderByName(name, userId); //...... boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed; //该provider正在运行 if (providerRunning) { cpi = cpr.info; String msg; checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission"); //检查调用者是否拥有读取数据库权限,如没有,则抛出异常 if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser)) != null) { throw new SecurityException(msg); } checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission"); if (r != null && cpr.canRunHere(r)) { //provider存在,并且进程存在,则直接返回 ContentProviderHolder holder = cpr.newHolder(null); // don't give caller the provider object, it needs // to make its own. holder.provider = null; return holder; } // 正常app与instant ap之间不互相暴露 try { if (AppGlobals.getPackageManager() .resolveContentProvider(name, 0 /*flags*/, userId) == null) { return null; } } catch (RemoteException e) { } final long origId = Binder.clearCallingIdentity(); checkTime(startTime, "getContentProviderImpl: incProviderCountLocked"); //...... if (!providerRunning) { try { checkTime(startTime, "getContentProviderImpl: before resolveContentProvider"); cpi = AppGlobals.getPackageManager(). resolveContentProvider(name, STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId); checkTime(startTime, "getContentProviderImpl: after resolveContentProvider"); } catch (RemoteException ex) { } if (cpi == null) { return null; } //检查singleton 属性,并且检查调用者权限 //system ready前调用system provider 返回异常 //system providers 未安装,抛出异常 //明确provider归属用户,如果用户管理尚未启动,则不允许该provider运行 if (!mUserController.isUserRunning(userId, 0)) { Slog.w(TAG, "Unable to launch app " + cpi.applicationInfo.packageName + "/" + cpi.applicationInfo.uid + " for provider " + name + ": user " + userId + " is stopped"); return null; } ComponentName comp = new ComponentName(cpi.packageName, cpi.name); checkTime(startTime, "getContentProviderImpl: before getProviderByClass"); cpr = mProviderMap.getProviderByClass(comp, userId); checkTime(startTime, "getContentProviderImpl: after getProviderByClass"); final boolean firstClass = cpr == null; if (firstClass) { final long ident = Binder.clearCallingIdentity(); // If permissions need a review before any of the app components can run, // we return no provider and launch a review activity if the calling app // is in the foreground. if (mPermissionReviewRequired) { if (!requestTargetProviderPermissionsReviewIfNeededLocked(cpi, r, userId)) { return null; } } try { checkTime(startTime, "getContentProviderImpl: before getApplicationInfo"); ApplicationInfo ai = AppGlobals.getPackageManager(). getApplicationInfo( cpi.applicationInfo.packageName, STOCK_PM_FLAGS, userId); checkTime(startTime, "getContentProviderImpl: after getApplicationInfo"); if (ai == null) { Slog.w(TAG, "No package info for content provider " + cpi.name); return null; } ai = getAppInfoForUser(ai, userId); cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton); } catch (RemoteException ex) { // pm is in same process, this will never happen. } finally { Binder.restoreCallingIdentity(ident); } } //...... //如果provider尚未启动,则拉起它 if (i >= N) { try { // Content provider is now in use, its package can't be stopped. // Use existing process if already started if (proc != null && proc.thread != null && !proc.killed) { if (!proc.pubProviders.containsKey(cpi.name)) { try { //进程存在后台,未被kill掉,调用该进程,安装provider proc.thread.scheduleInstallProvider(cpi); } catch (RemoteException e) { } } } else { //启动该provider所在进程,指定provider所在component proc = startProcessLocked(cpi.processName, cpr.appInfo, false, 0, "content provider", new ComponentName(cpi.applicationInfo.packageName, cpi.name), false, false, false); } //...... } //等待 provider 发布出来 synchronized (cpr) { while (cpr.provider == null) { //...... try { if (DEBUG_MU) Slog.v(TAG_MU, "Waiting to start provider " + cpr + " launchingApp=" + cpr.launchingApp); 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;}
private void installContentProviders( Context context, List providers) { final ArrayList results = new ArrayList<>(); for (ProviderInfo cpi : providers) { if (DEBUG_PROVIDER) { StringBuilder buf = new StringBuilder(128); buf.append("Pub "); buf.append(cpi.authority); buf.append(": "); buf.append(cpi.name); Log.i(TAG, buf.toString()); } //安装provider,通过将该provider加入到mProviderMap队列中 ContentProviderHolder cph = installProvider(context, null, cpi, false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/); if (cph != null) { cph.noReleaseNeeded = true; results.add(cph); } } try { //将列表中的provider发布到AMS中的mProviderMap下的mSingletonByName队列中 ActivityManager.getService().publishContentProviders( getApplicationThread(), results); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); }}
private ContentProviderHolder installProvider(Context context, ContentProviderHolder holder, ProviderInfo info, boolean noisy, boolean noReleaseNeeded, boolean stable) { ContentProvider localProvider = null; IContentProvider provider; //参数检查,provider的获取过程 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 ContentProviderHolder(info); holder.provider = provider; holder.noReleaseNeeded = true; //如果不存在,则将其加入到mProviderMap、 mLocalProviders、mLocalProvidersByName三个队列中 pr = installProviderAuthoritiesLocked(provider, localProvider, holder); mLocalProviders.put(jBinder, pr); mLocalProvidersByName.put(cname, pr); } retHolder = pr.mHolder; } else { ProviderRefCount prc = mProviderRefCountMap.get(jBinder); if (prc != null) { if (!noReleaseNeeded) { incProviderRefLocked(prc, stable); try { ActivityManager.getService().removeContentProvider( holder.connection, stable); } catch (RemoteException e) { //do nothing content provider object is dead any way } } } else { //加入到mProviderMap队列中 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队列中 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); if (provider != null) { // If this provider is hosted by the core OS and cannot be upgraded, // then I guess we're okay doing blocking calls to it. for (String auth : auths) { switch (auth) { case ContactsContract.AUTHORITY: case CallLog.AUTHORITY: case CallLog.SHADOW_AUTHORITY: case BlockedNumberContract.AUTHORITY: case CalendarContract.AUTHORITY: case Downloads.Impl.AUTHORITY: case "telephony": Binder.allowBlocking(provider.asBinder()); } } } 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;}
public final void publishContentProviders(IApplicationThread caller, List providers) { if (providers == null) { return; } enforceNotIsolatedCaller("publishContentProviders"); synchronized (this) { final ProcessRecord r = getRecordForAppLocked(caller); if (DEBUG_MU) Slog.v(TAG_MU, "ProcessRecord uid = " + r.uid); if (r == null) { //抛出异常 } final long origId = Binder.clearCallingIdentity(); 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; } ContentProviderRecord dst = r.pubProviders.get(src.info.name); if (DEBUG_MU) Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid); if (dst != null) { ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name); //加入到mProviderMap类的mSingletonByClass队列中 mProviderMap.putProviderByClass(comp, dst); String names[] = dst.info.authority.split(";"); for (int j = 0; j < names.length; j++) { //加入mProviderMap类的mSingletonByName队列中 mProviderMap.putProviderByName(names[j], dst); } //处理等待启动provider队列,如果启动了,则会从队列中移出 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); } synchronized (dst) { dst.provider = src.provider; dst.proc = r; dst.notifyAll(); } updateOomAdjLocked(r, true); maybeUpdateProviderUsageStatsLocked(r, src.info.packageName, src.info.authority); } } Binder.restoreCallingIdentity(origId); }}
更多相关文章
- Android 数据库Sqlite的使用(1)
- Android夸进程通信机制九:AIDL深入了解
- Android夸进程通信机制三:Messenger与Message
- Android夸进程通信机制六:使用ContentProvider进行进程间通信
- Android夸进程通信机制八:使用 AIDL进行进程间通信