本次学习Android四大组件之一——ContentProvider组件。这是应用程序之间共享数据的一种接口机制,提供了更高级的数据共享方法。
实践:本次学习之后做一个使用ContentProvider来实现数据共享的简单实例。

一、ContentProvider简介

(一)理论支持

1、ContentProvider(数据提供者)是在应用程序之间共享数据的一种接口机制
2、ContentProvider提供了更高级的数据共享方法,应用可以指定需要共享的数据,二其他应用程序则可以在不知道数据来源、路径的情况下,对共享数据进行查询、添加、删除和更新等操作。
3、许多的Android系统的内置数据也是通过ContentProvider提供给用户使用,例如:通讯录、音频、视频文件和图像文件等。
4、在创建ContentProvider时,需要首先使用数据库、文件系统或网络实现底层功能,然后再继承ContentProvider的类中实现基本数据操作的接口函数,包括添加、删除、查找和更新功能。
5、调用者不能够直接调用ContentProvider的接口函数,而需要使用ContentResolver对象(数据使用者),通过URI(代表要操作的数据,解析uri来获取数据)间接调用ContentProvider。ContentProvider可以调用文件系统、数据库(SQLite)和网络。

(二)数据模型

ContentProvider的数据模型类似于数据库中的数据表,每一行是一条记录,每一列具有相同的数据类型。
每条记录都包含一个长型的字段ID,用来唯一标识每一条记录。

(三)URI语法

URI是通用资源标志符,用来定位任何远程或者本地的可用资源。
ContentProvider规定数据形式的URI包括:scheme、主机名(授权者名字,可在配置文件中查看)、path(准确到表加上id更准确)。
其中scheme规定为:content(内容提供者)
content://’<’authority>/’<’data_path>/’<’id>

(四)URI的工具类

在安卓中提供了两个URI操作的工具类:
1、UriMatcher——用于匹配uri

用法:if(UriMatcher.match(uri) == CONTACT)

使用UriMatcher.addURI(authoroty,path,匹配码);

2、ContentUri——用于操作uri路径加上id部分

用法:withAppendedId(uri,id);为路径添加上id部分

在URI中还有几个常用的方法:

uri.parse(“string”);//将String类型转化为uri类型
List list = uri.getPathSegments();//将uri中的三部分分别放入list中

(五)URI数据路径表示形式

1、如果ContentProvider仅提供一个数据集,数据路径则是可以省略的。
2、如果ContentProvider仅提供过个数据集,数据路径必须指明具体的哪个。
请求整个Student数据集的URI应该写为:content://主机名/Student
请求Student中第三个数据的URI应该写为:content://主机名/Student/3

二、开发步骤

(一)创建数据提供者

程序员通过继承ContentProvider类可以创建一个新的数据提供者,通过三个步骤创建:

1、继承ContentProvider,并重载六个函数

delete()删除、insert()插入、qurey()查询、update()更新、onCreate()初始化底层数据集和建立数据连接等工作、

2、声明CONTENT_URI,实现UriMatcher

在新构造的ContentProvider类中,通过构造一个UriMatcher,判断URI是单条数据还是多条数据。
为了便于判断和使用URI,一般将URI的授权者名称和数据路径等内容声明为静态常量,并声明CONTENT_URI。
具体的声明过程放在实战中,注释出来了。
使用UriMatcher时,参照工具类的使用,可以直接调用match()方法。

3、注册ContentProvider

现在一般的开发工具都能够自动注册的。

(二)使用数据提供者

1、使用ContentProvider是通过Android组件都具有的ContentResolver对象,通过URI进行数据操作。
2、程序开发人员只需要知道URI和数据集的数据格式,则可以进行数据操作,解决不同应用之间的数据共享问题。
3、每个Android组件都有一个ContentResolver对象,获取ContentResolver对象的方法是调用getContentResolver()函数。

ContentResolver resolver = this.getContentResolver();

到这里ContentProvider的使用过程就讲解完了,很多人,包括在我一开始学习的时候都会将这个部分跟数据库连接的读写操作进行对比,想不明白。这里在针对我自己的理解解释一下两个的使用:
1、首先SQLite数据库的读写呢,是每个Android系统里面自己携带的一个数据库的增删查改,自己的改动别人不会知道,也不会影响到别人;
2、而,ContentProvider里面的数据增删查改啊,也是连接数据库的,但是这是可以提供给多个数据使用者的数据,这些人都通过注册的ContentProvider**数据提供者**来使用数据,实现了不同用户之间数据的共享。相似的数据库增删查改方法,不一样的使用途径。

