一、ContentProvider概念
ContentProvider提供了在android系统内共享数据的接口,可以在不同app之间进行数据共享。例如联系人,短信记录,通话记录都实现了ContentProvider接口,使其他APP可以访问到这些数据(必须获得权限)。
当应用集成了ContentProvider类,通过重写其方法,即可使该应用内的数据公开出去。ContentProvider共享数据的好处是统一了数据访问方式。
二、Uri简介
Uri(Uniform Resource Identifier )统一资源标识符。是用来标识 某一互联网资源名称的字符串,通过这个字符串可以访问到该资源。举个例子: 访问百度需要在网址栏输入http://www.baidu.com,这个"http://www.baidu.com"就是一种Uri,打开电脑里的一张图片,其地址可能是"D:/image/1.jpg",这个字符串也是一种Uri。 通过这两个例子,可以看出,Uri是电脑里的地址,通过地址可以找到目标文件的“家”。既然是“统一”,就有其规定的格式。需要符合当前的RFC4395规范:协议名称://域名.根域名/目录/文件名.后缀。“http”和"https"都是协议的一种。在android系统里,会用到"content"协议。
如果要把一个字符串转换成Uri,可以使用Uri类中的parse()方法,如下:(包名.类名.方法名)一定要加上"content://"。
<span style="font-size:18px;"><span style="font-size:18px;">Uri uri = Uri.parse("content://com.example.provider.contactprovider")</span></span>
因为Uri代表了要操作的数据,所以我们很经常需要解析Uri,并从Uri中获取数据。Android系统提供了两个用于操作Uri的工具类,分别为UriMatcher和ContentUris 。掌握它们的使用,会便于我们的开发工作。下面会详细讲解。
三、UriMatcher
从字面上看,是用来匹配Uri字符串的的类。对UriMatcher进行实例化:<span style="font-size:18px;">UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);</span>
其中UriMatcher.NO_MATCH参数的意思是,不对匹配结果的返回值(譬如匹配成功返回1,不成功返回2)进行再次匹配,只对Uri字符串本身进行匹对。
如果需要匹配的Uri字符串为:String uriString= “com.example.day17provider.DB.StuProvider”:该字符串指定了继承ContentProvider类的路径名,这个名字是在mianfest中自己定义的,按照规律,避免重复,一般使用该类的包名。
<span style="font-size:18px;">// 添加要匹配的URI。参数一:authority域名 参数二:资源路径:表名 参数三:匹配成功的返回码</span>
<span style="font-size:18px;">// 完整路径Uri:content://com.example.provider.contactprovider/javaScore</span>
<span style="font-size:18px;">matcher.addURI(uriString, database_table_name, 1);// 意思是,如果匹配成功返回1</span>
通过上述代码,把uriString和数据库的表名拼接到一起,指定了该APP所公开的数据库的表名。把一段Uri字符串添加到了匹配集里了,匹配到那个,返回指定的int类型的值。
如何匹配呢?很简单,只需要调用:
<span style="font-size:18px;">int code = matcher.match(uri); //uri为传进来的uri字符串,如果匹配成功,则code = 1</span>
就会自动与matcher.addUri()已经预定好的几条Uri进行比对了。
更进一步:模糊匹配
在matcher.addURI(uriString, database_table_name, 1);里,可以使用*和#进行模糊匹配
模糊匹配:
# 数字
* 字符
例: matcher.addURI(uriString,database_table_name/*, 1);表示匹配的是数据库表database_table_name中的键名,可以是name,sex等属性。用来查询指定的内容。
matcher.addURI(uriString, database_table_name/#, 2);表示匹配的是数据库表database_table_name中的键名,一般是int类型的_id列。用来查询指定的id信息。这里返回值就变成2了,要与上面不同。
四、追加和解析Uri
- 首先是为什么要追加和解析。
我们在查询数据的时候,一般会给定条件查询,譬如我只想指定查询都叫张三的考了多少分。这样就需要模糊匹配。而条件通常是由用户输入。我们要先获取用户输入的条件信息,追加到uri字符串上,才可以传递到contentprovider并得到查询结构。这个过程里,用户使用的应用会有一个将条件追加到uri的操作,提供数据的应用会有一个将条件从uri中解析出来的操作。
- 追加
首先将提供数据的contentprovider的地址声明出来,因为会经常用到这个很长的字符串。Uri contentUri = Uri.parse("content://com.example.provider.contentrovider/javaScore");
数字匹配Uri withAppendedId = ContentUris.withAppendedId(contentUri,Long.parseLong(id));字符匹配Uri contentUriWithName = Uri.withAppendedPath(contentUri, name);
- 解析
数字解析long id = ContentUris.parseId(uri);
字符解析String name = uri.getLastPathSegment();
五、ContentResolver
距离自定义ContentProvider出场只差一步,那就是ContentResolver类。用来访问由ContentProvider公开的数据。其实说白了,这就是一个接口,由于这个接口在数据所在程序里,所以通过这个接口就能访问所在程序的数据。就像开了窗户的屋子,你能通过窗户看到屋里的东西。那么,如何看?ContentProvider就起了人眼睛的作用。
首先,实例化ContentResolver。由于眼睛是全局的,没必要用一次就换新的,所以声明为全局:
private ContentResolver resolver;
在Activity的onCreate()方法里对其进行实例化:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
resolver = getContentResolver();
<span style="white-space:pre"> }
这样就得到了resolver实例,可以使用reslover的增删改查方法:
插入:resolver.insert(uri, values);
分析:参数一:uri,就是@四、追加和解析Uri中,追加以后的uri
参数二:values是ContentValues的实例,用来存放要更新的数据
查询:resolver.query(uri, projection, selection, selectionArgs, sortOrder);
分析: 参数一:同上;
参数二:要查询的列名;
参数三:查询条件;
参数四:条件中的参数(占位符);//《为什么需要占位符》见下篇博客
参数五:查询结果的排序方法;
删除:resolver.delete(url, where, selectionArgs);
删除:resolver.delete(url, where, selectionArgs);
分析:参数一:同上;
参数二:查询的条件;
参数三:条件中的参数(占位符)
改(更新):resolver.update(uri, values, where, selectionArgs);
分析:参数一:同上;
参数二:同上;
参数三:同上;
参数四:同上;
通过这些方法,就会把命令和参数传送到了继承ContentProvider的类里,因为底层对ContentProvider和ContentResolver的设计,其增删改查的参数完全相同,不必为此纠结。
六、自定义ContentProvider
- 声明一个类继承ContentProvider。重写其五个方法:增,删,改,查,获取mimeType。
方法体:
<span style="font-size:18px;">import android.content.ContentProvider; import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; public class Providers extends ContentProvider { public boolean onCreate() { return false; } public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { return null; } public String getType(Uri uri) { return null; } public Uri insert(Uri uri, ContentValues values) { return null; } public int delete(Uri uri, String selection, String[] selectionArgs) { return 0; } public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; } }</span>
- 在Mainfest文件中配置<provider></provider>节点
<span style="font-size:18px;"><span style="white-space:pre"> </span><provider android:name="com.example.providecontentprovider" android:authorities="com.example.day17provider.DB.StuProvider" android:exported="true" </provider></span>
-
- 添加匹配池
static {
matcher.addURI(AUTHORITY, DBHelper.TABLE_NAME, 1);
matcher.addURI(AUTHORITY, DBHelper.TABLE_NAME + "/#", 2);
matcher.addURI(AUTHORITY, DBHelper.TABLE_NAME + "/*", 3);
}
- 初始化
private static final UriMatcher matcher = new UriMatcher(
UriMatcher.NO_MATCH);
private static final String AUTHORITY = "com.example.provider.contentprovider";
private SQLiteDatabase db;
- onCreate()
public boolean onCreate() {
db = new DBHelper(getContext()).getWritableDatabase();
return false;
}
- 查
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { int code = matcher.match(uri); Cursor cursor = null; switch (code) { case 1:// 查找全部 cursor = db.query(DBHelper.TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder); break; case 2: // 指定ID long id = ContentUris.parseId(uri); cursor = db.query(DBHelper.TABLE_NAME, projection, DBHelper.JAVA_ID + " = ?", new String[] { id + "" }, null, null, sortOrder); break; case 3: // 取出name值 String name = uri.getLastPathSegment(); // 查找条件 cursor = db.query(DBHelper.TABLE_NAME, projection, " name like '%" + name + "%'", selectionArgs, null, null, null); break; } return cursor; }
- 插
public Uri insert(Uri uri, ContentValues values) { int code = matcher.match(uri); Uri withAppendedId = null; if (code == 1) { long id = db.insert(DBHelper.TABLE_NAME, null, values); withAppendedId = ContentUris.withAppendedId(uri, id); } return withAppendedId; }
- 删
public int delete(Uri uri, String selection, String[] selectionArgs) { int code = matcher.match(uri); int num = 0; switch (code) { case 1: break; case 2: long id = ContentUris.parseId(uri); num = db.delete(DBHelper.TABLE_NAME, DBHelper.JAVA_ID + "= ?", new String[] { id + "" }); break; } return num; }
- 改
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { int code = matcher.match(uri); switch (code) { case 1:// 没有指定name、id db.update(DBHelper.TABLE_NAME, values, selection, selectionArgs); break; case 2:// 根据id更新 // 取出id long id = ContentUris.parseId(uri); db.update(DBHelper.TABLE_NAME, values, DBHelper.JAVA_ID + "=?", new String[] { "" + id }); break; case 3:// 根据name更新 // 取出name String name = uri.getLastPathSegment(); db.update(DBHelper.JAVA_ID, values, DBHelper.JAVA_NAME + " like '%" + name + "%'", null); break; } return 0; }
- 用到的DBHelper类
<span style="font-size:18px;">public class DBHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "stu";
public static int VERSION = 1;
public static final String TABLE_NAME = "javaScore";
public static final String JAVA_ID = "_id";
public static final String JAVA_NAME = "name";
public static final String JAVA_SCORE = "score";
public DBHelper(Context context, String name, CursorFactory factory,
int version) {
super(context, name, factory, version);
}
public DBHelper(Context context) {
super(context, DB_NAME, null, VERSION);
}
public void onCreate(SQLiteDatabase db) {
String sql = "create table "+TABLE_NAME+"("+JAVA_ID
+" INTEGER PRIMARY KEY AUTOINCREMENT,"+JAVA_NAME
+" TEXT,"+JAVA_SCORE+" TEXT)";
db.execSQL(sql);
}
}</span>