- ContentProvider的概述
由官方的定义我们可以得知它是一个管理访问结构化数据的机制。我们系统中有些数据很重要,不能让人随便访问,但是因为比较有价值,所以很多应用程序需要用到它,这是就可通过ContentProvider这个机制,压缩数据,提供安全定义、访问数据的机制。该机制提供一个借口,使得应用程序能从该进程访问另外一个应用程序的数据。
不仅是系统重要的数据,如果我们开发过程中有数据也是比较重要,但是需要提供给多个程序访问,这个时候也可以用到ContentProvider机制,把我们的数据的增删改查分装在ContentProvider的增删改查中,并增加相应的安全机制,使得用户可我们规定的安全机制下访问我们的数据。
ContentProvider还有一个重要的特点就是它是可以使得某些数据可以被跨进程访问,一般我们的数据库是不可跨进程被访问,因为数据库一般的数据是属于某个应用程序的,如果其他程序可以随意访问其数据库,这是很危险的,但是如果该应用程序的数据想分享给其他应用程序,那么就可以通过建立一个ContentProvider,规定一些安全机制,屏蔽一些比较重要的数据被访问,或是规定访问权限,比如只可读不可写等,使其他应用程序在有限制的前提下访问我们的数据。这样做就会起到对数据进行保护同时又能使得有用的数据能被分享的作用。
ContentProvider的使用方法:
一般我们如果有数据想被跨进程共享,就可以定义个ContentProvider,并在该ContentProvider定义访问该数据的方法,这些方法只允许外界传入一个URI和其他附加信息,该URI用于定位想操作数据的对象,而附加信息一般就是操作对象里具体数据的条件(例如SQL里面的where语句或者是SharedPreferences的键值对)。这样,就能做到对外界屏蔽了访问数据的方法,单纯开发URI和附加信息的接口,使得其他应用程序是在我们规定的机制下访问数据。这样既能做到跨进程数据共享,又能消除访问不同类型数据时访问方法的差异性,用户可不用管访问的数据类型是数据库或是其他类型而单纯用URI和附加信息定位操作数据对象,同时有提高了安全性。
第一步:首先我们需要先建一个SQLiteOpenHelper的子类:
第二步:有了数据源,我们接下来就得建立访问数据源的ContentProvider,建立ContentProvider的步骤通常分为:建立匹配器并添加匹配规则,重写各种操作数据的方法。添加匹配规则必需在所有方法和构造函数的执行前执行,因为有时候我们需要在匹配成功后才建立数据库的SQLiteOpenHelper。所以这里必须使用静态代码块添加匹配规则。而重写访问数据的方法主要有增删改查四种,重写的步骤基本遵循: 对Uri进行匹配 –> 获取读或写数据库对象 –> 根据匹配结果利用switch语句判断该执行哪种操作 –> 返回结果。
最后,ContentProvider也是安卓的四大组件之一,所以我们要在Manifest文件的application标签中对其进行注册。这样,一个内容提供者就建立好了。我们可以另外建立一个测试工程,对其进行跨进程访问,不过需要注意的是我们对其进行访问前必须确定该ContentProvider是存在的,所以必须先运行ContentProvider的工程,然后就可以使用测试工程对其进行访问了。这里需要注意的是,当我们把ContentProvider进程关闭后,我们还是可以对其数据进行访问的。
一个程序可以通过实现一个ContentProvider的抽象接口将自己的数据完全暴露出去,而且ContentProviders是以类似数据库中表的方式将数据暴露,也就是说ContentProvider就像一个“数据库”。那么外界获取其提供的数据,也就应该与从数据库中获取数据的操作基本一样,只不过是采用URI来表示外界需要访问的“数据库”。
ContentProvider向外界提供数据操作的接口:
query(Uri, String[], String, String[], String)
insert(Uri, ContentValues)
update(Uri, ContentValues, String, String[])
delete(Uri, String, String[])
ContentProvider 组织数据主要包括:存储数据,读取数据,以数据库的方式暴露数据。数据的存储需要根据设计的需求,选择合适的存储结构,首选数据库,当然也可以选择本地其他文件,甚至可以是网络上的数据。数据的读取,以数据库的方式暴露数据这就要求,无论数据是如何存储的,数据最后必须以数据的方式访问。
2.ContentResolver的使用方法:
首先,我们得了解如何通过ContentResolver来对系统或我们自定义的ContentProvider进行交互,通常,我们常用到的是访问系统的数据,常见的有通讯录、日历、短信、多媒体等。
使用方法归纳:
1、从当前Activity获取系统的ContentResolver;
2、使用ContentProvider的insert、delete、update、query方法对ContentProvider的内容进行增删改查;
3、如果是使用query是的到一个Cursor的结果集,通过该结果集可以获得我们查询的结果。
ContentResolver是通过URI来查询ContentProvider中提供的数据。除了URI以外,还必须知道需要获取的数据段的名称,以及此数据段的数据类型。如果你需要获取一个特定的记录,你就必须知道当前记录的ID,也就是URI中D部分。
其中ContentProvider负责
* 组织应用程序的数据;
* 向其他应用程序提供数据;
ContentResolver则负责
* 获取ContentProvider提供的数据;
* 修改/添加/删除更新数据等;
调用关系:
3.URI是网络资源的定义,将其分为A,B,C,D 4个部分:
A:标准前缀,用来说明一个Content Provider控制这些数据,无法改变的;”content://”
B:URI的标识,它定义了是哪个Content Provider提供这些数据。对于第三方应用程序,为了保证URI标识的唯一性,它必须是一个完整的、小写的 类名。这个标识在 元素的 authorities属性中说明:
C:路径,Content Provider使用这些路径来确定当前需要生什么类型的数据,URI中可能不包括路径,也可能包括多个;
D:如果URI中包含,表示需要获取的记录的ID;如果没有ID,就表示返回全部;
由于URI通常比较长,而且有时候容易出错,切难以理解。所以,在Android当中定义了一些辅助类,并且定义了一些常量来代替这些长字符串,例如:CallLog.Calls.CONTENT_URI
Android的Uri由以下三部分组成:”content://”、数据的路径、标示(可选)
短信的Uri共有一下几种:
content://sms/inbox//收件箱
content://sms/set//已发送
content://sms/draft//草稿
content://sms/outbox//发件箱(正在发送的信息)
content://sms/failed//发送失败
content:/sms/queued//待发送列表(比如开启飞行模式后,该短信就在待发送的列表里)
URI 解析:
1.UriMatcher
第一步,初始化:UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
第二步,注册需要的Uri:matcher.addURI(“sms”, “inbox”, _ID);
第三步,与已经注册的Uri进行匹配:
Uri uri = Uri.parse(“content://” + “sms” + “/inbox”);
int match = matcher.match(uri);
switch (match){
case _ID:
return "vnd.Android.cursor.dir/inbox";
default:
return null;
}
2.ContentUris
ContentUris 类用于获取Uri路径后面的ID部分
第一步,为路径加上ID
Uri uri = Uri.parse(“content://sms/inbox”)
通过withAppendedId方法,为该Uri加上ID
Uri resultUri = ContentUris.withAppendedId(uri, 10);
最后resultUri为:content://sms/inbox/10
第二步,从路径中获取ID
Uri uri = Uri.parse(“content://sms/inbox/10”)
long personid = ContentUris.parseId(uri);
最后personid 为 :10
如何在应用中创建一个数据库并提供访问接口:
//创建数据库
SQLiteDatabase db = openOrCreateDatabase(“test.db”, MODE_PRIVATE, null);
//创建表
db.execSQL(“create table test1 (_id integer, name varchar(20), age integer)”);
应用继承ContentProvider的类,需要在AndroidManifest.xml中注册
android:authorities="com.android.mifiservertest"></provider>
android:authorities=”com.andriud.mifiservertest”为其他应用访问提供uri,只有通过此uri其他应用才能访问到该应用中的数据
其他应用//取得ContentResolver对象
ContentResolver cr = getContentResolver();
//指定uri并指定操作哪个表
Uri uri = Uri.parse(“content://com.android.mifiservertest/test1”); //test1为表名
//执行query方法返回一个结果集
Cursor cs = cr.query(uri, null, null, null, null);
//遍历结果集,取出数据
while(cs.moveToNext()){ }
4.SQLite详解:
SQLite 数据库功能非常强大,使用起来也非常方便,SQLite数据库的一般操作包括:创建数据库、打开数据库、创建表、向表中添加数据、从表中删除数据、修改表中的数据、关闭数据库、删除指定表、删除数据库和查询表中的某条数据。
1.创建和打开数据库
创建一个名为”test.db”的数据库,并返回一个SQLiteDatabase对象 mSQLiteDatabase。
mSQLiteDatabase = this.openOrCreateDatabase(“test.db”, MODE_PRIVATE, null);
Android 中创建和打开一个数据库都可以使用openOrCreateDatabase 方法来实现,因为它会自动去检测是否存在这个数据库,如果存在则打开,不过不存在则创建一个数据库;创建成功则返回一个 SQLiteDatabase对象,否则抛出异常FileNotFoundException。
2.创建表
一个数据库中可以包含多个表,我们的每一条数据都保存在一个指定的表中,要创建表可以通过execSQL 方法来执行一条SQL语句。execSQL能够执行大部分的SQL语句,下面我们来创建一个名为table1 且包含3个字段的表。
String CREATE_TABLE = “CREATE TABLE table1 (_id INTEGER PRIMARY KEY, num INTEGER, data TEXT)”;
mSQLiteDatabase.execSQL(CREATE_TABLE);
3.向表中添加一条数据
可以使用insert 方法来添加数据,但是 insert 方法要求把数据都打包到 ContentValues 中, ContentValues 其实就是一个Map, key值是字段名称, Value值是字段的值。通过 ContentValues 的 put 方法就可以把数据放到ContentValues中,然后插入到表中去。
ContentValues cv = new ContentValues();
cv.put(TABLE_NUM, 1);
cv.put(TABLE_DATA, “测试数据”);
mSQLiteDatabase.insert(TABLE_NAME, null, cv);
//这样同样可以使用execSQL方法来执行一条“插入”的SQL语句,代码如下:
String INSERT_DATA = “INSERT INTO table1 (_id, num, data) values (1, 1, ‘通过SQL语句插入’)” ;
mSQLiteDatabase.execSQL(INSERT_DATA);
4.从表中删除数据
要删除数据可以使用delete 方法,例如删除字段 “_id” 等于1的数据
mSQLiteDatabase.delete(“test.db”, ” where_id=”+0, null);
通过 execSQL方法执行SQL语句删除数据如下:
String DELETE_DATA = “DELETE FROM table1 WHERE _id=1”;
mSQLiteDatabase.execSQL(DELETE_DATA);
5.修改表中的数据
如果添加了数据后发现数据有误,这时需要修改这个数据,可以使用updata方法来更新一条数据。例如修改 “num” 值为0的数据
ContentValues cv = new ContentValues();
cv.put(TABLE_NUM, 3);
cv.put(TABLE_DATA, “修改后的数据”);
mSQLiteDatabase.update(“table1” cv, “num ” + “=” + Integer.toString(0), null);
6.关闭数据库
直接使用SQLiteDatabase 的 close 方法
mSQLiteDatabase.close();
7.删除指定表
使用execSQL方法来实现
mSQLiteDatabase.execSQL(“DROP TABLE table1”);
8.删除数据库
使用deleteDatabase 方法
this.deleteDatabase(“test.db”);
9.查询表中的某条数据
在Android中查询数据是通过Cursor类来实现的,当我们使用SQLiteDatabase.query()方法时,会得到一个Cursor对象,Cursor指向的就是每一条数据。它提供了很多有关查询的方法,具体方法如下:
move 以当前的位置为参考,将Cursor移动到指定的位置,成功返回true, 失败返回false
moveToPosition 将Cursor移动到指定的位置,成功返回true,失败返回false
moveToNext 将Cursor向前移动一个位置,成功返回true,失败返回false
moveToLast 将Cursor向后移动一个位置,成功返回true,失败返回 false。
movetoFirst 将Cursor移动到第一行,成功返回true,失败返回false
isBeforeFirst 返回Cursor是否指向第一项数据之前
isAfterLast 返回Cursor是否指向最后一项数据之后
isClosed 返回Cursor是否关闭
isFirst 返回Cursor是否指向第一项数据
isLast 返回Cursor是否指向最后一项数据
isNull 返回指定位置的值是否为null
getCount 返回总的数据项数
getInt 返回当前行中指定的索引数据
Cursor cur = mSQLiteDatabase.rawQuery(“SELECT * FROM table”, null);
if( cur != null ){
if( cur.moveToFirst() ){
do{
int numColumn = cur.getColumnIndex(“num”);
int num = cur.getInt(numColumn);
}while( cur.moveToNext());
}
}
短信的 ContentProvider 源码在什么地方:
/packages/providers/TelephonyProvider/src/com/android/providers/telephony/SmsProvider.java
短信数据库名字: mmssms.db
存储位置: /data/data/com.android.providers.telephony/databases/mmssms.db