三、实战——数据的增删查改(共享)

首先创建数据提供者。下面是我建立的继承了ContentProvider的MyContentProvider类:

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.widget.ListView;

import java.util.List;

public class MyContentProvider extends ContentProvider {

    private DBHelpler dbHelpler;
    /**继承了ContentProvider的类继承六个方法。
     * 声明CONTENT_URI,实现UriMatcher工具类。
     */
    public static final String AUTHORITY = "com.example.asus.sqlite";//URI的授权者的名称
    public static final String PATH_SINGLE = "Student/#";//单个Student数据的数据路径,#代表任何数字
    public static final String PATH_MULTIPLE = "Student";//多个Student数据的数据路径
    public static final String CONTENT_URI_STRING = "content://"+ AUTHORITY + "/" + PATH_MULTIPLE;//声明了CONTENT_URI的字符串形式
    public static final Uri CONTENT_URI = Uri.parse(CONTENT_URI_STRING);//Uri.parse方法将String类型转换成URI类型。
    private static final int MULTILE_STUDENT = 1;//多条数据的返回值
    private static final int SINGLE_STUDENT = 2;//单条数据的返回值
    private static final UriMatcher uriMathcher;//声明UriMatcher工具类
    //定义UriMatcher
    static {
        uriMathcher = new UriMatcher(UriMatcher.NO_MATCH);//无匹配时的返回码
        //添加新的匹配项
        uriMathcher.addURI(AUTHORITY,PATH_SINGLE,SINGLE_STUDENT);//单条数据时的返回码
        uriMathcher.addURI(AUTHORITY,PATH_MULTIPLE,MULTILE_STUDENT);//多条数据时的返回码
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        /**
         * 删除方法:
         * 1、Uri uri:查询内容
         * 3、String selection:查询条件。返回数据的查询条件
         * 4、String[] selectionArgs:?的值
         */
        //链接数据库
        SQLiteDatabase db = dbHelpler.getWritableDatabase();
        //定义返回值
        int results = 0;
        //检查uri匹配的返回值
        int code=uriMathcher.match(uri);
        if (code==SINGLE_STUDENT)
        {
            List list= uri.getPathSegments();//分割“content://com.zlg.android/student/2”,各个部分分开。
            String id=(String)list.get(list.size()-1);//最后一个
            results=db.delete("Student","id=?",new String[]{id});
        }
        if (code==MULTILE_STUDENT)
        {
            results=db.delete("Student",selection,selectionArgs);//全部删除
        }
        //提醒使用数据者,数据内容改变了
        this.getContext().getContentResolver().notifyChange(uri,null);
        return results;
    }

    @Override
    public String getType(Uri uri) {
        /*
        返回指定URI的数据类型
         */
        //检查uri匹配的返回值
        switch (uriMathcher.match(uri)) {
            //直接return了之后,不用再switch语句中再使用break
            case SINGLE_STUDENT: {
                return "vnd.android.cursor.item/com.example.asus.sqlite.Student";
            }
            case MULTILE_STUDENT: {
                return "vnd.android.cursor.dir/com.example.asus.sqlite.Student";
            }
        }
            return "";
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        /**
         * 插入方法:
         * 1、Uri uri:查询内容
         * 2、ContentValues values:包装好的values
         */
        //链接数据库
        SQLiteDatabase db = dbHelpler.getWritableDatabase();
        //调用系统insert方法,将包装好了的ContentValues values直接传进,返回一个id
        long id = db.insert("Student",null,values);
        //调用方法在原来的uri后面追加上新的id
        Uri newuri = ContentUris.withAppendedId(uri,id);
        //通知数据使用者数据集发生了变化
        getContext().getContentResolver().notifyChange(newuri,null);
        return newuri;
    }

