ContentProvider数据访问详解

ContentProvider数据访问详解

  Android官方指出的数据存储方式总共有五种:Shared Preferences、网络存储、文件存储、外储存储、SQLite,这些存储方式一般都只是在一个单独的应用程序中实现数据的共享,而对于需要操作其他应用程序中的数据时(如媒体库、通讯录等),可能就需要借助ContentProvider了。

1ContentProvider

  ContentProvider为存储和获取数据提供了统一的接口,使用表的形式来对数据进行封装,使得开发者在后续的开发过程中不用关心数据存储的细节。使用ContentProvider可以在不同的应用程序之间共享数据,Android为常见的数据类型提供了默认的ContentProvider(包括音频、视频、图片和通讯录等)。

  总的来说,利用ContentProvider来实现共享数据的好处是统一了数据的访问方式。

2URIUniform Resource Identifier

  URI为系统中的每一个资源赋予一个名字,比方说通话记录。每一个ContentProvider都拥有一个公共的URI,用于表示ContentProvider所提供的数据。 Android所提供的ContentProvider都位于android.provider包中, 可以将URI分为A、B、C、D 4个部分来理解。如对于content://com.wang.provider.myprovider/tablename/id:

  a、标准前缀——content://,用来说明一个Content Provider控制这些数据;

  b、URI的标识——com.wang.provider.myprovider,用于唯一标识这个ContentProvider,外部调用者可以根据这个标识来找到它。对于第三方应用程序,为了保证URI标识的唯一性,它必须是一个完整的、小写的类名。这个标识在元素的authorities属性中说明,一般是定义该ContentProvider的包.类的名称;

  c、路径——tablename,通俗的讲就是你要操作的数据库中表的名字,或者你也可以自己定义,记得在使用的时候保持一致就可以了;

  d、记录ID——id,如果URI中包含表示需要获取的记录的ID,则返回该id对应的数据,如果没有ID,就表示返回全部;

  对于第三部分路径(path)做进一步的解释,用来表示要操作的数据,构建时应根据实际项目需求而定。如:

a、操作tablename表中id为11的记录,构建路径:/tablename/11;

b、操作tablename表中id为11的记录的name字段:tablename/11/name;

c、操作tablename表中的所有记录:/tablename;

d、操作来自文件、xml或网络等其他存储方式的数据,如要操作xml文件中tablename节点下name字段:/ tablename/name;

e、若需要将一个字符串转换成Uri,可以使用Uri类中的parse()方法,如:

1 Uri uri = Uri.parse("content://com.wang.provider.myprovider/tablename");

3、UriMatcher

  Uri代表要操作的数据,在开发过程中对数据进行获取时需要解析Uri,Android提供了两个用于操作Uri的工具类,分别为UriMatcher和ContentUris 。掌握它们的基本概念和使用方法,对一个Android开发者来说是一项必要的技能。

  UriMatcher类用于匹配Uri,它的使用步骤如下:

  a、将需要匹配的Uri路径进行注册,代码如下:

1 //常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码
2 UriMatcher  sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
3 //如果match()方法匹配“content://com.wang.provider.myprovider/tablename”路径,返回匹配码为1
4 sMatcher.addURI("content://com.wang.provider.myprovider", " tablename ", 1);
5  //如果match()方法匹配content://com.wang.provider.myprovider/tablename/11路径,返回匹配码为2
6 sMatcher.addURI("com.wang.provider.myprovider", "tablename/#", 2);

  注意,添加第二个URI时,路径后面的id采用了通配符形式“#”,表示只要前面三个部分都匹配上了就OK。

  b、注册完需要匹配的Uri后,可以使用sMatcher.match(Uri)方法对输入的Uri进行匹配,如果匹配就返回对应的匹配码,匹配码为调用addURI()方法时传入的第三个参数。

 1 switch (sMatcher.match(Uri.parse("content://com.zhang.provider.yourprovider/tablename/100"))) {
 2    case 1:
 3      //match 1, todo something
 4      break;
 5    case 2
 6      //match 2, todo something
 7      break;
 8    default:
 9      //match nothing, todo something
10      break;
11 }

4ContentUris

  ContentUris类用于操作Uri路径后面的ID部分,它有两个比较实用的方法:withAppendedId(Uri uri, long id)和parseId(Uri uri)。

  withAppendedId(Uri uri, long id)用于为路径加上ID部分:

1 Uri uri = Uri.parse("content://com.wang.provider.myprovider/tablename");
2 Uri resultUri = ContentUris.withAppendedId(uri, 10);

parseId(Uri uri)则从路径中获取ID部分:

1 Uri uri = Uri.parse("content://com.zhang.provider.myprovider/tablename/10")
2 long personid = ContentUris.parseId(uri);

5ContentProvider数据共享

  ContentProvider类主要方法的介绍:

  public boolean onCreate(),在ContentProvider创建后就会被调用,而ContentProvider是在其它应用第一次访问它时被创建;

  public Uri insert(Uri uri, ContentValues values),供外部应用向ContentProvider添加数据;

  public int delete(Uri uri, String selection, String[] selectionArgs),供外部应用从ContentProvider删除数据;

  public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs),供外部应用更新ContentProvider中的数据;

  public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder),供外部应用从ContentProvider中获取数据;

  public String getType(Uri uri),返回当前Uri所代表数据的MIME类型;

  如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头,如要得到所有tablename记录的Uri为content://com.wang.provider.myprovider/tablename,那么返回的MIME类型字符串应该为:vnd.android.cursor.dir/table。

  如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头,如得到id为10的tablename记录,Uri为content://com.wang.provider.myprovider/tablename/10,那么返回的MIME类型字符串为:vnd.android.cursor.item/tablename 。

6、ContentResolver操作数据

  当外部应用需要对ContentProvider中的数据进行添加、删除、修改及查询操作时,可以使用ContentResolver 类来完成。而要获取ContentResolver 对象,可以使用Activity提供的getContentResolver()方法。

  ContentResolver 类提供了与ContentProvider类相同签名的四个方法:

  public Uri insert(Uri uri, ContentValues values),往ContentProvider添加数据;

  public int delete(Uri uri, String selection, String[] selectionArgs),从ContentProvider删除数据;

  public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs),更新ContentProvider中的数据;

  public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder),从ContentProvider中获取数据;

  这些方法的第一个参数为Uri,代表要操作的ContentProvider和对其中的什么数据进行操作,其实和ContentProvider里面的方法是一样的。他们所对应的数据,最终会被传到我们在之前程序里面定义的那个ContentProvider类的方法,假设给定的是:Uri.parse("content://com.wang.provider.myprovider/tablename/10"),那么将会对主机名为com.wang.provider.myprovider的ContentProvider进行操作,操作的数据为tablename表中id为10的记录。

使用ContentResolver对ContentProvider中的数据进行操作的代码如下:

 1 ContentResolver resolver = getContentResolver();
 2 Uri uri = Uri.parse("content://com.wang.provider.myprovider/tablename");
 3 //添加一条记录
 4 ContentValues values = new ContentValues();
 5 values.put("name", "wang1");
 6 values.put("age", 28);
 7 resolver.insert(uri, values);
 8 //获取tablename表中所有记录
 9 Cursor cursor = resolver.query(uri, null, null, null, "tablename data");
10 while(cursor.moveToNext()){
11    Log.i("ContentTest", "tablename_id="+ cursor.getInt(0)+ ", name="+ cursor.getString(1));
12 }
13 //把id为1的记录的name字段值更改新为zhang1
14 ContentValues updateValues = new ContentValues();
15 updateValues.put("name", "zhang1");
16 Uri updateIdUri = ContentUris.withAppendedId(uri, 2);
17 resolver.update(updateIdUri, updateValues, null, null);
18 //删除id为2的记录,即字段age
19 Uri deleteIdUri = ContentUris.withAppendedId(uri, 2);
20 resolver.delete(deleteIdUri, null, null);

7、监听数据变化

  如果ContentProvider的访问者需要知道数据发生的变化,可以在ContentProvider发生数据变化时调用getContentResolver().notifyChange(uri, null)来通知注册在此URI上的访问者。只给出类中监听部分的代码:

1 public class MyProvider extends ContentProvider {
2    public Uri insert(Uri uri, ContentValues values) {
3       db.insert("tablename", "tablenameid", values);
4       getContext().getContentResolver().notifyChange(uri, null);
5    }
6 }

  而访问者必须使用ContentObserver对数据(数据采用uri描述)进行监听,当监听到数据变化通知时,系统就会调用ContentObserver的onChange()方法:

 1 getContentResolver().registerContentObserver(Uri.parse("content://com.ljq.providers.personprovider/person"),
 2        true, new PersonObserver(new Handler()));
 3 public class PersonObserver extends ContentObserver{
 4    public PersonObserver(Handler handler) {
 5       super(handler);
 6    }
 7    public void onChange(boolean selfChange) {
 8       //to do something
 9    }
10 }

