android四大组件--ContentProvider详解

1、相关ContentProvider概念解析: html

一、ContentProvider简介
在Android官方指出的Android的数据存储方式总共有五种,分别是:Shared Preferences、网络存储、文件存储、外储存储、SQLite。可是咱们知道通常这些存储都只是在单独的一个应用程序之中达到一个数据的共享,有时候咱们须要操做其余应用程序的一些数据,例如咱们须要操做系统里的媒体库、通信录等,这时咱们就可能经过ContentProvider来知足咱们的需求了。 java

二、为何要选择ContentProvider? android

ContentProvider向咱们提供了咱们在应用程序以前共享数据的一种机制,而咱们知道每个应用程序都是运行在不一样的应用程序的,数据和文件在不一样应用程序之间达到数据的共享不是没有可能,而是显得比较复杂,而正好Android中的ContentProvider则达到了这一需求,好比有时候咱们须要操做手机里的联系人,手机里的多媒体等一些信息,咱们均可以用到这个ContentProvider来达到咱们所需。 sql

1)、ContentProvider为存储和获取数据提供了统一的接口。ContentProvide对数据进行封装,不用关心数据存储的细节。使用表的形式来组织数据。
2)、使用ContentProvider能够在不一样的应用程序之间共享数据。 
3)、Android为常见的一些数据提供了默认的ContentProvider(包括音频、视频、图片和通信录等)。 
总的来讲使用ContentProvider对外共享数据的好处是统一了数据的访问方式。 数据库

三、Uri介绍 网络

为系统的每个资源给其一个名字,比方说通话记录。
1)、每个ContentProvider都拥有一个公共的URI,这个URI用于表示这个ContentProvider所提供的数据。 
2)、Android所提供的ContentProvider都存放在android.provider包中。 将其分为A,B,C,D 4个部分:

\
A:标准前缀,用来讲明一个Content Provider控制这些数据,没法改变的;"content://"
B:URI 的标识,用于惟一标识这个ContentProvider,外部调用者能够根据这个标识来找到它。它定义了是哪一个Content Provider提供这些数据。对于第三方应用程序,为了保证URI标识的惟一性,它必须是一个完整的、小写的类名。这个标识在 元素的 authorities属性中说明:通常是定义该ContentProvider的包.类的名称
C:路径(path),通俗的讲就是你要操做的数据库中表的名字,或者你也能够本身定义,记得在使用的时候保持一致就能够了;"content://com.bing.provider.myprovider/tablename"
D:若是URI中包含表示须要获取的记录的ID;则就返回该id对应的数据,若是没有ID,就表示返回所有; "content://com.bing.provider.myprovider/tablename/#" #表示数据id。 app

PS: dom

路径(path)能够用来表示咱们要操做的数据,路径的构建应根据业务而定,以下:
一、要操做person表中id为10的记录,能够构建这样的路径:/person/10
二、要操做person表中id为10的记录的name字段, person/10/name
三、要操做person表中的全部记录,能够构建这样的路径:/person
四、要操做xxx表中的记录,能够构建这样的路径:/xxx
五、固然要操做的数据不必定来自数据库,也能够是文件、xml或网络等其余存储方式,以下:
要操做xml文件中person节点下的name节点,能够构建这样的路径:/person/name
六、若是要把一个字符串转换成Uri,可使用Uri类中的parse()方法,以下:Uri uri = Uri.parse("content://com.bing.provider.personprovider/person") ide

四、UriMatcher类使用介绍 函数

由于Uri表明了要操做的数据,因此咱们常常须要解析Uri,并从Uri中获取数据。Android系统提供了两个用于操做Uri的工具类,分别为UriMatcher和ContentUris 。掌握它们的使用,会便于咱们的开发工做。
UriMatcher类用于匹配Uri,它的用法以下:
首先第一步把你须要匹配Uri路径所有给注册上,以下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码
UriMatcher  sMatcher =newUriMatcher(UriMatcher.NO_MATCH);
//若是match()方法匹配 content://com.bing.procvide.personprovider/person路径,返回匹配码为1
sMatcher.addURI("com.bing.procvide.personprovider","person",1);//添加须要匹配uri,若是匹配就会返回匹配码
//若是match()方法匹配 content://com.bing.provider.personprovider/person/230路径,返回匹配码为2
sMatcher.addURI("com.bing.provider.personprovider","person/#",2);//#号为通配符
switch(sMatcher.match(Uri.parse(" content://com.ljq.provider.personprovider/person/10"))) {
   case1
     break;
   case2
     break;
   default://不匹配
     break;
}


