Android 源码分析 (十一) ContentProvider 启动

ContentProvider (内容提供者) 属于四大组件之一,可以说它是在四大组件中开发者使用率最少的一个,它的作用就是进程间进行数据交互,底层采用 Binder 机制进行进程间通信。

下面我们就以分析 ContentProvider 工作流程为主来进行全面分析。

源码分析

query 到 AMS 调用过程

下面先来看一个代码示例,代码如下:

    fun getContactsLists(): MutableList<String> {
        var contactsLists: MutableList<String> = mutableListOf<String>()
        lateinit var cursor: Cursor
        try {
        //使用 getContentResolver() 查询联系人列表
      	cursor = contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null)
        //对 cursor 进行遍历
        if (cursor != null) {
            while (cursor.moveToNext()) {
     
        //电话号码
            val number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))
            contactsLists.add("电话:$number")
            }
        }
	cursor.close()
        } catch (error: Exception) {
            logger.error("error:$error")
        }
        return contactsLists
    }

	//测试
    val contactsLists = getContactsLists()
    contactsLists.forEach { it -> println("通过ContentResolver获取联系人: $contactsLists") }

那么这一流程内部是怎么运行的,下面进入代码分析。老规矩,还是看一下该小节分析流程,这里以时序图为主

 通过上面代码示例想要 query 数据先要拿到 contentResolver 对象,通过父类 getContentResolver()方法获得,代码如下:

    @Override
    public ContentResolver getContentResolver() {
        return mBase.getContentResolver();
    }

这里的 mBase 是 Context 对象,它的实现类就是 ContextImpl 我们直接看它具体实现,代码如下:

//ContextImpl.java
    @Override
    public ContentResolver getContentResolver() {
        return mContentResolver;
    }

这里的 getContentResolver 方法中返回了 ApplicationContentResolver 对象,它是 ContextImpl 的静态内部类,继承自 ContentResolver ,它在 ContextImpl 的构造方法中被创建,就会启动 ContentProvider,这里以上面我们示例 query 来进行分析,我们看它的具体实现,