    @Override
    public boolean onCreate() {
       //实例化
        dbHelpler = new DBHelpler(this.getContext());
        return true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        /**
         * 查询方法:
         * 1、Uri uri:查询内容
         * 2、String[] projection:返回项。从数据集返回哪些数据项
         * 3、String selection:查询条件。返回数据的查询条件
         * 4、String[] selectionArgs:?的值
         * 5、String sortOrder:排序
         */
        //链接数据库
        SQLiteDatabase db = dbHelpler.getWritableDatabase();
        //检查uri匹配的返回值
        switch (uriMathcher.match(uri)) {
            case SINGLE_STUDENT: {
                List list = uri.getPathSegments();
                String id = (String)list.get(list.size()-1);
                if (selection == null || selection.length() == 0){
                    selection = "id="+id;
                }else {
                    selection += "and id = "+id;
                }
            }
            case MULTILE_STUDENT: {
                //查询多条处理代码
            }
        }
        return db.query("Student",projection,selection,selectionArgs,null,null,sortOrder);
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        /**
         * 更新方法:
         * 1、Uri uri:查询内容
         * 2、ContentValues values:包装好的values
         * 3、String selection:查询条件。返回数据的查询条件
         * 4、String[] selectionArgs:?的值
         */
        //链接数据库
        SQLiteDatabase db = dbHelpler.getWritableDatabase();
        int results = 0;
        switch (uriMathcher.match(uri)){
            case SINGLE_STUDENT:
                List list = uri.getPathSegments();
                String id = (String)list.get(list.size()-1);
                results = db.update("Student",values,"id=?",new String[]{id});
            break;
            case MULTILE_STUDENT:
                db.update("Student",values,selection,selectionArgs);
            break;
        }
        return results;
    }
}

然后,数据使用者来使用数据,当然需要的是获取当前Android的ContentResolver,来使用数据。

public void insertResolver(View v){
        //获取插入的值
        ed_name = (EditText)findViewById(R.id.name_ed);
        ed_age = (EditText)findViewById(R.id.age_ed);
        ed_height = (EditText)findViewById(R.id.id_ed);
        //获取使用数据者的对象
        ContentResolver contentResolver = this.getContentResolver();
        //还可以加id,只访问一条数据。
        Uri uri=Uri.parse("content://com.example.asus.sqlite/Student");

        ContentValues contentValues=new ContentValues();
        contentValues.put("name",ed_name.getText().toString());
        contentValues.put("age",ed_age.getText().toString());
        contentValues.put("height",ed_height.getText().toString());

        contentResolver.insert(uri,contentValues);
    }
    public void deleteResolver(View view){
        //获取需要删除的id
        ed_id = (EditText)findViewById(R.id.checkid_ed);
        String id = ed_id.getText().toString();
        //获取使用数据者的对象
        ContentResolver contentResolver = this.getContentResolver();
        //追加uri
        Uri uri= Uri.parse("content://com.example.asus.sqlite/Student/"+id);
        contentResolver.delete(uri,null,null);
    }
    public void QuriyResolver(View view){
        //获取需要查询的id
        ed_id = (EditText)findViewById(R.id.checkid_ed);
        String id0 = ed_id.getText().toString();
        //获取使用数据者的对象
        ContentResolver contentResolver = this.getContentResolver();
        //追加uri
        Uri uri= Uri.parse("content://com.example.asus.sqlite/Student/"+id0);
        al.clear();
        Cursor cursor=contentResolver.query(uri,new String[]{"id","name","age","height"},null,null,null);
        while (cursor.moveToNext())
        {
            listView = (ListView)findViewById(R.id.ListView);//声明ListView,一会显示数据库的内容。
            int id=cursor.getInt(0);//例的索引
            String name=cursor.getString(1);
            int age=cursor.getInt(cursor.getColumnIndex("age"));//不知道索引,可以根据他的列名来找。
            int height=cursor.getInt(3);
            al.add(id+","+name+","+age+","+height);
            Log.d(MainActivity.class.toString(),"信息为"+id+","+name+","+age+","+height);//在LogCat打印信息。
        }
        ad = new ArrayAdapter(this,android.R.layout.simple_list_item_1,al);
        listView.setAdapter(ad);
    }
    public void UpdateResolver(View view){
        //获取更新的值
        ed_name = (EditText)findViewById(R.id.name_ed);
        ed_age = (EditText)findViewById(R.id.age_ed);
        ed_height = (EditText)findViewById(R.id.id_ed);
        //获取使用数据者的对象
        ContentResolver contentResolver = this.getContentResolver();
        //获取需要更新的id
        ed_id = (EditText)findViewById(R.id.checkid_ed);
        String id0 = ed_id.getText().toString();
        //追加uri
        Uri uri= Uri.parse("content://com.example.asus.sqlite/Student/"+id0);
        ContentValues contentValues=new ContentValues();
        contentValues.put("name",ed_name.getText().toString());
        contentValues.put("age",ed_age.getText().toString());
        contentValues.put("height",ed_height.getText().toString());
        contentResolver.update(uri,contentValues,null,null);
    }

此上为本次学习的ContentProvider组件,希望增加的实战部分可以更好的让大家理解和使用。