注册完须要匹配的Uri后,就可使用sMatcher.match(uri)方法对输入的Uri进行匹配,若是匹配就返回匹配码,匹配码是调用addURI()方法传入的第三个参数,假设匹配content://com.ljq.provider.personprovider/person路径,返回的匹配码为1 


五、ContentUris类使用介绍

ContentUris类用于操做Uri路径后面的ID部分,它有两个比较实用的方法:
withAppendedId(uri, id)用于为路径加上ID部分:

?
1
2
3
Uri resultUri = ContentUris.withAppendedId(uri,10);
parseId(uri)方法用于从路径中获取ID部分:
?
1
2
longpersonid = ContentUris.parseId(uri);//获取的结果为:10

六、使用ContentProvider共享数据

1)ContentProvider类主要方法的做用:
public boolean onCreate():该方法在ContentProvider建立后就会被调用,Android开机后,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):该方法用于返回当前Url所表明数据的MIME类型。

2)若是操做的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头,

例如:要获得全部person记录的Uri为content://com.bing.provider.personprovider/person,那么返回的MIME类型字符串应该为:"vnd.android.cursor.dir/person"。

3)若是要操做的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头,

例如:获得id为10的person记录,Uri为content://com.bing.provider.personprovider/person/10,那么返回的MIME类型字符串为:"vnd.android.cursor.item/person"。

七、ContentResolver操做ContentProvider中的数据

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

2)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.bing.providers.personprovider/person/10"),那么将会对主机名为com.bing.providers.personprovider的ContentProvider进行操做,操做的数据为person表中id为10的记录。

使用ContentResolver对ContentProvider中的数据进行添加、删除、修改和查询操做:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ContentResolver resolver =  getContentResolver();
//添加一条记录
ContentValues values =newContentValues();
values.put("name","bingxin");
values.put("age",25);
resolver.insert(uri, values); 
//获取person表中全部记录
Cursor cursor = resolver.query(uri,null,null,null,"personid desc");
while(cursor.moveToNext()){
   Log.i("ContentTest","personid="+ cursor.getInt(0)+",name="+ cursor.getString(1));
}
//把id为1的记录的name字段值更改新为zhangsan
ContentValues updateValues =newContentValues();
updateValues.put("name","zhangsan");
Uri updateIdUri = ContentUris.withAppendedId(uri,2);
resolver.update(updateIdUri, updateValues,null,null);
//删除id为2的记录
Uri deleteIdUri = ContentUris.withAppendedId(uri,2);
resolver.delete(deleteIdUri,null,null);

八、监听ContentProvider中数据的变化

若是ContentProvider的访问者须要知道ContentProvider中的数据发生变化,能够在ContentProvider发生数据变化时调用getContentResolver().notifyChange(uri, null)来通知注册在此URI上的访问者,例子以下:
?
1
2
3
4
5
6
publicclassPersonContentProviderextendsContentProvider {
   publicUri insert(Uri uri, ContentValues values) {
      db.insert("person","personid", values);
      getContext().getContentResolver().notifyChange(uri,null);
   }
}

若是ContentProvider的访问者须要获得数据变化通知,必须使用ContentObserver对数据(数据采用uri描述)进行监听,当监听到数据变化通知时,系统就会调用ContentObserver的onChange()方法:
?
1
2
3
4
5
6
7
8
9
10
getContentResolver().registerContentObserver(Uri.parse(" content://com.ljq.providers.personprovider/person"),
       true,newPersonObserver(newHandler()));
publicclassPersonObserverextendsContentObserver{
   publicPersonObserver(Handler handler) {
      super(handler);
   }
   publicvoidonChange(booleanselfChange) {
      //此处能够进行相应的业务处理
   }
}


