美文网首页
Android ContentProvider源码阅读学习

Android ContentProvider源码阅读学习

作者: JeremyDai | 来源:发表于2016-06-15 17:05 被阅读268次

概述

如果在android当中使用过媒体数据,比如获取手机或平板里面自带的图片路径、音乐路径、视频路径等等都是通过MediaProvider ,或者是通讯录里面的数据库 ContractsProvide, 这些数据在android系统当中是非常重要的,这几个类都是继承自ContentProvider,它是android的四大天王之一。ContentProvider对外共享数据统一了数据的访问方式。

Content providers are one of the primary building blocks of Android applications, providing content to applications. They encapsulate data and provide it to applications through the single ContentResolver interface. A content provider is only required if you need to share data between multiple applications. For example, the contacts data is used by multiple applications and must be stored in a content provider. If you don't need to share data amongst multiple applications you can use a database directly via SQLiteDatabase.

使用

以mediaprovider 为例子:

//得到ContentResolver 对象,通过 contentresolver 可以获取到contenprovider的实例,避免了跨进程调用的细节
ContentResolver cr = context.getContentResover(); 
//设置你想要查询的uri 
Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
//查询数据库
Cursor cursor = mContext.getContentResolver().query(uri, null, null, null, null);
// 游标移到头部
cursor.moveToFirst();
// 获取数据库,使用while循环拿到你想要的数据信息这里就不写了
....
//关闭游标
cursor.close();

上面是一个很典型的我们查询数据库的代码。

如果你自己有一个数据库里面的数据需要共享给其他的应用来访问的话,那么你就需要写一个继承自ContentProvider的类并复写它其中的一些方法。
首先,需要在manifest xml里面申明:

<!-- android:exported="true" 指示该服务是否能够被其他应用程序组件调用或跟它交互。 -->
<provider
    android:name="com.abc.testdatabase.DatabaseProvider"
    android:authorities="com.abc.testdatabase.databaseprovider"
    android:exported="true"
    android:enabled="true" >
</provider>
// 实现里面的若干接口,然后里面你在封装一层去操作具体的数据库。
public class DatabaseProvider extends ContentProvider {
    @Override
    public boolean onCreate() {
        return true;
    }
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
                        String sortOrder) {
        return null;
    }
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        return null;
    }
    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        return 0;
    }
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        return 0;
    }
    @Override
    public String getType(Uri uri) {
        return null;
    }
}

通过代码我们可以知道返回mContext.getContentResolver()的ContentResolver对象是ApplicationContentResolver类,而ApplicationContentResolver类又是继承于ContentResolver的(This class provides applications access to the content model.)我们就先看看这个类里面的query方法里面的,


    /**
     * Query the given URI, returning a {@link Cursor} over the result set
     * with optional support for cancellation.
     * <p>
     * For best performance, the caller should follow these guidelines:
     * <ul>
     * <li>Provide an explicit projection, to prevent
     * reading data from storage that aren't going to be used.</li>
     * <li>Use question mark parameter markers such as 'phone=?' instead of
     * explicit values in the {@code selection} parameter, so that queries
     * that differ only by those values will be recognized as the same
     * for caching purposes.</li>
     * </ul>
     * </p>
     *
     * @param uri The URI, using the content:// scheme, for the content to
     *         retrieve.
     * @param projection A list of which columns to return. Passing null will
     *         return all columns, which is inefficient.
     * @param selection A filter declaring which rows to return, formatted as an
     *         SQL WHERE clause (excluding the WHERE itself). Passing null will
     *         return all rows for the given URI.
     * @param selectionArgs You may include ?s in selection, which will be
     *         replaced by the values from selectionArgs, in the order that they
     *         appear in the selection. The values will be bound as Strings.
     * @param sortOrder How to order the rows, formatted as an SQL ORDER BY
     *         clause (excluding the ORDER BY itself). Passing null will use the
     *         default sort order, which may be unordered.
     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
     * If the operation is canceled, then {@link OperationCanceledException} will be thrown
     * when the query is executed.
     * @return A Cursor object, which is positioned before the first entry, or null
     * @see Cursor
     */
    public final @Nullable Cursor query(final @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;
        }
        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 {
                qCursor = unstableProvider.query(mPackageName, uri, projection,
                        selection, selectionArgs, sortOrder, remoteCancellationSignal);
            } catch (DeadObjectException e) {
                // The remote process has died...  but we only hold an unstable
                // reference though, so we might recover!!!  Let's try!!!!
                // This is exciting!!1!!1!!!!1
                unstableProviderDied(unstableProvider);
                stableProvider = acquireProvider(uri);
                if (stableProvider == null) {
                    return null;
                }
                qCursor = stableProvider.query(mPackageName, uri, projection,
                        selection, selectionArgs, sortOrder, remoteCancellationSignal);
            }
            if (qCursor == null) {
                return null;
            }

            // Force query execution.  Might fail and throw a runtime exception here.
            qCursor.getCount();
            long durationMillis = SystemClock.uptimeMillis() - startTime;
            maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder);

            // Wrap the cursor object into CursorWrapperInner object.
            CursorWrapperInner wrapper = new CursorWrapperInner(qCursor,
                    stableProvider != null ? stableProvider : acquireProvider(uri));
            stableProvider = null;
            qCursor = null;
            return wrapper;
        } catch (RemoteException e) {
            // Arbitrary and not worth documenting, as Activity
            // Manager will kill this process shortly anyway.
            return null;
        } finally {
            if (qCursor != null) {
                qCursor.close();
            }
            if (cancellationSignal != null) {
                cancellationSignal.setRemote(null);
            }
            if (unstableProvider != null) {
                releaseUnstableProvider(unstableProvider);
            }
            if (stableProvider != null) {
                releaseProvider(stableProvider);
            }
        }
    }

