Android中ContentProvider的工作过程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lyz_zyx/article/details/83061337

ContentProvider是一种内容共享型组件,它通过Binder向其它组件乃至其它应用提供数据。关于ContentProvider是如何使用的,我们在之前文章《Android里内容提供者ContentProvider的使用》中已经有介绍过和实例演示。今天主要是对ContentProvider的一些工作过程作分析和学习。

ContentProvider的启动过程

一般来说,ContentProvider都应该是单实例的。因为android:multiprocess默认是false,当android:multiprocess为true时,ContentProvider为多实例,这时每个调用者的进程都存在一个ContentProvider对象。但由于实际开发中,并未发现多实例的具体使用场景(虽然官方解释说可以避免进程问通信的开销),因此我们可以简单认为ContentProvider都是单实例。下面来看看单实例的ContentProvider的启动过程。

当ContentProvider所在的进程启动时,ContentProvider会同时启动并被发布到ActivityManagerService中。而且,ContentProvider的onCreate很特殊,会先于Application的onCreate执行。我们在前面文章《Android应用程序启动详解(二)从源码了解App的启动过程》中也曾经一句话提过到这点,来回顾一下源码:

ActivityThread.java

private void handleBindApplication(AppBindData data) {
    ……
    final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();
    try {
        // 关键代码1
        Application app = data.info.makeApplication(data.restrictedBackupMode, null);
        mInitialApplication = app;
 
        if (!data.restrictedBackupMode) {
            if (!ArrayUtils.isEmpty(data.providers)) {
                // 关键代码2
                installContentProviders(app, data.providers);
                mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
            }
        }
 
        try {
            mInstrumentation.onCreate(data.instrumentationArgs);
 
        }
        catch (Exception e) {
            throw new RuntimeException( "Exception thrown in onCreate() of "
                + data.instrumentationName + ": " + e.toString(), e);
        }
 
        try {
            // 关键代码3
            mInstrumentation.callApplicationOnCreate(app);
        } catch (Exception e) {
            if (!mInstrumentation.onException(app, e)) {
                throw new RuntimeException(
                    "Unable to create application " + app.getClass().getName()
                    + ": " + e.toString(), e);
            }
        }
    } finally {
        StrictMode.setThreadPolicy(savedPolicy);
    }
}

关键代码1中就是创建Application对象,而关键代码3就是在创建出Application对象后,调用了callApplicationOnCreate(app)方法,也就是回调Application的onCreate方法。我们重要来看看关键代码2,它就是ContentProvider的创建过程逻辑。继续跟踪installContentProviders方法看它做了啥事情:

private void installContentProviders(
        Context context, List<ProviderInfo> providers) {
    final ArrayList<IActivityManager.ContentProviderHolder> results =
        new ArrayList<IActivityManager.ContentProviderHolder>();
    // 关键代码1
    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
        IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
                false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
        if (cph != null) {
            cph.noReleaseNeeded = true;
            results.add(cph);
        }
    }

    try {
        // 关键代码3
        ActivityManagerNative.getDefault().publishContentProviders(
            getApplicationThread(), results);
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
}

方法内逻辑很清楚,关键代码1中遍历当前进程的ProviderInfo的列表并一一调用关键代码2中的installProvider方法来启动它们,最后就是关键代码3中,将已经启动的ContentProvider发布到ActivityManagerService中(关于ActivityManagerNative.getDefault()为什么是ActivityManagerService,我们在前面也提到很多次,不明白的话可以查看之前的文章《Android应用程序启动详解(一)》)。来看看关键代码2的installProvider方法:

private IActivityManager.ContentProviderHolder installProvider(Context context,
        IActivityManager.ContentProviderHolder holder, ProviderInfo info,
        boolean noisy, boolean noReleaseNeeded, boolean stable) {
    ……
        try {
            // 关键代码1
            final java.lang.ClassLoader cl = c.getClassLoader();
            localProvider = (ContentProvider)cl.
                loadClass(info.name).newInstance();
            provider = localProvider.getIContentProvider();
            if (provider == null) {
                Slog.e(TAG, "Failed to instantiate class " +
                      info.name + " from sourceDir " +
                      info.applicationInfo.sourceDir);
                return null;
            }
            if (DEBUG_PROVIDER) Slog.v(
                TAG, "Instantiating local provider " + info.name);
            // XXX Need to create the correct context for this provider.
            // 关键代码2
            localProvider.attachInfo(c, info);
        } catch (java.lang.Exception e) {
            ……;
        }
    } else {
        ……
    }
    IActivityManager.ContentProviderHolder retHolder;
    synchronized (mProviderMap) {
        if (DEBUG_PROVIDER) Slog.v(TAG, "Checking to add " + provider
                + " / " + info.name);
        IBinder jBinder = provider.asBinder();
        if (localProvider != null) {
            ……
        } else {
            ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
            if (prc != null) {
                ……
            } else {
                // 关键代码3
                ProviderClientRecord client = installProviderAuthoritiesLocked(
                        provider, localProvider, holder);
                ……
            }
            retHolder = prc.holder;
        }
    }

    return retHolder;
}

上述关键代码1中,通过类加载器完成了ContentProvider对象的创建,还通过关键代码2中attachInfo方法来设置一些信息,来看看attachInfo的方法代码:

private void attachInfo(Context context, ProviderInfo info, boolean testing) {
    mNoPerms = testing;
    /*
     * Only allow it to be set once, so after the content service gives
     * this to us clients can't change it.
     */
    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.this.onCreate();
    }
}

方法中可以看到ContentProvider的onCreate方法就是在这里被调用的了,

再看回installProvider方法的关键代码3的installProviderAuthoritiesLocked方法是做了什么事情:

final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap = new ArrayMap<ProviderKey, ProviderClientRecord>();
private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider,
        ContentProvider localProvider, IActivityManager.ContentProviderHolder holder) {
    final String auths[] = holder.info.authority.split(";");
    final int userId = UserHandle.getUserId(holder.info.applicationInfo.uid);

    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;
}

原来是将刚创建的IContentProvider对象通过Map的形式来存储起来。好了,到此为止,ContentProvider就已经启动完成。

ContentProvider的操作过程

当ContentProvider启动后,外界就可以通过它所提供的insert、delete、update 和 query这四个接口来对ContentProvider中的数据源进行增删改查操作。这四个方法都是通过Binder来调用,外界无法直接访问ContentProvider,它只能通过ActivityManagerService根据Uri来获取对应用ContentProvider的Binder接口IConentProvider,然后再通过IContentProvider来访问ContentProvider中的数据源。

四个操作接口逻辑很类似,我们就从query方法入手,先来看下实例代码:

Cursor cursor = getContentResolver().query(Uri.parse("content:// com.zyx.PersonProvider/person/1"), null, null, null, null);

访问ContentProvider需要通过getContentResolver方法来获取ContentResolver的对象,ContentResolver是一个抽象类,getContentResolver方法实际上是返回了ApplicationContentResolver对象,来看代码:

ContentResolver.java

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");
    // 关键代码1
    IContentProvider unstableProvider = acquireUnstableProvider(uri);
    if (unstableProvider == null) {
        return null;
    }
    IContentProvider stableProvider = null;
    Cursor qCursor = null;
    try {
        long startTime = SystemClock.uptimeMillis();

        ICancellationSignal remoteCancellationSignal = null;
        if (cancellationSignal != null) {
            cancellationSignal.throwIfCanceled();
            remoteCancellationSignal = unstableProvider.createCancellationSignal();
            cancellationSignal.setRemote(remoteCancellationSignal);
        }
        try {
            // 关键代码2
            qCursor = unstableProvider.query(mPackageName, uri, projection,
                    selection, selectionArgs, sortOrder, remoteCancellationSignal);
        } catch (DeadObjectException e) {
            unstableProviderDied(unstableProvider);
            // 关键代码3
            stableProvider = acquireProvider(uri);
            if (stableProvider == null) {
                return null;
            }
            // 关键代码4
            qCursor = stableProvider.query(mPackageName, uri, projection,
                    selection, selectionArgs, sortOrder, remoteCancellationSignal);
        }
        ……
        return wrapper;
    } catch (RemoteException e) {
        return null;
    } finally {
        ……
    }
}