2、ContentProvider的实现过程

一、定义一个CONTENT_URI常量,提供了访问ContentProvider的标识符。 

?
1
publicstaticfinalUri CONTENT_URI = Uri.parse(" content://com.example.codelab.transportationprovider");

其中:content是协议
Com.exmaple.codelab.transportationprovider是类名,包含完整的包名。
Uri.parse将一个字符串转换成Uri类型。
若是Provider包含子表,一样定义包含字表的CONTENT_URI。
content://com.example.codelab.transportationprovider/train
content://com.example.codelab.transportationprovider/air/domestic
content://com.example.codelab.transportationprovider/air/international
而后定义列,确保里面包含一个_id的列。
二、定义一个类,继承ContentProvider。 
?
1
publicclassFirstContentProviderextendsContentProvider

先介绍一下ContentProvider用到的UriMatcher。UriMatcher的一个重要的函数是match(Uri uri)。这个函数能够匹配Uri,根据传入的不一样Uri返回不一样的自定义整形值,以代表Uri访问的不一样资源的类型。
例如:
?
1
2
3
4
5
6
publicstaticfinalUriMatcher uriMatcher;
      static{
                     uriMatcher =newUriMatcher(UriMatcher.NO_MATCH);
                     uriMatcher.addURI(Book.AUTHORITY,"item", Book.ITEM);
                     uriMatcher.addURI(Book.AUTHORITY,"item/#", Book.ITEM_ID);
              }

这里UriMatcher类型的静态字段是用来匹配传入到ContentProvider中的Uri的类。其构造方法传入的匹配码是使用match()方法匹配根路径时返回的值,这个匹配码能够为一个大于零的数表示匹配根路径或传入-1,即常量UriMatcher.NO_MATCH表示不匹配根路径。

addURI()方法是用来增长其余URI匹配路径的,

第一个参数传入标识ContentProvider的AUTHORITY字符串。

第二个参数传入须要匹配的路径,这里的#号为通配符,表明匹配任意数字,另外还能够用*来匹配任意文本。

第三个参数必须传入一个大于零的匹配码,用于match()方法对相匹配的URI返回相对应的匹配码。 例如:sMatcher.addURI(“com.test.provider.personprovider”, “person”, 1);若是match()方法匹配content://com.test.provider.personprovider/person路径,返回匹配码为1。

三、实现query,insert,update,delete,getType和onCreate方法。 
四、在AndroidManifest.xml当中进行声明。

?
1
<provider android:name="com.bj.FirstContentProvider"android:authorities="com.bj.firstcontentprovider"></provider>


3、实例

一、常量类

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
 * 提供ContentProvider对外的各类常量,当外部数据须要访问的时候,就能够参考这些常量操做数据。
 * @author HB
 *
 */
publicclassContentData {
    publicstaticfinalString AUTHORITY ="hb.android.contentProvider";
    publicstaticfinalString DATABASE_NAME ="teacher.db";
    //建立 数据库的时候,都必须加上版本信息;而且必须大于4
    publicstaticfinalintDATABASE_VERSION =4;
    publicstaticfinalString USERS_TABLE_NAME ="teacher";
     
    publicstaticfinalclassUserTableDataimplementsBaseColumns {
        publicstaticfinalString TABLE_NAME ="teacher";
        //Uri,外部程序须要访问就是经过这个Uri访问的,这个Uri必须的惟一的。
        publicstaticfinalUri CONTENT_URI = Uri.parse(" content://"+ AUTHORITY +"/teacher");
        // 数据集的MIME类型字符串则应该以vnd.android.cursor.dir/开头 
        publicstaticfinalString CONTENT_TYPE ="vnd.android.cursor.dir/hb.android.teachers";
        // 单一数据的MIME类型字符串应该以vnd.android.cursor.item/开头 
        publicstaticfinalString CONTENT_TYPE_ITME ="vnd.android.cursor.item/hb.android.teacher";
        /* 自定义匹配码 */ 
        publicstaticfinalintTEACHERS =1; 
        /* 自定义匹配码 */ 
        publicstaticfinalintTEACHER =2; 
         
        publicstaticfinalString TITLE ="title";
        publicstaticfinalString NAME ="name";
        publicstaticfinalString DATE_ADDED ="date_added";
        publicstaticfinalString SEX ="SEX";
        publicstaticfinalString DEFAULT_SORT_ORDER ="_id desc";
         
        publicstaticfinalUriMatcher uriMatcher; 
        static{ 
            // 常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码 
            uriMatcher =newUriMatcher(UriMatcher.NO_MATCH); 
            // 若是match()方法匹配 content://hb.android.teacherProvider/teachern路径,返回匹配码为TEACHERS 
            uriMatcher.addURI(ContentData.AUTHORITY,"teacher", TEACHERS); 
            // 若是match()方法匹配 content://hb.android.teacherProvider/teacher/230,路径,返回匹配码为TEACHER 
            uriMatcher.addURI(ContentData.AUTHORITY,"teacher/#", TEACHER); 
        }
    }
}