//ContentResolver.java
    public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
            @Nullable String[] projection, @Nullable Bundle queryArgs,
            @Nullable CancellationSignal cancellationSignal) {
        Preconditions.checkNotNull(uri, "uri");
        1. 拿到 IContentProvider 对象,它是 ContentProvider 的本地代理
        IContentProvider unstableProvider = acquireUnstableProvider(uri);
        if (unstableProvider == null) {
            return null;
        }
        IContentProvider stableProvider = null;
        Cursor qCursor = null;
        try {
            
            try {
               2. 调用 IContentProvider 的 query 函数来进行 query
                qCursor = unstableProvider.query(mPackageName, uri, projection,
                        queryArgs, remoteCancellationSignal);
            } catch (DeadObjectException e) {
                

            //...
            return wrapper;
        } catch (RemoteException e) {
            return null;
        } finally {
            ...
        }
    }

通过 acquireUnstableProvider 方法拿到 ContentProvider 的本地代理对象,我们先来看下注释 1 的  acquireUnstableProvider 方法怎么拿到 ContentProvider 本地代理对象,代码如下:

   //ContentResolver.java 
   public final IContentProvider acquireUnstableProvider(Uri uri) {
        if (!SCHEME_CONTENT.equals(uri.getScheme())) {
            return null;
        }
        String auth = uri.getAuthority();
        if (auth != null) {
          	//调用内部抽象方法
            return acquireUnstableProvider(mContext, uri.getAuthority());
        }
        return null;
    }

    /** @hide */
    protected abstract IContentProvider acquireUnstableProvider(Context c, String name);
//ContextImpl.java
        @Override
        protected IContentProvider acquireUnstableProvider(Context c, String auth) {
            return mMainThread.acquireProvider(c,
                    ContentProvider.getAuthorityWithoutUserId(auth),
                    resolveUserIdFromAuthority(auth), false);
        }

return 返回的对象,首先拿到 mMainThread 对象,然后调用它内部的 acquireProvider 方法

//ActivityThread.java

    public final IContentProvider acquireProvider(
            Context c, String auth, int userId, boolean stable) {
        1. acquireExistingProvider 方法主要检查 ActivityThread 全局变量 mProviderMap 中是否有目标 ContentProvider 存在,有就返回,没有就通过注释 2 处获取,
        final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
        if (provider != null) {
            return provider;
        }


        ContentProviderHolder holder = null;
        try {
            2. 调用 IAcitivityManager 获取 ContentProviderHolder 对象
            holder = ActivityManager.getService().getContentProvider(
                    getApplicationThread(), auth, userId, stable);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
      
			...

        3. 用来安装 ContentProvider
        holder = installProvider(c, holder, holder.info,
                true /*noisy*/, holder.noReleaseNeeded, stable);
        return holder.provider;
    }

总结

1 处的 acquireExistingProvider 方法内部会检查 ActivityThread 的全局变量 mProviderMap 中是否有 ContentProvider 存在

2 处 的 IActivityManager 的 getContentProvider 方法与 AMS 进行通信来获取

 3 是安装 ContentProvider,并将 ContentProvider 相关数据存储在 mProviderMap 中,起到缓存作用

我们现在来看下AMS 的 getContentProvider 方法具体实现,代码如下:

//AMS.java
    @Override
    public final ContentProviderHolder getContentProvider(
            IApplicationThread caller, String name, int userId, boolean stable) {
        enforceNotIsolatedCaller("getContentProvider");
       ...
        //调动内部 getContentProviderImpl 方法
        return getContentProviderImpl(caller, name, null, stable, userId);
    }
//AMS.java


    private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
            String name, IBinder token, boolean stable, int userId) {
        ContentProviderRecord cpr;
        ContentProviderConnection conn = null;
        ProviderInfo cpi = null;

        ..1. 获取目标 ContentProvider  应用程序进程的信息,如果进程已经启动就调用注释 2 ,否则调用注释 3 
                        ProcessRecord proc = getProcessRecordLocked(
                                cpi.processName, cpr.appInfo.uid, false);
                        if (proc != null && proc.thread != null && !proc.killed) {
                            if (DEBUG_PROVIDER) Slog.d(TAG_PROVIDER,
                                    "Installing in existing process " + proc);
                            if (!proc.pubProviders.containsKey(cpi.name)) {
                                checkTime(startTime, "getContentProviderImpl: scheduling install");
                                proc.pubProviders.put(cpi.name, cpr);
                                try {
                                 2. 调用 IApplicationThreadscheduleInstallProvider 函数
                                    proc.thread.scheduleInstallProvider(cpi);
                                } catch (RemoteException e) {
                                }
                            }
                        } else {
                            checkTime(startTime, "getContentProviderImpl: before start process");
                            3. 启动新进程
                            proc = startProcessLocked(cpi.processName,
                                    cpr.appInfo, false, 0, "content provider",
                                    new ComponentName(cpi.applicationInfo.packageName,
                                            cpi.name), false, false, false);
                            checkTime(startTime, "getContentProviderImpl: after start process");
                            if (proc == null) {
                                Slog.w(TAG, "Unable to launch app "
                                        + cpi.applicationInfo.packageName + "/"
                                        + cpi.applicationInfo.uid + " for provider "
                                        + name + ": process is bad");
                                return null;
                            }
                        }
                        cpr.launchingApp = proc;
                        mLaunchingProviders.add(cpr);
                    } finally {
                        Binder.restoreCallingIdentity(origId);
                    }
                }

              ....
    }

2 位置执行流程,首先调用 ActivityThread 的内部类 IApplication 的scheduleInstallProvider 函数,然后通过 H sendMessage 通知进行安装 ContentProvider

看3位置处我们直接看 ActivityThread main 函数,代码如下

//AMS.java


    final ProcessRecord startProcessLocked(String processName,
            ApplicationInfo info, boolean knownToBeDead, int intentFlags,
            String hostingType, ComponentName hostingName, boolean allowWhileBooting,
            boolean isolated, boolean keepIfLarge) {
        return startProcessLocked(processName, info, knownToBeDead, intentFlags, hostingType,
                hostingName, allowWhileBooting, isolated, 0 /* isolatedUid */, keepIfLarge,
                null /* ABI override */, null /* entryPoint */, null /* entryPointArgs */,
                null /* crashHandler */);
    }