8、完整项目代码

  a、对于实际测试中的Uri——com.wang.provider.myprovider,需要在AndroidManifest.xml文件中进行说明:

1 <provider android:name="MyProvider" android:authorities="com.wang.provider.myprovider" />

  b、定义Profile类,其主要负责定义Uri各组成部分名称对应的String常量:

 1 package com.wang.testcontentprovider;
 2
 3 import android.net.Uri;
 4
 5 public class Profile {
 6
 7     /**
 8      * 表格名称
 9      */
10     public static final String TABLE_NAME = "tablename";
11
12     /**
13      * 列表一,_ID,自动增加
14      */
15     public static final String COLUMN_ID = "_id";
16
17     /**
18      * 列表二,名称
19      */
20     public static final String COLUMN_NAME = "name";
21
22     public static final String AUTOHORITY = "com.wang.provider.myprovider";
23     public static final int ITEM = 1;
24     public static final int ITEM_ID = 2;
25
26     public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.wang.tablename";
27     public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.wang.tablename";
28
29     public static final Uri CONTENT_URI = Uri.parse("content://" + AUTOHORITY + "/tablename");
30 }  

  c、实现存放数据的DBHelper类,继承自SQLiteOpenHelper类,完成数据库的一些常规操作:

 1 package com.wang.testcontentprovider;
 2
 3 import android.content.Context;
 4 import android.database.SQLException;
 5 import android.database.sqlite.SQLiteDatabase;
 6 import android.database.sqlite.SQLiteOpenHelper;
 7
 8 public class DBHelper extends SQLiteOpenHelper {
 9
10     /**
11      * 数据库名称
12      */
13     private static final String DATABASE_NAME = "test.db";
14
15     /**
16      * 数据库版本
17      */
18     private static final int DATABASE_VERSION = 1;
19
20     public DBHelper(Context context) {
21         super(context, DATABASE_NAME, null, DATABASE_VERSION);
22     }
23
24     @Override
25     public void onCreate(SQLiteDatabase db)  throws SQLException {
26         //创建表格
27         db.execSQL("CREATE TABLE IF NOT EXISTS "+ Profile.TABLE_NAME + "("+ Profile.COLUMN_ID +" INTEGER PRIMARY KEY AUTOINCREMENT," + Profile.COLUMN_NAME +" VARCHAR NOT NULL);");
28     }
29
30     @Override
31     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)  throws SQLException {
32         //删除并创建表格
33         db.execSQL("DROP TABLE IF EXISTS "+ Profile.TABLE_NAME+";");
34         onCreate(db);
35     }
36 }  

  d、MyProvider类的实现,主要负责向ContentProvider中添加数据

 1 package com.wang.testcontentprovider;
 2
 3 import android.content.ContentProvider;
 4 import android.content.ContentUris;
 5 import android.content.ContentValues;
 6 import android.content.UriMatcher;
 7 import android.database.Cursor;
 8 import android.database.SQLException;
 9 import android.database.sqlite.SQLiteDatabase;