PS:

在建立UriMatcher对象uriMatcher时,咱们传给构造函数的参数为UriMatcher.NO_MATCH,它表示当uriMatcher不能匹配指定的URI时,就返回代码UriMatcher.NO_MATCH。接下来增长了三个匹配规则,分别是uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(ContentData.AUTHORITY, "teacher", TEACHERS); uriMatcher.addURI(ContentData.AUTHORITY, "teacher/#", TEACHER);

它们的匹配码分别是teacher.ITEM、teacher.ITEM_ID和teacher.ITEM_POS,其中,符号#表示匹配任何数字。

二、SQLite操做类DBOpenHelper

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
 * 这个类继承SQLiteOpenHelper抽象类,用于建立数据库和表。建立数据库是调用它的父类构造方法建立。
 * @author HB
 */
publicclassDBOpenHelperextendsSQLiteOpenHelper {
 
    // 在SQLiteOepnHelper的子类当中,必须有该构造函数,用来建立一个数据库;
    publicDBOpenHelper(Context context, String name, CursorFactory factory,
            intversion) {
        // 必须经过super调用父类当中的构造函数
        super(context, name, factory, version);
        // TODO Auto-generated constructor stub
    }
 
    // public DBOpenHelper(Context context, String name) {
    // this(context, name, VERSION);
    // }
 
    publicDBOpenHelper(Context context, String name,intversion) {
        this(context, name,null, version);
    }
     
    /**
     * 只有当数据库执行建立 的时候,才会执行这个方法。若是更改表名,也不会建立,只有当建立数据库的时候,才会建立改表名以后 的数据表
     */
    @Override
    publicvoidonCreate(SQLiteDatabase db) {
System.out.println("create table");
        db.execSQL("create table "+ ContentData.UserTableData.TABLE_NAME
                +"("+ ContentData.UserTableData._ID
                +" INTEGER PRIMARY KEY autoincrement,"
                + ContentData.UserTableData.NAME +" varchar(20),"
                + ContentData.UserTableData.TITLE +" varchar(20),"
                + ContentData.UserTableData.DATE_ADDED +" long,"
                + ContentData.UserTableData.SEX +" boolean)"+";");
    }
 
    @Override
    publicvoidonUpgrade(SQLiteDatabase db,intoldVersion,intnewVersion) {
 
    }
 
}

三、内容提供者代码
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/**
 * 这个类给外部程序提供访问内部数据的一个接口
 * @author HB
 *
 */
