Android组件----内容提供器Content Provider

来源:互联网 发布:url编码 js 编辑:程序博客网 时间:2024/04/29 13:42

内容提供器(Content Provider)主要用于在不同的应用程序之间共享数据,虽然Android提供的文件存储和SharePreferences都可以设置全局读写模式共享数据,但由于安全性问题,已经在Android4.2中被废弃。内容提供器将提供一种安全的数据共享方式。当一个程序通过内容提供器提供了外部访问接口,其他任何程序都可以通过该接口来对这部分数据进行访问,例如:联系人数据,短信数据。


一、访问其他应用程序中的数据

1 . 获得ContentResolver类的实例

对于每一个应用程序,如果想要访问内容提供器中的数据,就必须要借助于ContentResolver类,可以通过Context中的getContentResolver()放法获得该类的实例。

ContentResolver类中提供了类似于SQLite中类似的insert()、delete()、update()、query()等方法实现对数据的增删改查,但是参数与SQLite中的参数略有不同。

注:ContentResolver中的增删改查方法都是不接受表名参数的,而是使用Uri参数代替,这个参数被称为内容URI。

2 . 获得内容URI

内容URI给内容提供器中的参数建立了唯一标识符,它主要由权限路径两部分组成,权限主要是为了区分不同的应用程序,一般使用程序包名。比如:com.example.app.provider。路径则是对同一应用程序中不同的表进行区分,以/+表名的方式接在权限的后面。最后我饿还需要加上协议声明才组成了完整的内容URI。

内容URI标准格式

content://com.example.app.provider/table1content://com.example.app.provider/table2

3 . 将URI字符串解析为Uri对象

Uri uri = Uri.parse("content://com.example.app.provider/table1");

4 . 对数据进行增删改查

//插入数据ContentValues values1 = new ContentValues();values1.put("column1", "text1");values1.put("column2", "text2");getContentResolver().insert(uri, values1);//删除数据getContentResolver().delete(uri, "cloumn2=?", new String[]{"1"});//修改数据ContentValues values2 = new ContentValues();values2.put("column1", "t");getContentResolver().update(uri, values2, "column1 = ? and column2=?", new String[]{"text", "1"});//查询数据Cursor cursor = getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);if (cursor != null){    while (cursor.moveToNext()){        String column1 = cursor.getString(cursor.getColumnIndex("column1"));        int column2 = cursor.getInt(cursor.getColumnIndex("column2"));    }    cursor.close();}

上面的操作和SQLite的操作十分类似,只是传入的参数不再是表名,而是解析出来的Uri对象。


下面重点介绍query()中各参数的意义

query方法参数 对应SQL部分 描述 uri from table_name 指定要查询的应用程序下的某一张表 projection select column1,column2 指定查询的列名 selection where column=value 指定where的约束条件 selectionArgs - 为where中的占位符提供具体的值 orderBy order by column1,column2 指定查询结果的排序方式

下面放上一个获取联系人信息的小例子