10 import android.net.Uri;
11
12 public class MyProvider extends ContentProvider {
13
14     DBHelper mDbHelper = null;
15     SQLiteDatabase db = null;
16
17     private static final UriMatcher mMatcher;
18     static{
19         mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
20         mMatcher.addURI(Profile.AUTOHORITY,Profile.TABLE_NAME, Profile.ITEM);
21         mMatcher.addURI(Profile.AUTOHORITY, Profile.TABLE_NAME+"/#", Profile.ITEM_ID);
22     }
23
24     @Override
25     public int delete(Uri uri, String selection, String[] selectionArgs) {
26         // TODO Auto-generated method stub
27         return 0;
28     }
29
30     @Override
31     public String getType(Uri uri) {
32         switch (mMatcher.match(uri)) {
33         case Profile.ITEM:
34             return Profile.CONTENT_TYPE;
35         case Profile.ITEM_ID:
36             return Profile.CONTENT_ITEM_TYPE;
37         default:
38             throw new IllegalArgumentException("Unknown URI"+uri);
39         }
40     }
41
42     @Override
43     public Uri insert(Uri uri, ContentValues values) {
44         // TODO Auto-generated method stub
45         long rowId;
46         if(mMatcher.match(uri)!=Profile.ITEM){
47             throw new IllegalArgumentException("Unknown URI"+uri);
48         }
49         rowId = db.insert(Profile.TABLE_NAME,null,values);
50         if(rowId>0){
51             Uri noteUri=ContentUris.withAppendedId(Profile.CONTENT_URI, rowId);
52             getContext().getContentResolver().notifyChange(noteUri, null);
53             return noteUri;
54         }
55
56         throw new SQLException("Failed to insert row into " + uri);
57     }
58
59     @Override
60     public boolean onCreate() {
61         // TODO Auto-generated method stub
62         mDbHelper = new DBHelper(getContext());
63
64         db = mDbHelper.getReadableDatabase();
65
66         return true;
67     }
68
69     @Override
70     public Cursor query(Uri uri, String[] projection, String selection,
71             String[] selectionArgs, String sortOrder) {
72         // TODO Auto-generated method stub
73         Cursor c = null;
74         switch (mMatcher.match(uri)) {
75         case Profile.ITEM:
76             c =  db.query(Profile.TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
77             break;
78         case Profile.ITEM_ID:
79             c = db.query(Profile.TABLE_NAME, projection,Profile.COLUMN_ID + "="+uri.getLastPathSegment(), selectionArgs, null, null, sortOrder);
80             break;
81         default:
82             throw new IllegalArgumentException("Unknown URI"+uri);
83         }
84
85         c.setNotificationUri(getContext().getContentResolver(), uri);
86         return c;
87     }
88
89     @Override
90     public int update(Uri uri, ContentValues values, String selection,
91             String[] selectionArgs) {
92         // TODO Auto-generated method stub
93         return 0;
94     }
95
96 }

  e、作为主程序的MainActivity类,对ContentProvider中的数据进行获取,并在界面上进行简单的显示:

 1 package com.wang.testcontentprovider;
 2
 3 import android.support.v7.app.ActionBarActivity;
 4 import android.os.Bundle;
 5 import android.view.Menu;
 6 import android.view.MenuItem;
 7
 8 import android.app.ListActivity;
 9 import android.content.ContentResolver;
10 import android.content.ContentUris;
11 import android.content.ContentValues;
12 import android.database.Cursor;
13 import android.database.SQLException;
14 import android.database.sqlite.SQLiteDatabase;
15 import android.net.Uri;
16 import android.widget.SimpleCursorAdapter;
17
18 public class MainActivity extends ListActivity {
19     private SimpleCursorAdapter adapter= null;
20     private Cursor mCursor = null;
21     private ContentResolver mContentResolver = null;
22
23     @Override
24     public void onCreate(Bundle savedInstanceState) {
25         super.onCreate(savedInstanceState);
26         initData();
27         initAdapter();
28     }
29
30     public void initData(){
31         mContentResolver = getContentResolver();
32         for (int i = 0; i < 100; i++) {
33             ContentValues values = new ContentValues();
34             values.put(Profile.COLUMN_NAME, "Wang "+i);
35             mContentResolver.insert(Profile.CONTENT_URI, values);
36         }
37     }
38
39     public void initAdapter(){
40         mCursor = mContentResolver.query(Profile.CONTENT_URI, new String[]{Profile.COLUMN_ID,Profile.COLUMN_NAME}, null, null, null);
41
42         startManagingCursor(mCursor);
43
44         //设置adapter
45         adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2, mCursor, new String[]{Profile.COLUMN_ID,Profile.COLUMN_NAME}, new int[]{android.R.id.text1,android.R.id.text2});
46         setListAdapter(adapter);
47     }
48
49     @Override
50     public boolean onCreateOptionsMenu(Menu menu) {
51         getMenuInflater().inflate(R.menu.main, menu);
52         return true;
53     }
54
55 }  

9、结果分析

  先贴上一张不太美观的结果图:

  

时间: 2024-10-16 08:42:44

ContentProvider数据访问详解的相关文章

【Android基础】内容提供者ContentProvider的使用详解