publicclassTeacherContentProviderextendsContentProvider {
     
    privateDBOpenHelper dbOpenHelper =null; 
    // UriMatcher类用来匹配Uri,使用match()方法匹配路径时返回匹配码 
       
       
    /**
     * 是一个回调函数,在ContentProvider建立的时候,就会运行,第二个参数为指定数据库名称,若是不指定,就会找不到数据库;
     * 若是数据库存在的状况下是不会再建立一个数据库的。(固然首次调用 在这里也不会生成数据库必须调用SQLiteDatabase的 getWritableDatabase,getReadableDatabase两个方法中的一个才会建立数据库)
     */
    @Override 
    publicbooleanonCreate() {
        //这里会调用 DBOpenHelper的构造函数建立一个数据库;
        dbOpenHelper =newDBOpenHelper(this.getContext(), ContentData.DATABASE_NAME, ContentData.DATABASE_VERSION);
        returntrue; 
    } 
    /**
     * 当执行这个方法的时候,若是没有数据库,他会建立,同时也会建立表,可是若是没有表,下面在执行insert的时候就会出错
     * 这里的插入数据也彻底能够用sql语句书写,而后调用 db.execSQL(sql)执行。
     */
    @Override 
    publicUri insert(Uri uri, ContentValues values){ 
        //得到一个可写的数据库引用,若是数据库不存在,则根据onCreate的方法里建立;
        SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); 
        longid =0; 
         
        switch(uriMatcher.match(uri)) { 
        caseTEACHERS: 
            id = db.insert("teacher",null, values);   // 返回的是记录的行号,主键为int,实际上就是主键值 
            returnContentUris.withAppendedId(uri, id); 
        caseTEACHER: 
            id = db.insert("teacher",null, values);
            String path = uri.toString(); 
            returnUri.parse(path.substring(0, path.lastIndexOf("/"))+id);// 替换掉id 
        default: 
            thrownewIllegalArgumentException("Unknown URI "+ uri); 
        }
    } 
       
    @Override 
    publicintdelete(Uri uri, String selection, String[] selectionArgs) { 
        SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); 
        intcount =0; 
        switch(uriMatcher.match(uri)) { 
        caseTEACHERS: 
            count = db.delete("teacher", selection, selectionArgs); 
            break; 
        caseTEACHER: 
            // 下面的方法用于从URI中解析出id,对这样的路径 content://hb.android.teacherProvider/teacher/10 
            // 进行解析,返回值为10 
            longpersonid = ContentUris.parseId(uri); 
            String where ="_ID="+ personid;  // 删除指定id的记录 
            where += !TextUtils.isEmpty(selection) ?" and ("+ selection +")":"";  // 把其它条件附加上 
            count = db.delete("teacher", where, selectionArgs); 
            break; 
        default: 
            thrownewIllegalArgumentException("Unknown URI "+ uri); 
        } 
        db.close(); 
        returncount; 
    } 
   
    @Override 
    publicintupdate(Uri uri, ContentValues values, String selection, 
            String[] selectionArgs) { 
        SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); 
        intcount =0; 
        switch(uriMatcher.match(uri)) { 
        caseTEACHERS: 
            count = db.update("teacher", values, selection, selectionArgs); 
            break; 
        caseTEACHER: 
            // 下面的方法用于从URI中解析出id,对这样的路径 content://com.ljq.provider.personprovider/person/10 
            // 进行解析,返回值为10 
            longpersonid = ContentUris.parseId(uri); 
            String where ="_ID="+ personid;// 获取指定id的记录 
            where += !TextUtils.isEmpty(selection) ?" and ("+ selection +")":"";// 把其它条件附加上 
            count = db.update("teacher", values, where, selectionArgs); 
            break; 
        default: 
            thrownewIllegalArgumentException("Unknown URI "+ uri); 
        } 
        db.close(); 
        returncount; 
    } 
       
    @Override 
    publicString getType(Uri uri) { 
        switch(uriMatcher.match(uri)) { 
        caseTEACHERS: 
            returnCONTENT_TYPE; 
        caseTEACHER: 
            returnCONTENT_TYPE_ITME; 
        default: 
            thrownewIllegalArgumentException("Unknown URI "+ uri); 
        } 
    } 
   
    @Override 
    publicCursor query(Uri uri, String[] projection, String selection, 
            String[] selectionArgs, String sortOrder) { 
        SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); 
        switch(uriMatcher.match(uri)) { 
        caseTEACHERS: 
            returndb.query("teacher", projection, selection, selectionArgs,null,null, sortOrder); 
        caseTEACHER: 
            // 进行解析,返回值为10 
            longpersonid = ContentUris.parseId(uri); 
            String where ="_ID="+ personid;// 获取指定id的记录 
            where += !TextUtils.isEmpty(selection) ?" and ("+ selection +")":"";// 把其它条件附加上 
            returndb.query("teacher", projection, where, selectionArgs,null,null, sortOrder); 
        default: 
            thrownewIllegalArgumentException("Unknown URI "+ uri); 
        } 
    } 
}