我们直接看 ActivityThread main 函数,代码如下

//ActivityThread.java


    //通过反射调用执行的
    public static void main(String[] args) {
				....
        //主线程消息循环
        Looper.prepareMainLooper();
        //创建 ActivityThread 对象
        ActivityThread thread = new ActivityThread();
        //Application,Activity 入口
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

主要看 ActivityThread attach 函数具体实现,代码如下:

//ActivityThread.java
    private void attach(boolean system) {
     ...
     
     final IActivityManager mgr = ActivityManager.getService();
     try {
          关联 Application
          mgr.attachApplication(mAppThread);
     } catch (RemoteException ex) {
      throw ex.rethrowFromSystemServer();
     }
     ...
      
    }

从上得到 AMS 的代理类 IActivityManager ,在注释 调用 AMS 的 attachApplication 函数,并将 IApplicationThread 对象传入 AMS 保持应用进程和 AMS 跨进程通信,应用程序调用 AMS 的过程就分析完了,下面我们分析 AMS 到应用程序进程的 ContentProvider 安装过程

AMS 启动 ContentProvider 的过程

先看流程图

 

//AMS.java

private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid) {
  ...
  
   thread.bindApplication(processName, appInfo, providers,
                        app.instr.mClass,
                        profilerInfo, app.instr.mArguments,
                        app.instr.mWatcher,
                        app.instr.mUiAutomationConnection, testMode,
                        mBinderTransactionTrackingEnabled, enableTrackAllocation,
                        isRestrictedBackupMode || !normalMode, app.persistent,
                        new Configuration(getGlobalConfiguration()), app.compat,
                        getCommonServicesLocked(app.isolated),
                        mCoreSettingsObserver.getCoreSettingsLocked(),
                        buildSerial);
    
    
  ...
}

在 attachApplicationLocked 函数中调用了 thread.bindApplication 方法,thread 是 IApplicationThread ,这里和 IActivityManager 一样采用了 aidl 进行进程间传输数据,我们回到 ActivityThread 内部类 ApplicationThread 的 bindApplication 方法

//ActivityThread.java

        public final void bindApplication(String processName, ApplicationInfo appInfo,
                List<ProviderInfo> providers, 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,
                String buildSerial) {

            if (services != null) {
                // Setup the service cache in the ServiceManager
                ServiceManager.initServiceCache(services);
            }

            setCoreSettings(coreSettings);

            AppBindData data = new AppBindData();
            data.processName = processName;
            data.appInfo = appInfo;
            data.providers = providers;
            data.instrumentationName = instrumentationName;
            data.instrumentationArgs = instrumentationArgs;
            data.instrumentationWatcher = instrumentationWatcher;
            data.instrumentationUiAutomationConnection = instrumentationUiConnection;
            data.debugMode = debugMode;
            data.enableBinderTracking = enableBinderTracking;
            data.trackAllocation = trackAllocation;
            data.restrictedBackupMode = isRestrictedBackupMode;
            data.persistent = persistent;
            data.config = config;
            data.compatInfo = compatInfo;
            data.initProfilerInfo = profilerInfo;
            data.buildSerial = buildSerial;
          	//发送消息给 H 类
            sendMessage(H.BIND_APPLICATION, data);
        }

ActivityThread 内部类 H 收到消息,开始处理 BIND_APPLICSTION消息,代码如下:

//ActivityThread.java
...
  public void handleMessage(Message msg) {
  ...
   case BIND_APPLICATION:
    AppBindData data = (AppBindData)msg.obj;
    handleBindApplication(data);
                    
    break;    
  ...
}
...
//ActivityThread.java
...
  public void handleMessage(Message msg) {
  ...
   case BIND_APPLICATION:
    AppBindData data = (AppBindData)msg.obj;
    handleBindApplication(data);
                    
    break;    
  ...
}
...
//ActivityThread.java