1.什么是ContentProvider 首先,ContentProvider(内容提供者)是android中的四大组件之一,但是在一般的开发中,可能使用的比较少. ContentProvider为不同的软件之间数据共享,提供统一的接口.也就是说,如果我们想让其他的应用使用我们自己程序内的数据,就可以使用ContentProvider定义一个对外开放的接口,从而使得其他的应用可以使用咱们应用的文件.数据库内存储的信息.当然,自己开发的应用需要给其他应用共享信息的需求可能比较少见,但是在Andro

JVM 运行时数据区详解

一.运行时数据区: Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同数据区域. 1.有一些是随虚拟机的启动而创建,随虚拟机的退出而销毁,所有的线程共享这些数据区. 2.第二种则是与线程一一对应,随线程的开始和结束而创建和销毁,线程之间相互隔离. java虚拟机所管理的内存将会包括以下几个运行时数据区域 二.数据区详解 1.程序计数器(Program Counter Register) 也叫PC寄存器是一块较小的内存空间,它的作用是存储当前线程所执行的字节码的信号指示器.

分享《深度学习与计算机视觉算法原理框架应用》PDF《大数据架构详解从数据获取到深度学习》PDF +数据集

下载:https://pan.baidu.com/s/12-s95JrHek82tLRk3UQO_w 更多分享资料:https://www.cnblogs.com/javapythonstudy/ <深度学习与计算机视觉 算法原理.框架应用>PDF,带书签,347页.<大数据架构详解:从数据获取到深度学习>PDF,带书签,373页.配套源代码. <深度学习与计算机视觉 算法原理.框架应用>全书共13章,分为2篇,第1篇基础知识,第2篇实例精讲.用通俗易懂的文字表达公式背

分享《深度学习与计算机视觉算法原理框架应用》《大数据架构详解从数据获取到深度学习》PDF数据集

下载:https://pan.baidu.com/s/12-s95JrHek82tLRk3UQO_w 更多资料分享:http://blog.51cto.com/3215120 <深度学习与计算机视觉 算法原理.框架应用>PDF,带书签,347页.<大数据架构详解:从数据获取到深度学习>PDF,带书签,373页.配套源代码. <深度学习与计算机视觉 算法原理.框架应用>全书共13章,分为2篇,第1篇基础知识,第2篇实例精讲.用通俗易懂的文字表达公式背后的原理,实例部分提供

Rxjava2 Observable的数据过滤详解及实例(一)

目录 简要: 1. Debounce 1.1 debounce(timeout, unit) 1.2 debounce(debounceSelector) 2. Throttle 2.1 throttleFirst(windowDuration, unit) 2.2 throttleLast(intervalDuration, unit) 2.3 throttleWithTimeout(timeout, unit) 3. Sample 3.1 sample(period, unit) 3.2 s

Rxjava2 Observable的数据变换详解及实例(二)

目录 1. Window 1.1 window(closingSelector) 1.2 window(openingIndicator, closingIndicator) 1.3 window(count) 1.4 window(count, skip) 1.5 window(timespan, TimeUnit) 1.6 window(timespan, TimeUnit, count) 1.7 window(timespan, timeskip, TimeUnit) 2. GroupBy

Rxjava2 Observable的数据变换详解及实例(一)

目录 简要: 1.1 buffer(count) 1.2 buffer(boundary) 1.3 buffer(count, skip) 1.4 buffer(timespan, TimeUnit) 1.5 buffer(timespan, TimeUnit, count) 1.6 buffer(timespan, timeskip, TimeUnit) 1.7 buffer(bufferClosingSelector) 2. Map 3. FlatMap 3.1 flatMap(mapper

Rxjava2 Observable的数据过滤详解及实例(二)

目录 6. Filter 7. Frist 7.1 firstElement() 7.2 first(defaultItem) 7.3 firstOrError() 8. Single 8.1 singleElement() 8.2 single(defaultItem) 8.3 singleOrError() 9. ElementAt 9.1 elementAt(index) 9.2 elementAt(index, defaultItem) 9.3 elementAtOrError(inde

sas数据读取详解 四种读取数据方式以及数据指针的位置 、读取mess data的两个小工具、特殊的读取技巧、infile语句及其选项(dsd dlm missover truncover obs firstobs)、proc import、自定义缺失值

(The record length is the number of characters, including spaces, in a data line.) If your data lines are long, and it looks like SAS is not reading all your data, then use the LRECL= option in the INFILE statement to specify a record length at least