上述代码中,关键代码1 和 关键代码3都是为了获取一个IContentProvider接口对象,分别调用了acquireUnstableProvider 和 acquireProvider 方法,然后再调用其获取对象的query方法,acquireUnstableProvider方法的内部也是调用了acquireProvider方法,ApplicationContentResolver继承ContentResolver:

ContextImpl.java

private static final class ApplicationContentResolver extends ContentResolver {
    ……
    @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);
    }
……
}

再来看看acquireProvider方法的代码:

ActivityThread.java

public final IContentProvider acquireProvider(
        Context c, String auth, int userId, boolean stable) {
    // 关键代码1
    final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
    if (provider != null) {
        return provider;
    }

    IActivityManager.ContentProviderHolder holder = null;
    try {
        // 关键代码2
        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;
}

关键代码1中,通过acquireExistingProvider方法来获得已经存在的ContentProvider,如果存在就直接返回。否则,就会到关键代码2处发送一个进程间请求给ActivityManagerService让其启动目标ContentProvider,这样最终就会到了进程的启动入口方法ActivityThread的main方法,通过一系列过程后就又了”ContentProvider的启动过程”中所述的installContentProviders方法去。我们来看看关键代码1 的acquireExistingProvider方法的代码:

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);
        if (pr == null) {
            return null;
        }
        IContentProvider provider = pr.mProvider;
        IBinder jBinder = provider.asBinder();
        if (!jBinder.isBinderAlive()) {
            ……
            return null;
        }
        ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
        if (prc != null) {
            incProviderRefLocked(prc, stable);
        }
        return provider;
    }
}

我们看到了熟悉的mProviderMap对象,也就是在启动过程中调用了onCreate后将其保存起来的Map对象。知道了IContentProvider的获取,现在回到ContentResolver的query中的关键代码2和关键代码4中去看它们调用了。

IContentProvider是ContentProvider的Binder对象,它的具本实现是ContentProviderNative和ContentProvider.Transport,其中ContentProvider.Transport继承了ContentProviderNative。来看看ContentProvider.Transport的query方法代码:

ContentProvider.java

class Transport extends ContentProviderNative {
    ……
    @Override
    public Cursor query(String callingPkg, Uri uri, String[] projection,
            String selection, String[] selectionArgs, String sortOrder,
            ICancellationSignal cancellationSignal) {
        validateIncomingUri(uri);
        uri = getUriWithoutUserId(uri);
        if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
            if (projection != null) {
                return new MatrixCursor(projection, 0);
            }
            Cursor cursor = ContentProvider.this.query(uri, projection, selection,
                    selectionArgs, sortOrder, CancellationSignal.fromTransport(
                            cancellationSignal));
            if (cursor == null) {
                return null;
            }
            return new MatrixCursor(cursor.getColumnNames(), 0);
        }
        final String original = setCallingPackage(callingPkg);
        try {
            // 关键代码
            return ContentProvider.this.query(
                    uri, projection, selection, selectionArgs, sortOrder,
                    CancellationSignal.fromTransport(cancellationSignal));
        } finally {
            setCallingPackage(original);
        }
    }
……
}

关键代码明显不过,它调用了ContentProvider的query方法,也就是我们自定义继承ContentProvider的类的query方法,query方法的执行结果再通过Binder返回给调用者,就这样整个调用过程就完成了。

总结

  1. ContentProvider一般是单实例,也可通过设置android:multiprocess为true使其变为多实例,虽官方说可以避免进程问通信的开销,但目前并未发现多实例的具体使用场景
  2. ContentProvider是在Application创建完成后进行,而且ContentProvider的onCreate方法是先于Application的onCreate方法
  3. ContentProvider创建完成后,会将IContentProvider(ContentProvider的Binder对象)会保存于一个ArrayMap对象中,当外部调用insert、delete、update 和 query方法时便先检查该对象是否存在
  4. IContentProvider的实现类是ContentProviderNative和ContentProvider.Transport,当外部调用insert、delete、update 和 query方法时,就是调用到IContentProviderinsert、delete、update 和 query方法,最终也是会调用到ContentProviderinsert、delete、update 和 query方法。

 

——本文部分内容参考自《Android开发艺术探索》

 

猜你喜欢

转载自blog.csdn.net/lyz_zyx/article/details/83061337