PS:

一、这里咱们在ArticlesProvider类的内部中定义了一个DBHelper类,它继承于SQLiteOpenHelper类,它用是用辅助咱们操做数据库的。使用这个DBHelper类来辅助操做数据库的好处是只有当咱们第一次对数据库时行操做时,系统才会执行打开数据库文件的操做。拿咱们这个例子来讲,只有第三方应用程序第一次调用query、insert、update或者delete函数来操做数据库时,咱们才会真正去打开相应的数据库文件。这样在onCreate函数里,就不用执行打开数据库的操做,由于这是一个耗时的操做,而在onCreate函数中,要避免执行这些耗时的操做。

二、咱们在实现本身的Content Provider时,必须继承于ContentProvider类,而且实现如下六个函数:

-- onCreate(),用来执行一些初始化的工做。

-- query(Uri, String[], String, String[], String),用来返回数据给调用者。

-- insert(Uri, ContentValues),用来插入新的数据。

-- update(Uri, ContentValues, String, String[]),用来更新已有的数据。

-- delete(Uri, String, String[]),用来删除数据。

-- getType(Uri),用来返回数据的MIME类型。

四、manifest

?
1
2
3
4
5
6
7
8
9
10
11
12
13
<!--?xml version="1.0"encoding="utf-8"?-->
<manifest xmlns:android=" http://schemas.android.com/apk/res/android"package="hb.android.contentProvider"android:versioncode="1"android:versionname="1.0">
    <uses-sdk android:minsdkversion="8">
     
         
            <intent-filter>
                 
                <category android:name="android.intent.category.LAUNCHER">
            </category></action></intent-filter>
        </activity>
<provider android:name=".TeacherContentProvider"android:authorities="hb.android.contentProvider"android:multiprocess="false">
    </provider></application>
</uses-sdk></manifest>


PS:

在配置Content Provider的时候,最重要的就是要指定它的authorities属性了,只有配置了这个属性,第三方应用程序才能经过它来找到这个Content Provider。这要须要注意的,这里配置的authorities属性的值是和咱们前面在Articles.java文件中定义的AUTHORITY常量的值是一致的。另一个属性multiprocess是一个布尔值,它表示这个Content Provider是否能够在每一个客户进程中建立一个实例,这样作的目的是为了减小进程间通讯的开销。这里咱们为了减小没必要要的内存开销,把属性multiprocess的值设置为false,使得系统只能有一个Content Provider实例存在,它运行在本身的进程中。在这个配置文件里面,咱们还能够设置这个Content Provider的访问权限,这里咱们为了简单起见,就不设置权限了。
六、布局文件

?
1
2
3
4
5
6
7
8
9
<!--?xml version="1.0"encoding="utf-8"?-->
<linearlayout xmlns:android=" http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="fill_parent"android:layout_height="fill_parent">
<button android:id="@+id/insert"android:text="@string/insert"android:layout_width="fill_parent"android:layout_height="wrap_content">
</button><button android:id="@+id/query"android:text="@string/query"android:layout_width="fill_parent"android:layout_height="wrap_content">
</button><button android:id="@+id/querys"android:text="@string/querys"android:layout_width="fill_parent"android:layout_height="wrap_content">
</button><button android:id="@+id/update"android:text="@string/update"android:layout_width="fill_parent"android:layout_height="wrap_content">
</button><button android:id="@+id/delete"android:text="@string/delete"android:layout_width="fill_parent"android:layout_height="wrap_content">
 
</button></linearlayout>


七、activity
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
packagehb.android.contentProvider;
 
importjava.util.Date;
 
importandroid.app.Activity;
importandroid.content.ContentResolver;
importandroid.content.ContentValues;
importandroid.database.Cursor;
importandroid.net.Uri;
importandroid.os.Bundle;
importandroid.view.View;
importandroid.view.View.OnClickListener;
importandroid.widget.Button;
 
/**
 * 这个类用来测试ContentProvider是否可用。经过 给定的uri访问,数据库;
 *
 * @author HB
 *
 */
