Android四大组件Android 复习学习使用

ContentProvider详解

2018-07-16  本文已影响0人  menteelin

概述

ContentProvider虽说我们平时用的并不多,但是作为安卓四大组件之一,其地位不容忽视。ContentProvider的作用是为不同的应用之间数据共享,提供统一的接口,我们知道安卓系统中应用内部的数据是对外隔离的,要想让其它应用能使用自己的数据(例如通讯录)这个时候就用到了ContentProvider。

ContentProvider如何共享数据

ContentProvider通过uri来标识其它应用要访问的数据,通过ContentResolver的增、删、改、查方法实现对共享数据的操作。还可以通过注册ContentObserver来监听数据是否发生了变化来对应的刷新页面。下面分别说说每个类的作用。

ContentProvider讲解

ContentProvider是一个抽象类,如果我们需要开发自己的内容提供者我们就需要继承这个类并复写其方法,需要实现的主要方法如下:
public boolean onCreate()
在创建ContentProvider时使用
public Cursor query()
用于查询指定uri的数据返回一个Cursor
public Uri insert()
用于向指定uri的ContentProvider中添加数据
public int delete()
用于删除指定uri的数据
public int update()
用户更新指定uri的数据
public String getType()
用于返回指定的Uri中的数据MIME类型
数据访问的方法insert,delete和update可能被多个线程同时调用,此时必须是线程安全的

uri讲解

其它应用可以通过ContentResolver来访问ContentProvider提供的数据,而ContentResolver通过uri来定位自己要访问的数据,所以我们要先了解uri。URI(Universal Resource Identifier)统一资源定位符,如果您使用过安卓的隐式启动就会发现,在隐式启动的过程中我们也是通过uri来定位我们需要打开的Activity并且可以在uri中传递参数。URI的格式如下:
[scheme:][//host:port][path][?query]
单单看这个可能我们并不知道是什么意思,下面来举个栗子就一目了然了
URI:http://www.baidu.com:8080/wenku/jiatiao.html?id=123456&name=jack
scheme:根据格式我们很容易看出来scheme为http
host:www.baidu.com
port:就是主机名后面path前面的部分为8080
path:在port后面?的前面为wenku/jiatiao.html
query:?之后的都是query部分为 id=123456$name=jack
uri的各个部分在安卓中都是可以通过代码获取的,下面我们就以上面这个uri为例来说下获取各个部分的方法:
getScheme() :获取Uri中的scheme字符串部分,在这里是http
getHost():获取Authority中的Host字符串,即 www.baidu.com
getPost():获取Authority中的Port字符串,即 8080
getPath():获取Uri中path部分,即 wenku/jiatiao.html
getQuery():获取Uri中的query部分,即 id=15&name=du
到这里关于uri的部分就讲解完了,下面让我们通过一个自定义ContentProvider实例来说下自定义ContentProvider的流程以及怎么使用ContentResolver操作ContentProverb的数据

自定义ContentProvider实例讲解

我们先创建一个项目叫contentprovider,在该项目中我们自定义一个StudentContentProvider的类用来共享该应用的数据,自定义StudentContentProvider的代码如下:

public class StudentContentProvider extends ContentProvider {

    //这里的AUTHORITY就是我们在AndroidManifest.xml中配置的authorities
    private static final String AUTHORITY = "com.jrmf360.studentProvider";

    //匹配成功后的匹配码
    private static final int MATCH_CODE = 100;

    private static UriMatcher uriMatcher;

    private StudentDao studentDao;

    //数据改变后指定通知的Uri
    private static final Uri NOTIFY_URI = Uri.parse("content://" + AUTHORITY + "/student");

    static {
        //匹配不成功返回NO_MATCH(-1)
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

        //添加我们需要匹配的uri
        uriMatcher.addURI(AUTHORITY,"student", MATCH_CODE);
    }


    @Override
    public boolean onCreate() {
        studentDao = StudentDao.getInstance(getContext());
        return false;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
                        @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        int match = uriMatcher.match(uri);
        if (match == MATCH_CODE){
            Cursor cursor = studentDao.queryStudent();
            return cursor;
        }
        return null;
    }

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

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        if (uriMatcher.match(uri) == MATCH_CODE){
            studentDao.insertStudent(values);
            notifyChange();
        }
        return null;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        if (uriMatcher.match(uri) == MATCH_CODE){
            int delCount = studentDao.deleteStudent();
            notifyChange();
            return delCount;
        }
        return 0;
    }

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

    private void notifyChange(){
        getContext().getContentResolver().notifyChange(NOTIFY_URI,null);
    }
}