MainActivity.java中的代码
public class MainActivity extends AppCompatActivity {    private ListView contactsView;    private ArrayAdapter<String> adapter;    private List<String> contactsList = new ArrayList<>();    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        contactsView = (ListView) findViewById(R.id.contacts_view);        adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, contactsList);        contactsView.setAdapter(adapter);        readContacts();    }    private void readContacts() {        Cursor cursor = null;        try{            //查询联系人数据            cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);            while(cursor.moveToNext()){                //获取联系人姓名                String displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));                //获取联系人手机号                String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));                contactsList.add(displayName + "\n" + number);            }        }catch (Exception e){            e.printStackTrace();        }finally {            if (cursor != null){                cursor.close();            }        }    }}
activity_main.xml文件
<?xml version="1.0" encoding="utf-8"?><LinearLayout    xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent">    <ListView        android:id="@+id/contacts_view"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:text="Hello World!" /></LinearLayout>

上面的代码中的Uri对象并不是通过parse()方法解析,是因为官方向我们直接提供了解析好的Uri对象。我们获取到手机联系人姓名和电话号码后简单地显示在了ListView中。


二、创建自己的内容提供器

上面我们使用别人提供的内容提供器来获取其他程序共享的数据,下面我们将创建自己的内容提供器来共享自己应用程序中的数据。下面是基本的步骤

1 . 创建一个类去继承ContentProvider,重写其中的6个抽象方法。

下面简单介绍这6个方法(与SQLite中的方法很类似)

1 . onCreate() ——- 初始化内容提供器的时候调用。通常在这里完成数据库的额创建或升级等操作。返回true表示内容提供器初始化成功,返回false表示失败。

注:只有当有ContentResolver尝试访问我们程序中的数据时,内容提供器才会被初始化。

2 . query() —–从内容提供器中来查询数据,query方法的参数已经在上面的表格中做了详细说明。

3 . insert() —– 向内容提供器中添加一条数据,使用方法咋在上面的代码中已经有介绍。

4 . update() —– 更新内容提供器中已有的数据。

5 . delete() —– 从内容提供器中删除数据

6 . getType() —– 根据传入的URI来返回响应的MIME类型。

2 . 分析内容URI。

标准的内容URI(以路径结尾,表示期望访问表中的所有数据):

content://com.example.app.provider/table

含有id的内容URI(以id结尾,表示访问表中拥有相应id的数据):

content://com.example.app.provider/table/1

为了下一步准确解析这两种内容URI,我们还必须使用通配符来匹配上面两种模式。

1 . * ——- 表示匹配任意长度的任意字符
2 . # ——- 表示匹配任意长度的数字

//匹配上面第一行内容URI(所有数据)content://com.example.app.provider/table/*//匹配上面第二种URI(相应id的数据)content://com.example.app.provider/table/#

3 . 借助于UriMatcher类实现匹配内容URI。

上面我们已经分析了内容URI,接下来我们借助于UriMatcher类提供的addURI()方法,分别依次传入三个参数(权限、路径,自定义代码(标识))。最后我们再调用UriMatcher的match()方法,传入一个Uri对象,就会返回我们刚刚传入的自定义代码(标识),我们以此来判断调用方想访问数据的意图。

public class MyProvider extends ContentProvider {  //自定义代码  public static final int TABLE1_DIR = 0;  public static final int TABLE1_ITEM = 1;  private static UriMatcher uriMatcher;  static {      uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);      //期望查询所有数据      uriMatcher.addURI("com.example.app.provider", "table1", TABLE1_DIR);      //期望查询相应id的数据      uriMatcher.addURI("com.example.app.provider", "table1/#", TABLE1_ITEM);  }  @Nullable  @Override  public Cursor query(Uri uri, String[] strings, String s, String[] strings1, String s1) {      switch (uriMatcher.match(uri)){          case TABLE1_DIR:              //查询所有数据              break;          case TABLE1_ITEM:              //查询单条数据              break;          default:              break;      }      return null;  }}

上面的代码只是简单演示了在query()方法中的重写,对于insert()、update()、delete()中的重写都是类似的,都是从Uri对象中获得自定义代码,得到访问数据者的意图后写相应的逻辑。

4 . 重写getType()方法。

getType()方法是所有内容提供器都必须提供的一个方法,用于获取Uri对象所对应的MIME类型,一个内容URI所对应的MIME类型字符串有三部分组成。Android做了如下规定:

1 . 必须以vnd开头

2 . 如果内容URI以路径结尾,则后面接android.cursor.dir/,如果内容URI以id结尾,则后接android.cursor.item/

3 . 最后接vnd.<权限>.<路径>

//内容URI以路径结尾vnd.android.cursor.dir/vnd.com.example.app.provider.table1//内容URI以id结尾vnd.android.cursor.item/vnd.com.example.app.provider.table1
@Nullable@Overridepublic String getType(Uri uri) {    switch (uriMatcher.match(uri)){        case TABLE1_DIR:            return "vnd.android.cursor.dir/vnd.com.example.app.provider.table1";        case TABLE1_ITEM:            return "vnd.android.cursor.item/vnd.com.example.app.provider.table1";    }    return null;}

从上面的内容我们也会发现,内容提供器之所以可以保证数据的安全性,是因为要想对数据进行增删改查,我们都首先需要匹配到内容URI格式才可以,只要我们不把隐私数据添加进UriMatcher中,我们的隐私数据就不会被访问到。

1 0