Android 存储选项之 ContentProvider 启动存在的暗坑_第1张图片 闪存
Android 存储优化系列专题
  • SharedPreferences 系列

《Android 之不要滥用 SharedPreferences》
《Android 之不要滥用 SharedPreferences(2)— 数据丢失》

  • ContentProvider 系列(待更)

《Android 存储选项之 ContentProvider 的启动性能》
《Android 存储选项之 ContentProvider 深入分析》

  • 对象序列化系列

《Android 对象序列化之你不知道的 Serializable》
《Android 对象序列化之 Parcelable 取代 Serializable ?》
《Android 对象序列化之追求性能完美的 Serial》

  • 数据序列化系列(待更)

《Android 数据序列化之 JSON》
《Android 数据序列化之 Protocol Buffer 源码分析》

  • SQLite 存储系列

《Android 存储选项之 SQLiteDatabase 创建过程源码分析》
《Android 存储选项之 SQLiteDatabase 源码分析》
《数据库连接池 SQLiteConnectionPool 源码分析》
《SQLiteDatabase 启用事务源码分析》
《SQLite 数据库 WAL 模式工作原理简介》
《SQLite 数据库锁机制与事务简介》
《SQLite 数据库优化那些事儿》


前言

在 SharedPreferences 系列《Android 之不要滥用 SharedPreferences》和 《Android 之不要滥用 SharedPreferences(2)— 数据丢失》两篇文章中,详细分析了 SharedPreferences 的实现机制。简单回顾下,Android 系统为什么不把 SharedPreferences 设计成跨进程安全的呢?那是因为 Android 系统更希望我们在这个场景使用 ContentProvider 作为存储方式。

ContentProvider 作为 Android 四大组件之一,为应用开发者提供了不同进程甚至是不同应用程序之间共享数据的机制。

今天我们主要来聊聊 ContentProvider 这个存储方法。


ContentProvider 的使用

Android 系统中比如相册、日历、音频、视频、通讯录等模块都提供了 ContentProvider 的访问支持。它的使用非常简单,你可以参考官方文档。

但是,ContentProvider 在使用过程中也存在一些“暗坑”需要我们特别注意。

  • 启动性能

ContentProvider 的生命周期默认在 Application onCreate() 之前,而且都是在主线程创建的。我们自定义的 ContentProvider 类的构造函数、静态代码块、onCreate 函数都尽量不要做耗时的操作,会拖慢启动速度。

Android 存储选项之 ContentProvider 启动存在的暗坑_第2张图片 ContentProvider 启动流程
  • 稳定性

ContentProvider 在进行跨进程数据传递时,利用了 Android 的 Binder 和匿名共享内存机制。简单来说,就是通过 Binder 传递 CursorWindow 对象内部的匿名共享内存的文件描述符。这样在跨进程传输中,结果数据并不需要跨进程传输,而是在不同进程中通过传输的匿名共享内存文件描述符来操作同一块匿名内存,这样来实现不同进程访问相同数据的目的

Android 存储选项之 ContentProvider 启动存在的暗坑_第3张图片 CursorWindow

基于 mmap 的匿名共享内存机制也是有代价的。当传输的数据量非常小的时候,可能不一定划算。所以 ContentProvider 提供了一种 call 函数,它会直接通过 Binder 来传输数据。

Android 的 Binder 传输是有大小限制的,一般来说限制是 1 ~ 2MB。ContentProvider 的接口调用参数和 call 函数调用并没有使用匿名共享机制,比如要批量插入很多数据,那么就会出现一个插入数据的数组,如果这个数组太大了,那么这个操作就可能出现数据超大异常。

  • 安全性

虽然 ContentProvider 为应用程序之间的数据共享提供了很好的安全机制,但是如果 ContentProvider 是 exported,当支持执行 SQL 语句时就需要注意 SQL 注入的问题。另外如果我们传入的参数是一个文件路径,然后返回文件内容,这个时候也要校验合法性,不然整个应用的私有数据都有可能被别人拿到,在 Intent 传递参数的时候可能会经常会犯这个错误。