里面首先就会执行acquireUnstableProvider,
IContentProvider unstableProvider = acquireUnstableProvider(uri);最终会走到acquireUnstableProvider(Context c, String name)
protected abstract IContentProvider acquireUnstableProvider(Context c, String name); 可以看到这是个抽象方法具体实现由其子类来实现,也就是在 ApplicationContentResolver里面了(它是ContexImp.java中的一内部类继承自ContentResolver)

private final ActivityThread mMainThread;
@Override
        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 {
          //得到了contentprovider ,ActivityManagerService里面实现
            holder = ActivityManagerNative.getDefault().getContentProvider(
                    getApplicationThread(), auth, userId, stable);getContentProvider
        } catch (RemoteException ex) {
        }
        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、从已经保存的本地provider中查找是否有对应的provider,有则将其返回退出。
2、从AMS中找到对应的provider。
3、安装从AMS中找到的provider。并且将provider保存在本地。
4、返回此provider

public final ContentProviderHolder getContentProvider(
            IApplicationThread caller, String name, int userId, boolean stable) {
    enforceNotIsolatedCaller("getContentProvider");
    return getContentProviderImpl(caller, name, null, stable, userId);
}


private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
            String name, IBinder token, boolean stable, int userId) {
    ...
    ContentProviderRecord cpr;

    // First check if this content provider has been published...
    cpr = mProviderMap.getProviderByName(name, userId);
    ...
    if (!providerRunning) {
        cpi = AppGlobals.getPackageManager().resolveContentProvider(name, STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
    }         
    ...
    cpr = mProviderMap.getProviderByClass(comp, userId);
    if (firstClass) {
        try {
            ApplicationInfo ai = AppGlobals.getPackageManager().
                getApplicationInfo(cpi.applicationInfo.packageName, STOCK_PM_FLAGS, userId);
            cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }
    ...
    if (firstClass) {
        mProviderMap.putProviderByClass(comp, cpr);
    }
    mProviderMap.putProviderByName(name, cpr);
    ...
    // Wait for the provider to be published... 
    synchronized (cpr) {  
        while (cpr.provider == null) {  
            ......  
            try {  
                cpr.wait();  
            } 
        }  
    }  
    return cpr != null ? cpr.newHolder(conn) : null;
}

1、首先每个context类都会内部包含了一个ContentResolver的子对象ApplicationContentResolver。
2、通过调用ApplicationContentResolver的主要方法query来获取CP的数据库数据。
3、调用的过程首先会调用ContentResolver的核心方法acquireProvider()。而acquireProvider()方法是一个抽象方法,其实现是交由子类实现。
4、通过子类的acquireProvider()方法实现了解到主要的实现是交由ActivityThread类来完成。
5、ActivityThread类会做出一个判断,如果本地保存一个需要获取的CP对象实例,就会直接返回这个对象实例,如果没有保存,则会访问AMS对象去查找获取一个对象的CP对象实例,当找到这个对象实例后会保存到本地以便日后快速获取。
6、如果在AMS里面没有找到,就会继续深入到PMS里去从全部的provider中查找。
7、获取到CP对象实例后会通过层层返回,最后再调用该CP对象的query方法获取相应的数据。

MediaProvider的启动和创建过程

参考文档:
http://blog.csdn.net/innost/article/details/47254697
http://www.jianshu.com/p/3de229704a22

相关文章

网友评论

      本文标题:Android ContentProvider源码阅读学习

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