編輯:關於Android編程
內容提供者顧名思義是用來提供數據的,現在假設我要獲取系統聯系人的數據,可以這樣查詢:
getContentResolver().query(Contacts.CONTENT_URI, null, null, null, null);
這樣就會跳轉到聯系人的provider的query:
public class ContactsProvider2 extends AbstractContactsProvider
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal) {
這個過程是如何做到的?下面來跟下這個流程:
調用getContentResolver會進入ContextImpl.java:
public ContentResolver getContentResolver() { return mContentResolver; }
mContentResolver是ApplicationContentResolver類型,在實例化ContextImpl的時候初始化。
接著進入ContentResolver.java中的query:
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal) { Preconditions.checkNotNull(uri, "uri"); IContentProvider unstableProvider = acquireUnstableProvider(uri); if (unstableProvider == null) { return null; } qCursor = unstableProvider.query(mPackageName, uri, projection, selection, selectionArgs, sortOrder, remoteCancellationSignal); }
先調用acquireUnstableProvider來獲取IContentProvider binder接口,這個接口的真正的實現者其實是ContentProvider中的Transport:
class Transport extends ContentProviderNative {
abstract public class ContentProviderNative extends Binder implements IContentProvider {
可以看到ContentProviderNative繼承Binder實現IContentProvider接口,就是服務的中間者stub的替身,真正的實現者在它的子類當中:
class Transport extends ContentProviderNative { AppOpsManager mAppOpsManager = null; int mReadOp = AppOpsManager.OP_NONE; int mWriteOp = AppOpsManager.OP_NONE; @Override public Cursor query(String callingPkg, Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, ICancellationSignal cancellationSignal) { Cursor cursor = ContentProvider.this.query(uri, projection, selection, selectionArgs, sortOrder, CancellationSignal.fromTransport( cancellationSignal)); }
這裡調用ContentProvider.this.query其實就是調用ContentProvider子類的query,比如這裡調用的是聯系人的ContactsProvider2,它繼承AbstractContactsProvider,AbstractContactsProvider繼承ContentProvider。
知道了這個binder的真身後,我們回到前面ContentResolver中的query中,是通過acquireUnstableProvider來獲取binder接口,跟蹤到ApplicationContentResolver中的acquireUnstableProvider:
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; } // There is a possible race here. Another thread may try to acquire // the same provider at the same time. When this happens, we want to ensure // that the first one wins. // Note that we cannot hold the lock while acquiring and installing the // provider since it might take a long time to run and it could also potentially // be re-entrant in the case where the provider is in the same process. IActivityManager.ContentProviderHolder holder = null; try { holder = ActivityManagerNative.getDefault().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; } // Install provider will increment the reference count for us, and break // any ties in the race. holder = installProvider(c, holder, holder.info, true /*noisy*/, holder.noReleaseNeeded, stable); return holder.provider; }
該方法同樣先調用acquireExistingProvider獲取緩存中的IContentProvider binder本地代理對象,如果不存在,則跨進程到AMS中獲取,獲取到後調用installProvider來創建provider,最後返回provider的本地代理ContentProviderProxy。
下面進入AMS看下getContentProvider是如何獲取這個holder:
public final ContentProviderHolder getContentProvider( IApplicationThread caller, String name, int userId, boolean stable) { return getContentProviderImpl(caller, name, null, stable, userId); }
進入getContentProviderImpl:
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller, String name, IBinder token, boolean stable, int userId) { ContentProviderRecord cpr; ContentProviderConnection conn = null; ProviderInfo cpi = null; // First check if this content provider has been published... cpr = mProviderMap.getProviderByName(name, userId); cpi = AppGlobals.getPackageManager(). resolveContentProvider(name, STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId); cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton); // If the provider is not already being launched, then get it started. proc = startProcessLocked(cpi.processName, cpr.appInfo, false, 0, "content provider", new ComponentName(cpi.applicationInfo.packageName, cpi.name), false, false, false); cpr.launchingApp = proc; mLaunchingProviders.add(cpr); // Make sure the provider is published (the same provider class // may be published under multiple names). if (firstClass) { mProviderMap.putProviderByClass(comp, cpr); } mProviderMap.putProviderByName(name, cpr);
synchronized (cpr) { while (cpr.provider == null) { if (cpr.launchingApp == null) { Slog.w(TAG, "Unable to launch app " + cpi.applicationInfo.packageName + "/" + cpi.applicationInfo.uid + " for provider " + name + ": launching app became null"); EventLog.writeEvent(EventLogTags.AM_PROVIDER_LOST_PROCESS, UserHandle.getUserId(cpi.applicationInfo.uid), cpi.applicationInfo.packageName, cpi.applicationInfo.uid, name); return 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; } } } }
首先會去檢查content provider是否已經發布了,如果沒有就會創建ContentProviderRecord對象,如果provider所在進程未啟動,接著會調用startProcessLocked啟動進程。接著等待新進程保存的provider,獲取provider信息後就返回ContentProviderHolder。
下面看下startProcessLocked流程:
private final void startProcessLocked(ProcessRecord app, String hostingType, String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) { if (entryPoint == null) entryPoint = "android.app.ActivityThread"; Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " + app.processName); checkTime(startTime, "startProcess: asking zygote to start proc"); Process.ProcessStartResult startResult = Process.start(entryPoint, app.processName, uid, uid, gids, debugFlags, mountExternal, app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet, app.info.dataDir, entryPointArgs); }
進入Process.start會通知zygote開啟進程,接著進入ActivityThread.main:
public static void main(String[] args) { Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } Looper.loop(); }
創建消息隊列後,進入ActivityThread.attach:
private void attach(boolean system) { final IActivityManager mgr = ActivityManagerNative.getDefault(); try { mgr.attachApplication(mAppThread); } }
然後跨進程進入AMS的attachApplication:
public final void attachApplication(IApplicationThread thread) { synchronized (this) { int callingPid = Binder.getCallingPid(); final long origId = Binder.clearCallingIdentity(); attachApplicationLocked(thread, callingPid); Binder.restoreCallingIdentity(origId); } }
進入attachApplicationLocked:
thread.bindApplication(processName, appInfo, providers, app.instrumentationClass, profilerInfo, app.instrumentationArguments, app.instrumentationWatcher, app.instrumentationUiAutomationConnection, testMode, mBinderTransactionTrackingEnabled, enableTrackAllocation, isRestrictedBackupMode || !normalMode, app.persistent, new Configuration(mConfiguration), app.compat, getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked());
接著又跨進程返回ApplicationThread中的bindApplication:
public final void bindApplication(String processName, ApplicationInfo appInfo, Listproviders, ComponentName instrumentationName, ProfilerInfo profilerInfo, Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher, IUiAutomationConnection instrumentationUiConnection, int debugMode, boolean enableBinderTracking, boolean trackAllocation, boolean isRestrictedBackupMode, boolean persistent, Configuration config, CompatibilityInfo compatInfo, Map services, Bundle coreSettings) { sendMessage(H.BIND_APPLICATION, data); }
這裡發送消息BIND_APPLICATION,然後接收消息:
case BIND_APPLICATION: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication"); AppBindData data = (AppBindData)msg.obj; handleBindApplication(data); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break;
進入handleBindApplication:
private void handleBindApplication(AppBindData data) { installContentProviders(app, data.providers); }
private void installContentProviders( Context context, Listproviders) { 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()); } 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) { throw ex.rethrowFromSystemServer(); } }
這個函數主要是做了兩件事情,一是調用installProvider來在本地安裝每一個Content Proivder的信息,並且為每一個Content Provider創建一個ContentProviderHolder對象來保存相關的信息。ContentProviderHolder對象是一個Binder對象,是用來把Content Provider的信息傳遞給ActivityManagerService服務的。當這些Content Provider都處理好了以後,還要調用ActivityManagerService服務的publishContentProviders函數來通知ActivityManagerService服務,這個進程中所要加載的Content Provider,都已經准備完畢了,而ActivityManagerService服務的publishContentProviders函數的作用就是用來喚醒在前面等待的線程的了。
下面看下installProvider:
private IActivityManager.ContentProviderHolder installProvider(Context context, IActivityManager.ContentProviderHolder holder, ProviderInfo info, boolean noisy, boolean noReleaseNeeded, boolean stable) { ContentProvider localProvider = null; IContentProvider provider; final java.lang.ClassLoader cl = c.getClassLoader(); localProvider = (ContentProvider)cl. loadClass(info.name).newInstance(); provider = localProvider.getIContentProvider(); localProvider.attachInfo(c, info); }
這個函數的作用主要就是在應用程序進程中把相應的Content Provider類加載進來了,調用getIContentProvider獲取本地binder對象。ContentProvider類和Transport類的關系就類似於ActivityThread和ApplicationThread的關系,其它應用程序不是直接調用ContentProvider接口來訪問它的數據,而是通過調用它的內部對象mTransport來間接調用ContentProvider的接口,接著調用attachInfo函數調用provider的onCreate方法。
接著看下AMS的publishContentProviders方法:
public final void publishContentProviders(IApplicationThread caller, Listproviders) { synchronized (this) { final ProcessRecord r = getRecordForAppLocked(caller); if (DEBUG_MU) Slog.v(TAG_MU, "ProcessRecord uid = " + r.uid); if (r == null) { throw new SecurityException( "Unable to find app for caller " + caller + " (pid=" + Binder.getCallingPid() + ") when publishing content providers"); } 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.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); } synchronized (dst) { dst.provider = src.provider; dst.proc = r; dst.notifyAll(); } updateOomAdjLocked(r); maybeUpdateProviderUsageStatsLocked(r, src.info.packageName, src.info.authority); } } } }
首先是把這個Content Provider信息保存好在mProvidersByClass和mProvidersByName中,這兩個Map中,一個是以類名為鍵值保存Content Provider信息,一個是以authority為鍵值保存Content Provider信息。執行了dst.notiryAll語句後,在前面等待要獲取的Content Provider接口加載完畢的線程就被喚醒了。
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller, String name, IBinder token, boolean stable, int userId) { cpr.wait(); return cpr != null ? cpr.newHolder(conn) : null; }
喚醒之後,它檢查本地ContentProviderRecord變量cpr的provider域不為null,於是就返回了。
它最終返回到ActivityThread類的acquireProvider函數中。
public final IContentProvider acquireProvider( Context c, String auth, int userId, boolean stable) { holder = installProvider(c, holder, holder.info,true /*noisy*/, holder.noReleaseNeeded, stable); return holder.provider; }
這裡同樣是執行installProvider函數,這裡傳進來的參數provider是不為null的,因此,它不需要執行在本地加載Content Provider的工作,只需要把從ActivityMangerService中獲得的Content Provider接口保存在成員變量mProviderMap中就可以了:
private IActivityManager.ContentProviderHolder installProvider(Context context, IActivityManager.ContentProviderHolder holder, ProviderInfo info, boolean noisy, boolean noReleaseNeeded, boolean stable) { provider = holder.provider; 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); }
這裡我們分析Android應用程序組件Content Provider在不同進程中傳輸數據的過程,即Content Provider在不同應用程序中共享數據的原理。
概述RecyclerView出現已經有一段時間了,相信大家肯定不陌生了,大家可以通過導入support-v7對其進行使用。 據官方的介紹,該控件用於在有限的窗口中展示大量
TabWidget類似於Android 中查看電話薄的界面,通過多個標簽切換顯示不同內容。要實現這一效果,首先要了解TabHost,它是一個用來存放多個Tab標簽的容器。
小米手環需要安卓4.4及以上系統,於是很多朋友購買之後進行手機系統升級,但是卻發現小米手環充不了電。遇到這種情況要怎麼辦呢?那麼小米手環充不了電怎麼辦呢?小
本文實例講述了Android自定義圓形進度條,分享給大家供大家參考。具體如下:大家也可以參考這兩篇文章進行學習: 《自定義Android圓形進度條(附源碼)》