美文网首页源码分析Android技术知识Android开发
分分钟钟看懂ContentProvider启动源码流程

分分钟钟看懂ContentProvider启动源码流程

作者: umbrella1 | 来源:发表于2017-07-06 17:03 被阅读532次
    类图1
    类图2

    为了放大时序图,一些类缩小下:
    CR:ContentResolver
    ACR:ApplicationContentResolver
    CPN:ContentProviderNative
    CPR:ContentProviderProxy
    AMN:ActivityManagerNative
    AMP:ActivityManagerProxy
    AMS:ActivityManagerService
    ATP:ApplicationThreadProxy


    时序图
    时序图
    1. A应用发送信息给AMS(ActivityManagerService,进程system_server)要访问B应用的XXXContentProvider
    2. AMS检查B应用没有被启动过,则新开一个进程启动B应用
    3. 启动应用B后,AMS向B应用启动XXXContentProvider,并实行相应的onCreate,返回IActivityManager.ContentProviderHolder,其中实现IContentProvider接口的Transact
    4. AMS 把IActivityManager.ContentProviderHolder对象返回给A应用,A应用改造成代理ContentProviderProxy(即IContentProvider),此时A应用就可以调用增删改查等接口到B应用了。
      照着类图和时序图的步数来分析:
      在A应用进程处理:Step1、2、3、4、5、6、18、19
      在AMS system_server进程处理:Step7、10、11、12、17
      在B应用进程处理:Step8、9、13、14、15、16、20

    Step1:
      ContentResolver contentResolver = Conext.getContentResolver();
    返回是ApplicationContentResolver,定义在ContextImpl内部静态类。

    Step2:
      Uri uri = Uri.parse("content://cn.umbrella.providers.contact/item");
    Cursor cursor = contentResolver.query(uri, new String[]{"id","name","phone"}, null, null, "id asc");
    在ApplicationContentResolver.acquireProvider()调用ActivityThread类的acquireProvider函数进一步执行获取Content Provider接口的操作。

    Step3、4、5:
      ActivityThread. acquireProvider:先本地查找,若有,则直接返回,没有则调用ActivityManagerNative.getDefault().getContentProvider(getApplicationThread(), auth, userId, stable)即ActivityManagerProxy到AMS的getContentProvider()。

    Step6:
      在AMS. getContentProvider()调用getContentProviderImpl进一步处理,
    在AMS中:ProviderMap mProviderMap成员变量是保存系统中的ContentProvider信息,boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed先检查XXXContentProvider存在、宿主进程以及没有被杀的情况下,就直接返回。
      若不存在,则会通过AppGlobals.getPackageManage().resolveContentProvider和getApplicationInfo来分别获取XXXProvider应用程序的相关信息,并保存cpi和cpr变量中。接下来会判断mLaunchingProviders(系统中所有正在加载的Content Provider都保到这里面)是不是正在被其它应用程序加载XXXContentProvider,如果B应用进程已开启,但对应的contentprovider未初始化过,则会走proc.thread.scheduleInstallProvider(cpi),到B应用的ActivityThread的handleInstallProvider、installContentProviders初始化contentProvider,略过Step7-13步直接Step14。
      如果B应用进程未启动,则继续走Step7 调用startProcessLocked函数来启动新进程并加装XXXContentProvider,并且把这个正在加载的信息增加到mLaunchingProviders中去,同步等到XXXContentProvider初始化完,while循环cpr.provider判空,然后cpr.wait(),等到Step17步publishContentProviders会dst.notifyAll()过来。

    Step7、8、9、10:
      ActivityManagerService.startProcessLocked、Process.start、ActivityThread.main、ActivityThread.attach、ActivityManagerService.attachApplication 新进程的创建完回到AMS的过程,可参考startService源码从AMS进程到service的新进程启动过程分析

    Step11:
      AMS. attachApplicationLocked, 会对这个B应用进程记录块做一些初始化,并获得需要加装的ContentProvider列表,即包括XXXContentProvider,然后调用从参数传进来的IApplicationThread对象thread(即ApplicationThreadProxy)的bindApplication进入到B应用程序XXXContentProvider进程中的ApplicationThread对象的bindApplication函数中去。

    Step12、13:
      ApplicationThread(ActivityThread 变量). bindApplication通过H(Handler)到ActivityThread主进程中handleBindApplication处理,调用installContentProviders函数来在初始化XXXContent Providers信息,以及Application等初始化工作。

    Step14、15:
      ActivityThread.installContentProviders 先调用installContentProviders对XXXContentProvider的初始化attachInfo,并调用onCreate,并把ContextProvider的成员变量Transport(父类ContentProviderNative,是binder对象,并且实现了IContentProvider接口)然后调用ActivityManagerNative.getDefault().publishContentProviders(getApplicationThread(), results),到Step16.

    Step16:
      ActivityManagerProxy. publishContentProviders(IApplicationThread caller, List<ContentProviderHolder> providers)通知AMS进程 ,B应用进程及XXXContentProvider都已初始化完毕,并把相应的IContentProvider给过去。

    Step17 :
      AMS. publishContentProviders:会把B应用进程的的ContentProvider保存起来,并移除mLaunchingProviders里面相应的值,然后通知dst.notifyAll();到Step6的getContentProviderImpl,接着返回给A应用进程的ContentProviderHolder对象。

    Step18:
      在AMS进程通过binder返回给A应用对象ContentProviderHolder,会把B应用进程的IContentProvider接口(即Transport)通过ContentProviderNative.asInterface(source.readStrongBinder())改造为ContentProviderProxy。

    Step19、20:
      A应用进程拿到B应用进程的Transport后, ContentResolver.query()中调用ContentProviderProxy.query,这里面会BulkCursorToCursorAdaptor,包含CursorWindow等匿名共享内存方式读取数据。

    附上一些关键的源码:

    ContextImpl.java:
    class ContextImpl extends Context {
        private final ApplicationContentResolver mContentResolver;
        @Override
        public ContentResolver getContentResolver() {
            return mContentResolver;
        }
        ...
        private static final class ApplicationContentResolver extends ContentResolver {
            private final ActivityThread mMainThread;
            private final UserHandle mUser;
            ...
            @Override
            protected IContentProvider acquireUnstableProvider(Context c, String auth) {
                return mMainThread.acquireProvider(c,
                        ContentProvider.getAuthorityWithoutUserId(auth),
                        resolveUserIdFromAuthority(auth), false);
            }
           ...
        }
    }
    ContentResolver.java:
    public abstract class ContentResolver {
        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);
            ...
            try {
                qCursor = unstableProvider.query(mPackageName, uri, projection,
                        selection, selectionArgs, sortOrder, remoteCancellationSignal);
            } 
            ...
        }
    }
    ActivityThread.java:
    public final class ActivityThread {
    
        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;
            }
            ...
            IActivityManager.ContentProviderHolder holder = null;
            try {
                holder = ActivityManagerNative.getDefault().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 final IContentProvider acquireExistingProvider(
                Context c, String auth, int userId, boolean stable) {
            synchronized (mProviderMap) {
                final ProviderKey key = new ProviderKey(auth, userId);
                final ProviderClientRecord pr = mProviderMap.get(key);
                ...
                IContentProvider provider = pr.mProvider;
                IBinder jBinder = provider.asBinder();
                ...            
                return provider;
            }
        }
        private void installContentProviders(Context context, List<ProviderInfo> providers) {
            final ArrayList<IActivityManager.ContentProviderHolder> results =
                new ArrayList<IActivityManager.ContentProviderHolder>();
            for (ProviderInfo cpi : providers) {            
                IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
                        false, true , true );
                if (cph != null) {
                    cph.noReleaseNeeded = true;
                    results.add(cph);
                }
            }
            try {
                ActivityManagerNative.getDefault().publishContentProviders(
                    getApplicationThread(), results);
            } catch (RemoteException ex) {
            }
        }
        private IActivityManager.ContentProviderHolder installProvider(Context context,
                IActivityManager.ContentProviderHolder holder, ProviderInfo info,
                boolean noisy, boolean noReleaseNeeded, boolean stable) {
            ContentProvider localProvider = null;
            IContentProvider provider;
            if (holder == null || holder.provider == null) {
                Context c = null;
                ApplicationInfo ai = info.applicationInfo;
                if (context.getPackageName().equals(ai.packageName)) {
                    c = context;
                } else if (mInitialApplication != null &&mInitialApplication.getPackageName().equals(ai.packageName)) {
                    c = mInitialApplication;
                } else {
                    try {
                        c = context.createPackageContext(ai.packageName,
                                Context.CONTEXT_INCLUDE_CODE);
                    } 
                }            
                try {
                    final java.lang.ClassLoader cl = c.getClassLoader();
                    localProvider = (ContentProvider)cl.
                        loadClass(info.name).newInstance();
                    provider = localProvider.getIContentProvider();            
                    localProvider.attachInfo(c, info);
                } catch (java.lang.Exception e) {
                }
            } else {
                provider = holder.provider;
            }
            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 {
                    ...
                }
            }
            return retHolder;
        }
        private void handleBindApplication(AppBindData data) {
            ...
            final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
            ...    
            if (ii != null) {
                final ApplicationInfo instrApp = new ApplicationInfo();
                ii.copyTo(instrApp);
                instrApp.initForUser(UserHandle.myUserId());
                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();
                }
                final ComponentName component = new ComponentName(ii.packageName, ii.name);
                mInstrumentation.init(this, instrContext, appContext, component,
                        data.instrumentationWatcher, data.instrumentationUiAutomationConnection);
            } else {
                mInstrumentation = new Instrumentation();
            }
            ...
            try {
                // If the app is being launched for full backup or restore, bring it up in
                // a restricted environment with the base application class.
                Application app = data.info.makeApplication(data.restrictedBackupMode, null);
                mInitialApplication = app;
                // don't bring up providers in restricted mode; they may depend on the
                // app's custom Application class
                if (!data.restrictedBackupMode) {
                    if (!ArrayUtils.isEmpty(data.providers)) {
                        installContentProviders(app, data.providers);
                    }
                }
                try {
                    mInstrumentation.onCreate(data.instrumentationArgs);
                }
                try {
                    mInstrumentation.callApplicationOnCreate(app);
                } 
            } 
        }
    }
    ActivityManagerNative.java:
    public abstract class ActivityManagerNative extends Binder implements IActivityManager{
        static public IActivityManager asInterface(IBinder obj) {
            if (obj == null) {
                return null;
            }
            IActivityManager in =
                (IActivityManager)obj.queryLocalInterface(descriptor);
            if (in != null) {
                return in;
            }
            return new ActivityManagerProxy(obj);
        }
        static public IActivityManager getDefault() {
            return gDefault.get();
        }
        private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
            protected IActivityManager create() {
                IBinder b = ServiceManager.getService("activity");
                IActivityManager am = asInterface(b);
                return am;
            }
        };
    }
    class ActivityManagerProxy implements IActivityManager{
        public ActivityManagerProxy(IBinder remote) {
            mRemote = remote;
        }
        public ContentProviderHolder getContentProvider(IApplicationThread caller,
                String name, int userId, boolean stable) throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            data.writeInterfaceToken(IActivityManager.descriptor);
            data.writeStrongBinder(caller != null ? caller.asBinder() : null);
            data.writeString(name);
            data.writeInt(userId);
            data.writeInt(stable ? 1 : 0);
            mRemote.transact(GET_CONTENT_PROVIDER_TRANSACTION, data, reply, 0);
            reply.readException();
            int res = reply.readInt();
            ContentProviderHolder cph = null;
            if (res != 0) {
                cph = ContentProviderHolder.CREATOR.createFromParcel(reply);
            }
            data.recycle();
            reply.recycle();
            return cph;
        }
        public void publishContentProviders(IApplicationThread caller,
                List<ContentProviderHolder> providers) throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            data.writeInterfaceToken(IActivityManager.descriptor);
            data.writeStrongBinder(caller != null ? caller.asBinder() : null);
            data.writeTypedList(providers);
            mRemote.transact(PUBLISH_CONTENT_PROVIDERS_TRANSACTION, data, reply, 0);
            reply.readException();
            data.recycle();
            reply.recycle();
        }
    }
    ActivityManagerService.java:
    public final class ActivityManagerService extends ActivityManagerNative
            implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
        @Override
        public final ContentProviderHolder getContentProvider(
                IApplicationThread caller, String name, int userId, boolean stable) {
            ...
            // The incoming user check is now handled in checkContentProviderPermissionLocked() to deal
            // with cross-user grant.
            return getContentProviderImpl(caller, name, null, stable, userId);
        }
        private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
                String name, IBinder token, boolean stable, int userId) {
            ContentProviderRecord cpr;
            ContentProviderConnection conn = null;
            ProviderInfo cpi = null;
    
            synchronized(this) {
                long startTime = SystemClock.uptimeMillis();
    
                ProcessRecord r = null;
                ...
                // First check if this content provider has been published...
                cpr = mProviderMap.getProviderByName(name, userId);
                ...
                boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed;
                if (providerRunning) {
                    cpi = cpr.info;
                    ...
                    if (r != null && cpr.canRunHere(r)) {
                        // This provider has been published or is in the process
                        // of being published...  but it is also allowed to run
                        // in the caller's process, so don't make a connection
                        // and just let the caller instantiate its own instance.
                        ContentProviderHolder holder = cpr.newHolder(null);
                        // don't give caller the provider object, it needs
                        // to make its own.
                        holder.provider = null;
                        return holder;
                    }
                    ...
                }
    
                if (!providerRunning) {
                    try {
                        cpi = AppGlobals.getPackageManager().
                            resolveContentProvider(name,
                                STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
                    } catch (RemoteException ex) {
                    }
                    ...
                        try {
                            ApplicationInfo ai =
                                AppGlobals.getPackageManager().
                                    getApplicationInfo(
                                            cpi.applicationInfo.packageName,
                                            STOCK_PM_FLAGS, userId);                        
                            ai = getAppInfoForUser(ai, userId);
                            cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
                        } catch (RemoteException ex) {
                           ...
                        }
                   ...
                    final int N = mLaunchingProviders.size();
                    int i;
                    for (i = 0; i < N; i++) {
                        if (mLaunchingProviders.get(i) == cpr) {
                            break;
                        }
                    }
                    // If the provider is not already being launched, then get it
                    // started.
                    if (i >= N) {
                        ...
                        try {
                            ...
                            ProcessRecord proc = getProcessRecordLocked(
                                    cpi.processName, cpr.appInfo.uid, false);
                            if (proc != null && proc.thread != null && !proc.killed) {
                                if (!proc.pubProviders.containsKey(cpi.name)) {
                                    checkTime(startTime, "getContentProviderImpl: scheduling install");
                                    proc.pubProviders.put(cpi.name, cpr);
                                    try {
                                        proc.thread.scheduleInstallProvider(cpi);
                                    } catch (RemoteException e) {
                                    }
                                }
                            } else {
                                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);
                        } finally {
                            Binder.restoreCallingIdentity(origId);
                        }
                    }
                    ...
                    mProviderMap.putProviderByName(name, cpr);
                    conn = incProviderCountLocked(r, cpr, token, stable);
                    if (conn != null) {
                        conn.waiting = true;
                    }
                }
            }
            // Wait for the provider to be published...
            synchronized (cpr) {
                while (cpr.provider == null) {
                   ...
                    try {
                        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;
        }
        
        @Override
        public final void attachApplication(IApplicationThread thread) {
            synchronized (this) {
                int callingPid = Binder.getCallingPid();
                final long origId = Binder.clearCallingIdentity();
                attachApplicationLocked(thread, callingPid);
                Binder.restoreCallingIdentity(origId);
            }
        }
        private final boolean attachApplicationLocked(IApplicationThread thread,
                int pid) {
            ...
                ProfilerInfo profilerInfo = profileFile == null ? null
                        : new ProfilerInfo(profileFile, profileFd, samplingInterval, profileAutoStop);
                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());
            ...
            return true;
        }
    }
    IActivityManager.java:
    public interface IActivityManager extends IInterface {
        public static class ContentProviderHolder implements Parcelable {
            public final ProviderInfo info;
            public IContentProvider provider;
            public IBinder connection;
            public boolean noReleaseNeeded;
            ...
            public static final Parcelable.Creator<ContentProviderHolder> CREATOR
                    = new Parcelable.Creator<ContentProviderHolder>() {
                @Override
                public ContentProviderHolder createFromParcel(Parcel source) {
                    return new ContentProviderHolder(source);
                }
    
                @Override
                public ContentProviderHolder[] newArray(int size) {
                    return new ContentProviderHolder[size];
                }
            };
            private ContentProviderHolder(Parcel source) {
                info = ProviderInfo.CREATOR.createFromParcel(source);
                provider = ContentProviderNative.asInterface(
                        source.readStrongBinder());
                connection = source.readStrongBinder();
                noReleaseNeeded = source.readInt() != 0;
            }
        }
    }
    

    相关文章

      网友评论

      • cztfreeww:写的非常简明,很好理解。
        请问作者两个时序图是不是一样的?第二个不应该接着第一个吗?
      • ogeng:请问 当手机开机后,没有启动B应用前,通过A应用中的getContentResolver调用B的数据,但是cursor却等于null,B应用里面是有数据的
        ogeng:@umbrella1 B应用中的日志 都没有显示,方便加下你qq吗。我Q:570277075
        umbrella1:@ogeng 可以在B应用的provider的query打log,看下有cursor返回
      • Persistence软装设计:简单明了,挺好

      本文标题:分分钟钟看懂ContentProvider启动源码流程

      本文链接:https://www.haomeiwen.com/subject/xfuxhxtx.html