Android ContentProvider源码阅读学习

概述

如果在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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 155,770评论 4 358
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,116评论 1 286
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 105,656评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,372评论 0 201
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,704评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,166评论 1 204
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,535评论 2 306
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,259评论 0 193
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,882评论 1 236
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,218评论 2 239
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,760评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,126评论 2 249
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,667评论 3 228
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,935评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,644评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,171评论 2 265
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,119评论 2 258

推荐阅读更多精彩内容