publicclassTeacherActivityextendsActivity {
    Button insert;
    Button query;
    Button update;
    Button delete;
    Button querys;
    Uri uri = Uri.parse(" content://hb.android.contentProvider/teacher");
 
    /** Called when the activity is first created. */
    @Override
    publicvoidonCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
 
        insert = (Button) findViewById(R.id.insert);
        query = (Button) findViewById(R.id.query);
        update = (Button) findViewById(R.id.update);
        delete = (Button) findViewById(R.id.delete);
        querys = (Button) findViewById(R.id.querys);
        // 绑定监听器的两种方法一;
        insert.setOnClickListener(newInsertListener());
        query.setOnClickListener(newQueryListener());
        // 方法二
        update.setOnClickListener(newOnClickListener() {
            publicvoidonClick(View v) {
                // TODO Auto-generated method stub
                ContentResolver cr = getContentResolver();
                ContentValues cv =newContentValues();
                cv.put("name","huangbiao");
                cv.put("date_added", (newDate()).toString());
                inturi2 = cr.update(uri, cv,"_ID=?",newString[]{"3"});
System.out.println("updated"+":"+uri2);
            }
        });
 
        delete.setOnClickListener(newOnClickListener() {
             
            publicvoidonClick(View v) {
                ContentResolver cr = getContentResolver();
                cr.delete(uri,"_ID=?",newString[]{"2"});
            }
        });
 
        querys.setOnClickListener(newOnClickListener() {
 
            publicvoidonClick(View v) {
                // TODO Auto-generated method stub
                ContentResolver cr = getContentResolver();
                // 查找id为1的数据
                Cursor c = cr.query(uri,null,null,null,null);
                System.out.println(c.getCount());
                c.close();
            }
        });
    }
 
    classInsertListenerimplementsOnClickListener {
 
        publicvoidonClick(View v) {
            // TODO Auto-generated method stub
            ContentResolver cr = getContentResolver();
 
            ContentValues cv =newContentValues();
            cv.put("title","jiaoshou");
            cv.put("name","jiaoshi");
            cv.put("sex",true);
            Uri uri2 = cr.insert(uri, cv);
            System.out.println(uri2.toString());
        }
 
    }
 
    classQueryListenerimplementsOnClickListener {
 
        publicvoidonClick(View v) {
            // TODO Auto-generated method stub
            ContentResolver cr = getContentResolver();
            // 查找id为1的数据
            Cursor c = cr.query(uri,null,"_ID=?",newString[] {"1"},null);
            //这里必需要调用 c.moveToFirst将游标移动到第一条数据,否则会出现index -1 requested , with a size of 1错误;cr.query返回的是一个结果集。
            if(c.moveToFirst() ==false) {
                // 为空的Cursor
                return;
            }
            intname = c.getColumnIndex("name");
            System.out.println(c.getString(name));
            c.close();
        }
    }
}

最终的效果以下:

n峨�
夗z{O畫鈛觊 wk�'Z喎�组件Content Provider中的数据更新通知机制和Android系统中的广播(Broadcast)通知机制的实现思路是类似的。

在Android的广播机制中,首先是接收者对本身感兴趣的广播进行注册,接着当发送者发出这些广播时,接收者就会获得通知了。更多关于Android系统的广播机制的知识,能够参考前面Android四大组件--Broadcast Receiver详解这一文章。

然而,Content Provider中的数据监控机制与Android系统中的广播机制又有三个主要的区别,

一是前者是经过URI来把通知的发送者和接收者关联在一块儿的,然后者是经过Intent来关联的,

二是前者的通知注册中心是由ContentService服务来扮演的,然后者是由ActivityManagerService服务来扮演的,

三是前者负责接收数据更新通知的类必需要继承ContentObserver类,然后者要继承BroadcastReceiver类。

之因此会有这些区别,是因为Content Proivder组件的数据共享功能自己就是创建在URI的基础之上的,所以专门针对URI来设计另一套通知机制会更实用和方便,而Android系统的广播机制是一种更加通用的事件通知机制,它的适用范围会更普遍一些。

相关文章
相关标签/搜索