private void handleBindApplication(AppBindData data) {
  ...
    
        1. 创建 ContentImpl
        final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
  
  ...
    
            2 通过反射创建 Instrumentation 
                final ClassLoader cl = instrContext.getClassLoader();
                mInstrumentation = (Instrumentation)
                    cl.loadClass(data.instrumentationName.getClassName()).newInstance();
            } catch (Exception e) {
               ...
            }

            final ComponentName component = new ComponentName(ii.packageName, ii.name);
            3 处初始化 Instrumentaion
            mInstrumentation.init(this, instrContext, appContext, component,
                    data.instrumentationWatcher, data.instrumentationUiAutomationConnection);

....
  
   try {

            创建 Application
            Application app = data.info.makeApplication(data.restrictedBackupMode, null);
            mInitialApplication = app;


            if (!data.restrictedBackupMode) {
                if (!ArrayUtils.isEmpty(data.providers)) {
                    5 ContentProvider 启动的
                    installContentProviders(app, data.providers);
                    mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
                }
            }


            try {
                mInstrumentation.onCreate(data.instrumentationArgs);
            }
            catch (Exception e) {
          ...
            }

            try {
                6  调用 Application onCreate 生命周期方法
                mInstrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
               ...
            }
}

注释 5 看 ContentProvider 是如何启动的,代码如下:

//ActivityThread.java

    private void installContentProviders(
            Context context, List<ProviderInfo> providers) {
        final ArrayList<ContentProviderHolder> results = new ArrayList<>();

      1 遍历当前应用程序进程的 ProviderInfo 列表,得到每个 ContentProvicer 的存储信息
        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());
            }
            2 调用 installProvider 方法来启动这些 ContentProvider
            ContentProviderHolder cph = installProvider(context, null, cpi,
                    false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
            if (cph != null) {
                cph.noReleaseNeeded = true;
                results.add(cph);
            }
        }

        try {
            3 处是将启动了的 ContentProvider 存入 AMS 的 mProviderMap 中 就是用来缓存启动过的 ContentProvidec
            ActivityManager.getService().publishContentProviders(
                getApplicationThread(), results);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }

现在来看调用 installProvider 方法来启动这些 ContentProvider

//ActivityThread.java

 private ContentProviderHolder installProvider(Context context,
            ContentProviderHolder holder, ProviderInfo info,
            boolean noisy, boolean noReleaseNeeded, boolean stable) {
   
   
   ....
       try {
                final java.lang.ClassLoader cl = c.getClassLoader();
                1 通过反射实例化 ContentProvider 对象
                localProvider = (ContentProvider)cl.
                    loadClass(info.name).newInstance();
                provider = localProvider.getIContentProvider();
         
                ...
               
                2处调用它的 attachInfo 方法
                localProvider.attachInfo(c, info);
            } catch (java.lang.Exception e) {
                if (!mInstrumentation.onException(null, e)) {
                    throw new RuntimeException(
                            "Unable to get provider " + info.name
                            + ": " + e.toString(), e);
                }
                return null;
            }
   
   ....
     
 }

在注释 2 处调用它的 attachInfo 方法,代码如下

//ContentProvider.java
    public void attachInfo(Context context, ProviderInfo info) {
        attachInfo(context, info, false);
    }

    private void attachInfo(Context context, ProviderInfo info, boolean testing) {
        mNoPerms = testing;


        if (mContext == null) {
            mContext = context;
            if (context != null) {
                mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(
                        Context.APP_OPS_SERVICE);
            }
            mMyUid = Process.myUid();
            if (info != null) {
                setReadPermission(info.readPermission);
                setWritePermission(info.writePermission);
                setPathPermissions(info.pathPermissions);
                mExported = info.exported;
                mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0;
                setAuthorities(info.authority);
            }
            /**
             * 安装成功,调用生命周期函数 onCreate
             */
            ContentProvider.this.onCreate();
        }
    }

    public abstract boolean onCreate();

可以看到最后在 ContentProvider 的 attachInfo 函数中进行调用了抽象方法 onCreate, 那么它的子类就会进行实现 onCreate 达到启动成功的通知。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值