例子中有注释,但还是要说明一下几点:

  <provider
        android:authorities="com.jrmf360.studentProvider"
        android:name=".StudentContentProvider"
        android:exported="true"/>

这里的authorities唯一标识该内容提供者,这样其它的应用才可以找到该内容提供者并操作它的数据;exported为true当前内容提供者可以被其它应用使用,默认为true。

private void notifyChange(){
        getContext().getContentResolver().notifyChange(NOTIFY_URI,null);
    }

在该方法中我们通知ContentObserver那个uri数据发生了变化,以便及时更新页面。

通过其他的应用操作ContentProvider中的数据

我们再创建另外一个项目,在该项目的MainActivity方法中我们来操作刚才自定义的ContentProvider中的数据

   contentResolver = getContentResolver();
private static final String AUTHORITY = "com.jrmf360.studentProvider";
private static final Uri STUDENT_URI = Uri.parse("content://" + AUTHORITY + "/student");

contentResolver.registerContentObserver(STUDENT_URI, true, new MyContentObserver(handler));

可以看到在注册ContentObserver的方法中我们需要传递一个ContentObserver对象,下面是我写的MyContentObserver类

public class MyContentObserver extends ContentObserver {

    Handler mHandler;
    public MyContentObserver(Handler handler) {
        super(handler);
        mHandler = handler;
    }

    @Override
    public void onChange(boolean selfChange) {
        super.onChange(selfChange);
    }

    @Override
    public void onChange(boolean selfChange, Uri uri) {
        super.onChange(selfChange, uri);
        Message message = Message.obtain();
        message.obj = uri;
        mHandler.sendMessage(message);
    }
}

自定义ContentObserver类,必须实现其构造函数,在构造函数中我们需要传递一个Handler,到此我们可以明白,ContentObserver在收到数据变化的通知后通过Handler机制来通知主线程更新UI

@Override
public void onClick(View v) {
    int id = v.getId();
    if (id == R.id.btn_query) {
        Cursor cursor = contentResolver.query(STUDENT_URI, null, null, null, null, null);
        if (cursor != null && cursor.getCount() > 0) {
            while (cursor.moveToNext()) {
                String name = cursor.getString(cursor.getColumnIndex("name"));
                int age = cursor.getInt(cursor.getColumnIndex("age"));
                String desc = cursor.getString(cursor.getColumnIndex("desc"));
                Log.e(getClass().getSimpleName(), "Student:" + "name = " + name + ",age = " + age + ",desc = " + desc);
            }
            cursor.close();
        }
    } else if (id == R.id.btn_insert) {
        ContentValues contentValues = new ContentValues();
        contentValues.put("name", "新插入");
        contentValues.put("age", "-100");
        contentValues.put("desc", "我是新插入的呦。。。");
        contentResolver.insert(STUDENT_URI, contentValues);
    } else if (id == R.id.btn_del) {
        contentResolver.delete(STUDENT_URI, null, null);
    }
}

这样我们就可以对ContentProvider共享的数据进行增删查的操作。至此,就完成了ConentProvider的自定义,通过其他应用操作数据,更新UI的流程,感谢您的阅读。

上一篇下一篇

猜你喜欢

热点阅读