今天我们先来聊聊 ContentProvider 的启动性能,稳定性和安全性放到系列后面文章进行介绍。还是从源码的角度出发,分析自定义 ContentProvider 的创建过程和生命周期回调过程。


自定义 ContentProvider 的启动过程分析

我们要从应用程序启动类 ActivityThread 开始,关于 ActivityThread 大家肯定不会感到陌生,它是我们应用进程的入口类,也就是 main 函数所在类。

 public static void main(String[] args) {    //省略部分    //主线程Looper创建    Looper.prepareMainLooper();    //创建ActivityThread对象    ActivityThread thread = new ActivityThread();    //调用自己的attach()方法    thread.attach(false, startSeq);    if (sMainThreadHandler == null) {        sMainThreadHandler = thread.getHandler();    }    //省略        //开启出队    Looper.loop();    //如果调用了主线程Looper的quit() 抛异常在这里    throw new RuntimeException("Main thread loop unexpectedly exited");}

在 main 方法中创建了 ActivityThread 对象,并调用它的 attach 方法,然后在 attach 方法中通过 AMS(ActivityManagerService)远程调用了 attachApplicationLocked 方法,在该方法中完成自定义 ContentProvider 的收集工作,先来看下 ActivityThread 的 attach 方法。

 private void attach(boolean system, long startSeq) {    sCurrentActivityThread = this;    mSystemThread = system;    if (!system) {            ... 省略                 //我们重点关注这里        //这里得到实例ActivityManagerService代理类Proxy        final IActivityManager mgr = ActivityManager.getService();        try {            //然后通过代理类完成远程(跨进程)调用            //mAppThread是当前进程的ApplicationThread实例           //在完成跨进程条用完成之后要通过该对象回到当前进程            mgr.attachApplication(mAppThread, startSeq);        } catch (RemoteException ex) {            throw ex.rethrowFromSystemServer();        }}

通过 ActivityManager 获得 ActivityManagerService 的代理对象,完成跨进程调用,由于 Binder 进程间通信不是我们今天要分析的内容,这里通过一张流程图简单说下该过程。

Android 存储选项之 ContentProvider 启动存在的暗坑_第4张图片 ActivityManagerService
ActivityManagerSerivce 的 attachApplication 方法分析

前面有简单提到通过 AMS 远程调用该方法,在该方法内开始收集注册在 AndroidManifest.xml 中的 ContentProvider 信息,实际上该方法主要完成两部分工作:

  1. generateApplicationProvidersLocked 方法,通过 PMS 完成 ContentProvider 注册信息(ProviderInfo)的收集工作。
  2. 将收集 Providers 的信息集合,作为参数远程调用 ApplicationThread 的 bindApplication 方法。此时将重新回到应用进程启动类 ActivityThread 中。

在 attachApplication 方法中调用了 attachApplicationLocked ,我们直接看下该方法:

private final boolean attachApplicationLocked(IApplicationThread thread,        int pid, int callingUid, long startSeq) {   // ...省略       ProcessRecord app;    long startTime = SystemClock.uptimeMillis();    if (pid != MY_PID && pid >= 0) {        synchronized (mPidsSelfLocked) {            app = mPidsSelfLocked.get(pid);        }    } else {        app = null;    }    // ...省略    //通过PMS查找应用在Manifest中注册的ContentProvider    List providers = normalMode ? generateApplicationProvidersLocked(app) : null;    if (providers != null && checkAppInLaunchingProvidersLocked(app)) {        //这里启动Provider的超时机制 10s钟        Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);        msg.obj = app;        //通过发送延迟消息        mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);    }   //...省略           //调用ApplicationThread的bindApplication(),         //重新回到启动类 ActivityThread 中        if (app.isolatedEntryPoint != null) {            // This is an isolated process which should just call an entry point instead of            // being bound to an application.            thread.runIsolatedEntryPoint(app.isolatedEntryPoint, app.isolatedEntryPointArgs);        } else if (app.instr != null) {            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, isAutofillCompatEnabled);        } else {            thread.bindApplication(processName, appInfo, providers, null, profilerInfo,                    null, null, null, testMode,                    mBinderTransactionTrackingEnabled, enableTrackAllocation,                    isRestrictedBackupMode || !normalMode, app.persistent,                    new Configuration(getGlobalConfiguration()), app.compat,                    getCommonServicesLocked(app.isolated),                    mCoreSettingsObserver.getCoreSettingsLocked(),                    buildSerial, isAutofillCompatEnabled);        }    } catch (Exception e) {        //...省略        return false;    }   // ...省略    return true;}

先来跟踪第一部分内容,generateApplicationProviderslocked 方法开始收集 ContentProvider 的注册信息。

private final List generateApplicationProvidersLocked(ProcessRecord app) {    List providers = null;    try {        //通过PackageManager获取 provider 注册信息        //这里通信过程与ActivityManager类似        providers = AppGlobals.getPackageManager()                .queryContentProviders(app.processName, app.uid,                        STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS                                | MATCH_DEBUG_TRIAGED_MISSING, /*metadastaKey=*/ null)                .getList();    } catch (RemoteException ex) {}    // 省略    return providers;}

上述方法中 AppGlobals 的 getPackageManager 方法实际返回的是 PackageManagerService,然后调用它的 queryContentProviders 方法:

public @NonNull ParceledListSlice queryContentProviders(String processName,        int uid, int flags, String metaDataKey) {    //...省略    ArrayList finalList = null;    // reader    synchronized (mPackages) {        //遍历mProviders        //第一个mProviders是ProviderIntentResolver类型        //第二个mProviders是一个ArrayMap        final Iterator i = mProviders.mProviders.values().iterator();        while (i.hasNext()) {            //PackageParser.Provider持有ProviderInfo            final PackageParser.Provider p = i.next();            PackageSetting ps = mSettings.mPackages.get(p.owner.packageName);            if (ps != null && p.info.authority != null                    && (processName == null                            || (p.info.processName.equals(processName)                                    && UserHandle.isSameApp(p.info.applicationInfo.uid, uid)))                    && mSettings.isEnabledAndMatchLPr(p.info, flags, userId)) {                // See PM.queryContentProviders()'s javadoc for why we have the metaData                // parameter.                if (metaDataKey != null                        && (p.metaData == null || !p.metaData.containsKey(metaDataKey))) {                    continue;                }                final ComponentName component =                        new ComponentName(p.info.packageName, p.info.name);                if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) {                    continue;                }                if (finalList == null) {                    finalList = new ArrayList(3);                }                //ProviderInfo表示一个ContentProvider相关信息,包括其包名,类名等。                //它继承自ComponentInfo并实现了Parcalable                ProviderInfo info = PackageParser.generateProviderInfo(p, flags,                        ps.readUserState(userId), userId);                if (info != null) {                    finalList.add(info);                }            }        }    }    if (finalList != null) {        Collections.sort(finalList, mProviderInitOrderSorter);        return new ParceledListSlice(finalList);    }    return ParceledListSlice.emptyList();}

该方法的主要工作是遍历 ProviderIntentResolver 中 mProviders (Map)容器,该容器保存的是 PackageParse.Provider:

 private final ArrayMap mProviders            = new ArrayMap();

每个 PackageParse.Provider 持有一个 ProviderInfo,ProviderInfo 代表一个 ContentProvider 相关信息。所以此时我们需要知道 mProviders 是在哪里被添加数据的?

经过查找发现在 commitPackageSettings 方法中添加相关数据

private void commitPackageSettings(PackageParser.Package pkg,                                   @Nullable PackageParser.Package oldPkg, PackageSetting pkgSetting, UserHandle user,                                   final @ScanFlags int scanFlags, boolean chatty) {    //...省略    synchronized (mPackages) {        // We don't expect installation to fail beyond this point        // Add the new setting to mSettings        mSettings.insertPackageSettingLPw(pkgSetting, pkg);        // Add the new setting to mPackages        mPackages.put(pkg.applicationInfo.packageName, pkg);        // Make sure we don't accidentally delete its data.        final Iterator iter = mSettings.mPackagesToBeCleaned.iterator();        while (iter.hasNext()) {            PackageCleanItem item = iter.next();            if (pkgName.equals(item.packageName)) {                iter.remove();            }        }        // Add the package's KeySets to the global KeySetManagerService        KeySetManagerService ksms = mSettings.mKeySetManagerService;        ksms.addScannedPackageLPw(pkg);        /**存储ContentProvider信息*/        int N = pkg.providers.size();        StringBuilder r = null;        int i;        for (i = 0; i < N; i++) {            PackageParser.Provider p = pkg.providers.get(i);            p.info.processName = fixProcessName(pkg.applicationInfo.processName,                    p.info.processName);            //在queryContentProviders()方法遍历的便是该Providers中Map容器            mProviders.addProvider(p);            p.syncable = p.info.isSyncable;            if (p.info.authority != null) {                String names[] = p.info.authority.split(";");                p.info.authority = null;                for (int j = 0; j < names.length; j++) {                    if (j == 1 && p.syncable) {                        p = new PackageParser.Provider(p);                        p.syncable = false;                    }                    if (!mProvidersByAuthority.containsKey(names[j])) {                        mProvidersByAuthority.put(names[j], p);                        if (p.info.authority == null) {                            p.info.authority = names[j];                        } else {                            p.info.authority = p.info.authority + ";" + names[j];                        }                        if (DEBUG_PACKAGE_SCANNING) {                            if (chatty)                                Log.d(TAG, "Registered content provider: " + names[j]                                        + ", className = " + p.info.name + ", isSyncable = "                                        + p.info.isSyncable);                        }                    } else {                        PackageParser.Provider other = mProvidersByAuthority.get(names[j]);                        Slog.w(TAG, "Skipping provider name " + names[j] +                                " (in package " + pkg.applicationInfo.packageName +                                "): name already used by "                                + ((other != null && other.getComponentName() != null)                                ? other.getComponentName().getPackageName() : "?"));                    }                }            }            if (chatty) {                if (r == null) {                    r = new StringBuilder(256);                } else {                    r.append(' ');                }                r.append(p.info.name);            }        }        if (r != null) {            if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Providers: " + r);        }        /**存储Service信息*/        N = pkg.services.size();        r = null;        for (i = 0; i < N; i++) {            PackageParser.Service s = pkg.services.get(i);            s.info.processName = fixProcessName(pkg.applicationInfo.processName,                    s.info.processName);            mServices.addService(s);            if (chatty) {                if (r == null) {                    r = new StringBuilder(256);                } else {                    r.append(' ');                }                r.append(s.info.name);            }        }        if (r != null) {            if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Services: " + r);        }        /**存储BrodcastReceiver信息*/        N = pkg.receivers.size();        r = null;        for (i = 0; i < N; i++) {            PackageParser.Activity a = pkg.receivers.get(i);            a.info.processName = fixProcessName(pkg.applicationInfo.processName,                    a.info.processName);            mReceivers.addActivity(a, "receiver");            if (chatty) {                if (r == null) {                    r = new StringBuilder(256);                } else {                    r.append(' ');                }                r.append(a.info.name);            }        }        if (r != null) {            if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Receivers: " + r);        }        /**存储Activity信息*/        N = pkg.activities.size();        r = null;        for (i = 0; i < N; i++) {            PackageParser.Activity a = pkg.activities.get(i);            a.info.processName = fixProcessName(pkg.applicationInfo.processName,                    a.info.processName);            mActivities.addActivity(a, "activity");            if (chatty) {                if (r == null) {                    r = new StringBuilder(256);                } else {                    r.append(' ');                }                r.append(a.info.name);            }        }             // ...省略    }    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}

从源码中可以看出,commitPackageSettings 方法中不仅包含 ContentProvider 信息,还包括 Service、BroadcastReceiver、Activity 信息。这些信息都是解析 AndroidManifest.xml 文件得到。辅助 PMS 完成解析工作任务的是 PackageParser,完成这一过程的具体方法如下:

private boolean parseBaseApplication(Package owner, Resources res,        XmlResourceParser parser, int flags, String[] outError)    throws XmlPullParserException, IOException {...省略 //解析Manifest.xml while ((type = parser.next()) != XmlPullParser.END_DOCUMENT            && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {        if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {            continue;        }        //获取xml节点名称        String tagName = parser.getName();        //解析到 activity 节点        if (tagName.equals("activity")) {            Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs, false,                    owner.baseHardwareAccelerated);            if (a == null) {                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;                return false;            }            owner.activities.add(a);         //解析到广播 receiver 节点        } else if (tagName.equals("receiver")) {            Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs,                    true, false);            if (a == null) {                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;                return false;            }            owner.receivers.add(a);          //解析到服务 service 节点        } else if (tagName.equals("service")) {            Service s = parseService(owner, res, parser, flags, outError, cachedArgs);            if (s == null) {                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;                return false;            }            owner.services.add(s);          //解析到ContentProvider 节点        } else if (tagName.equals("provider")) {            Provider p = parseProvider(owner, res, parser, flags, outError, cachedArgs);            if (p == null) {                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;                return false;            }            owner.providers.add(p);        } else {            if (!RIGID_PARSER) {                Slog.w(TAG, "Unknown element under : " + tagName                        + " at " + mArchiveSourcePath + " "                        + parser.getPositionDescription());                XmlUtils.skipCurrentTag(parser);                continue;              } else {                outError[0] = "Bad element under : " + tagName;                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;                return false;              }         }     }    ...省略    return true;}

解析过程中对应的 tagName 与我们在 AndroidManifest.xml 中声明一致,这也可以证明我们在 Manifest 中四大组件的声明。

到此 attachApplication 方法工作的第一部分就算是分析完了,该部分内容的主要工作是:通过 PMS 收集在 AndroidManifest 中注册的 ContentProvider 信息,将其封装成 ProviderInfo 集合。

ActivityManagerService attachApplication 方法第二部分分析:

将第一部分收集的 ProviderInfo 信息集合,作为参数远程调用 ApplicationThread 的 bindApplication 方法。此时将重新回到应用进程启动类 ActivityThread 中:

Android 存储选项之 ContentProvider 启动存在的暗坑_第5张图片 ApplicationThread

ApplicationThread 是 ActivityThread 与系统 PMS 进程通信的桥梁,它本质也是一个 Binder 对象。ApplicationThread bindApplication 方法:

    public final void bindApplication(String processName, ApplicationInfo appInfo,                                      List providers, ...省略) {        ... 省略        //将返回数据都封装在AppBindData中        AppBindData data = new AppBindData();        data.processName = processName;        data.appInfo = appInfo;        //这是我们要跟踪的ContentProvider集合        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;        data.autofillCompatibilityEnabled = autofillCompatibilityEnabled;        //发送BIND_APPLICATION消息到主线程Handler        sendMessage(H.BIND_APPLICATION, data);    }

在 bindApplication 方法发送 BIND_APPLICATION 消息到当前进程的主线程 Handler 中。

小结

在分析 ContentProvider 的收集过程中,验证了自定义 ContentProvider 必须在 AndroidManifest.xml 注册,并且验证了 Activity、Service、BroadcastReceiver
在 Manifest 中的注册过程。


回到 ActivityThread

在 ApplicationThread 的 bindApplication 方法发送消息到主线程,此时来到 ActivityThread Handler 的 handleMessage 方法,先看下在 ActivityThread 中的声明:

class H extends Handler {    public static final int BIND_APPLICATION        = 110;    public static final int EXIT_APPLICATION        = 111;    public static final int RECEIVER                = 113;    public static final int CREATE_SERVICE          = 114;    public static final int SERVICE_ARGS            = 115;    public static final int STOP_SERVICE            = 116;    // 省略    public void handleMessage(Message msg) {        switch (msg.what) {            //通过 ApplicationThread 发送的 BIND_APPLICATION            case BIND_APPLICATION:                AppBindData data = (AppBindData) msg.obj;                //调用 handleBindApplication 开始真正创建 Application                handleBindApplication(data);                break;                                ...省略        }    }}

不知道大家有没有注意到,到现在为止我们应用的 Application 对象还没有被创建。先来跟踪下 handleBindApplication 方法(这个方法超级长,可能是 ActivityThread 中最长的一个方法)。虽然该方法体较长,但是我们只分析主线部分也可以将它划分成两个部分:

  • 遍历 PMS 收集到的所有 ContentProvider 集合信息(ProviderInfo),并创建所有 ContentProvider 实例。回调其 onCreate 方法。

  • 创建当前进程的 Application 对象,首先回调其 attach 方法,这步发生在遍历 ContentProvider 集合之前,创建每个 ContentProvider 并回调其 onCreate 方法之后,回调 Application 的 onCreate。

handleBindApplication 方法实现如下:

    private void handleBindApplication(AppBindData data) {        ...省略                 /**         * 这里创建了Application对象,并回调其attach()         */        app = data.info.makeApplication(data.restrictedBackupMode, null);                if (!data.restrictedBackupMode) {            //判断Provider集合信息不为空            if (!ArrayUtils.isEmpty(data.providers)) {                /**                 * 创建所有在Manifest中注册的Provider,并回调onCreate                 * */                installContentProviders(app, data.providers);                mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10 * 1000);            }        }                ...省略                /**        * 回调Application的onCreate         * */        mInstrumentation.callApplicationOnCreate(app);                ... 省略    }

先来看下 Application 的创建过程,makeApplication 方法:

public Application makeApplication(boolean forceDefaultAppClass,    Instrumentation instrumentation) {... 省略  try {      java.lang.ClassLoader cl = getClassLoader();      if (!mPackageName.equals("android")) {          Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,                  "initializeJavaContextClassLoader");          initializeJavaContextClassLoader();          Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);      }      //创建ContextImpl将其关联到Application      ContextImpl appContext =     ContextImpl.createAppContext(mActivityThread, this);      //通过Instrumentation创建Application 并回调其attach方法      app = mActivityThread.mInstrumentation.newApplication(              cl, appClass, appContext);      appContext.setOuterContext(app);  } catch (Exception e) {      ... 省略  }  ... 省略  return app;}

通过 Instrumentation 的 newApplication 方法完成创建:

public Application newApplication(ClassLoader cl, String className, Context context)        throws InstantiationException, IllegalAccessException,         ClassNotFoundException {    //通过反射创建Application    Application app = getFactory(context.getPackageName())            .instantiateApplication(cl, className);    //调用Application的attach    app.attach(context);    return app;}

此时应用进程的 Application 对象才被创建,但是生命周期 onCreate 方法并没有被回调。重新回到 handBindApplication 方法,看下 Provider 的创建过程。

installContentProviders 方法如下:
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());        }        //遍历创建所有的ContentProvider        ContentProviderHolder cph = installProvider(context, null, cpi,                false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);        if (cph != null) {            cph.noReleaseNeeded = true;            results.add(cph);        }    }    // 省略}

这里我们重点跟踪 installProvider 方法如下:

private ContentProviderHolder installProvider(Context context,                                              ContentProviderHolder holder, ProviderInfo info,                                              boolean noisy, boolean noReleaseNeeded, boolean stable) {    ContentProvider localProvider = null;    //这是一个Binder对象,实际类型是ContentProviderProxy    IContentProvider provider;    if (holder == null || holder.provider == null) {        // 方法参数中holder传递进来的就是null,故会走该逻辑        //...省略        Context c = null;        ApplicationInfo ai = info.applicationInfo;        //根据包名判断该ContentProvider是否属于当前应用        //该部分if-else逻辑主要为获得一个合适的Context,然后通过该Context获得ClassLoader        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);            } catch (PackageManager.NameNotFoundException e) {            }        }        if (c == null) {            //如果获取不到上下文,直接return。            return null;        }        if (info.splitName != null) {            try {                c = c.createContextForSplit(info.splitName);            } catch (NameNotFoundException e) {                throw new RuntimeException(e);            }        }        try {            final java.lang.ClassLoader cl = c.getClassLoader();            LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);            if (packageInfo == null) {                // System startup case.                packageInfo = getSystemContext().mPackageInfo;            }            //通过类加载器加载, 反射创建 Class.newInstance() ContentProvider实例            localProvider = packageInfo.getAppFactory()                    .instantiateProvider(cl, info.name);            //这里得到是当前ContentProvider的Binder对象,它的实际类型是ContentProviderProxy            provider = localProvider.getIContentProvider();            if (provider == null) {                Slog.e(TAG, "Failed to instantiate class " +                        info.name + " from sourceDir " +                        info.applicationInfo.sourceDir);                //如果未能正确初始化该ContentProvider的Binder,直接return。                return null;            }            if (DEBUG_PROVIDER) Slog.v(                    TAG, "Instantiating local provider " + info.name);            //ContentProvider的attachInfo中会回调ContentProvider的onCreate()方法            //在attachInfo中毁掉了ContentProvider的onCreate()方法。            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;        }    } else {        provider = holder.provider;        if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ": "                + info.name);    }    //...省略        return retHolder;}

由于其方法参数 holder 传递为 null,故会走 holder == null 的逻辑:

    packageInfo.getAppFactory().instantiateProvider(cl, info.name)

在该方法内实际通过 ClassLoader 加载,并反射 newInstance 创建该 ContentProvider 实例。

 public @NonNull ContentProvider instantiateProvider(@NonNull ClassLoader cl,        @NonNull String className)        throws InstantiationException, IllegalAccessException, ClassNotFoundException {    //通过类加载加载,并反射创建该Provider实例    return (ContentProvider) cl.loadClass(className).newInstance();}

回到 installProvider 方法,创建并返回 ContentProvider 实例后会调用它的 attachInfo 方法:

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);        }        //这里回调了ContentProvider的onCreate方法        ContentProvider.this.onCreate();    }}

首次创建 ConentProvider 时,该 mContext 变量肯定是为 null 。在方法的最后回调了 onCreate 方法,此时我们自定义的 ContextProvider 的 onCreate 就会被回调。

这里我们也得到验证,自定义 ContentProvider 的创建过程以及生命周期默认在 Application 的 onCreate 方法之前

重新回到 handleBindApplication 方法,Application 的生命周期还没有回调呢!

/**  * 回调Application的onCreate  * */mInstrumentation.callApplicationOnCreate(app);

知道此刻,我们的 Application 才算是真正创建完成。

handleBindApplication 方法中首先创建了当前进程的 Applicaiton 对象,并没有立即回调其 onCreate 方法,而是创建所有在 Manifest 注册的 ContentProvider 对象,并回调其生命周期 onCreate 完成之后,才重新回调Application 的 onCreate 方法。


总结

大家是否注意到 ContentProvider 加载和创建都是在主线程完成,并且还都是在应用启动过程完成,ContentProvider 的生命周期默认在 Application onCreate 之前。这也验证了文章开头为大家介绍的启动性能,在使用 ContentProvider 需要注意的“暗坑”,自定义 ContentProvider 类的构造函数、静态代码块、onCreate 函数都尽量不要做耗时的操作,会拖慢启动速度


以上便是个人在学习 ContentProvider 启动性能的心得和体会,文中如有不妥或有更好的分析结果,欢迎大家指出!

文章如果对你有帮助,就请留个赞吧!

更多相关文章

  1. android使用android:ellipsize="end"无效的解决方法
  2. Android数据加密之Rsa加密
  3. Android 软件安装方法介绍
  4. 数据显示Android“姜饼”系统用户数增多
  5. android中的数据库操作
  6. Android数据加密之Aes加密
  7. Android实现全屏显示的方法

随机推荐

  1. Android 第八天_重置版_服务service
  2. android surfaceflinger研究----显示系统
  3. Android Studio几个常用的设置
  4. Android 系统服务一览表
  5. android 学习二 从HelloWorld说起
  6. android xml中应用占位符
  7. google map 的使用
  8. Android实现购物车加减器控件
  9. android native jni 代码
  10. Android 过度绘制优化