ContentProvider总结

ContentProvider

ContentProvider作为四大组件之一,作用是IPC(跨进程通信),底层实现是Binder。

Android提供了ContentProvider,一个程序可以通过实现一个Content provider的抽象接口将自己的数据完全暴露出去,而且Content providers是以类似数据库中表的方式将数据暴露。

ContentProvider中的URI
image.png

URI:是网络资源的定义
Authority:授权信息,用以区别不同的ContentProvider;
Path:表名,用以区分ContentProvider中不同的数据表;
Id:Id号,用以区别表中的不同数据;

自定义ContentProvider必须要实现6个方法:
public class MyContentProvider extends ContentProvider{

    @Override
    public boolean onCreate() {
        return false;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        return null;
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        return null;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }
}
ContentResolver

Content providers是以类似数据库中表的方式将数据暴露出去,那么ContentResolver也将采用类似数据库的操作来从Content providers中获取数据。

这里常使用到query操作:
query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) 

uri :查询地址 (Uri)
projection :查询的数据字段名称 (String[] )
selection :查询的条件 (String)
selectionArgs :查询条件的参数 (String[])
sortOrder :排序 (String)

ContentProvider、ContentResolver、ContentObserver 之间的关系

ContentProvider——内容提供者, 在android中的作用是对外共享数据,也就是说你可以通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过ContentProvider 对你应用中的数据进行添删改查。
ContentResolver——内容解析者, 其作用是按照一定规则访问内容提供者的数据(其实就是调用内容提供者自定义的接口来操作它的数据)。
ContentObserver——内容观察者,目的是观察(捕捉)特定Uri引起的数据库的变化,继而做一些相应的处理,它类似于数据库技术中的触发器(Trigger),当ContentObserver所观察的Uri发生变化时,便会触发它。

使用案例:获取手机联系人和短信

1.首先需要添加权限

    <uses-permission android:name="android.permission.READ_SMS"/>
    <uses-permission android:name="android.permission.READ_CONTACTS"/>

2.需要询问权限并同意

requestPermissions( arrayOf(Manifest.permission.READ_CONTACTS, Manifest.permission.READ_SMS), REQUEST_CODE)

查询联系人和短信的管理类:

object ExtendsContentProvider {

    @SuppressLint("Range")
    fun readMessages(context: Context) {
        //短信表的路径
        var uri = Uri.parse("content://sms")
        //需要查询的字段
        var projection = arrayOf("address", "date", "body")
        var cursor = context.contentResolver.query(uri, projection, null, null, null)
        cursor?.let {
            while (it.moveToNext()) {
                Log.i(
                    "minfo", it.getString(it.getColumnIndex("address"))
                            + it.getString(it.getColumnIndex("date"))
                            + it.getString(it.getColumnIndex("body"))
                )
            }
            it.close()
        }
    }

    @SuppressLint("Range")
    fun readContacts(context: Context) {
        //ContactsContract.CommonDataKinds.Phone 联系人表
        var cursor: Cursor? = context.contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
            null, null, null, null)
        cursor?.let {
            while (it.moveToNext()) {
                //读取通讯录的姓名
                var name = it.getString(it.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))
                //读取通讯录的号码
                var number = cursor.getString(it.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))
                Log.i("minfo", "$name--$number")
            }
        }
    }

/**
     * 模糊查询联系人
     */
    @SuppressLint("Range")
    fun getContactsFuzzyQueryByKey(context: Context, key: String) {
        //ContactsContract.CommonDataKinds.Phone 联系人表
        val projection = arrayOf(
            ContactsContract.PhoneLookup.DISPLAY_NAME,
            ContactsContract.CommonDataKinds.Phone.NUMBER
        )
        val selection = StringBuilder()
        selection.append(ContactsContract.Contacts.DISPLAY_NAME)
        selection.append(" LIKE '%$key%' ")
        var cursor: Cursor? = context.contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
            projection, selection.toString(), null, null)
        cursor?.let {
            while (it.moveToNext()) {
                //读取通讯录的姓名
                var name = it.getString(it.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))
                //读取通讯录的号码
                var number = cursor.getString(it.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))
                Log.i("minfo", "$name--$number")
            }
            it.close()
        }
    }

    /**
     * 模糊查询短信
     */
    @SuppressLint("Range")
    fun getSmsFuzzyQueryByKey(context: Context, key: String) {
        //短信表的路径
        var uri = Uri.parse("content://sms")
        //需要查询的字段
        var projection = arrayOf(Telephony.Sms.ADDRESS, Telephony.Sms.BODY)
        val selection = java.lang.StringBuilder()
        selection.append(Telephony.Sms.BODY)
        selection.append(" LIKE '%$key%' ")
        var cursor = context.contentResolver.query(uri, projection, selection.toString(), null, null)
        cursor?.let {
            while (it.moveToNext()) {
                var number = it.getString(it.getColumnIndex(Telephony.Sms.ADDRESS))
                var content = it.getString(it.getColumnIndex(Telephony.Sms.BODY))
                Log.i("minfo", "$content--$number")
            }
            it.close()
        }
    }

}

contentProvider的初始化流程

Application的创建和执行onCreate,来看看这段代码

android.app.ActivityThread
         ......
            Application app;
            ......
            try {
                // If the app is being launched for full backup or restore, bring it up in
                // a restricted environment with the base application class.
                2.创建Application(内部也是反射)
                app = data.info.makeApplication(data.restrictedBackupMode, null);
                ......
                if (!data.restrictedBackupMode) {
                    if (!ArrayUtils.isEmpty(data.providers)) {
                        本文关注点:初始化ContentProvider
                        installContentProviders(app, data.providers);
                    }
                }
                try {
                       3.初始化Application
                       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 {
                ......
            }
        }
     ......
 }

可以看到,执行初始化ContentProvider的时机在makeApplication和callApplicationOnCreate直接,那最起码可以得出一个结论:ContentProvider初始化的时机在Application的onCreate之前。

想要弄明白ContentProvider初始化做了什么,那就需要去installContentProviders方法看一看了。

android.app.ActivityThread


    private void installContentProviders(
            Context context, List<ProviderInfo> providers) {
        final ArrayList<ContentProviderHolder> results = new ArrayList<>();
        遍历ProviderInfo列表
        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());
            }
            各自启动
            ContentProviderHolder cph = installProvider(context, null, cpi,
                    false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
            if (cph != null) {
                cph.noReleaseNeeded = true;
                启动后放入数组中
                results.add(cph);
            }
        }

        try {
            将数组传给AMS
            ActivityManager.getService().publishContentProviders(
                getApplicationThread(), results);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }

installContentProviders完成了通过installProvider方法完成ContentProvider的启动,并且将启动了的ContentProvider放在了数组中,传递给了AMS,AMS内部会将他们存起来,这样外部调用者就可以直接从AMS中获取ContentProvider了。

参考:
https://juejin.cn/post/6996512754899091487

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容