[Android]Android四大组件之ContentProvider
URI简介
URI(Universal Resource Identifier),又被称为"通用资源标志符"。
URI由许多部分所组成,示例及解说如下:

Content URIs介绍
Android遵循URI的标准,定义了一套专用的Uri(即,Content URIs)。并且,Android提供了ContentUris、UriMatcher等类用于操作Content URIs。
1. Content URIs语法
Content URIs的语法如下:
content://authority/path/id
Content URIs的示例及说明如下:

说明:
content: Content URIs前缀,它对应与标准URI的scheme。它的值为ContentResolver.SCHEME_CONTENT(即,content://)。
authority: 一个唯一的标识符,Google建议使用类的全名来作为authority。外部调用者可以根据这个标识来找到它。
path: 它可以用来表示我们要操作的数据,外部调用者根据这个路径信息来判断要返回什么类型的数据。这个后缀路径可以自由定义。
id: 唯一的数字标识符。它表示要具体操作的数据类型中的具体某一项。
2. Content URIs相关类介绍
2.1 ContentUris
ContentUris中包含了三个静态函数:
long parseId(Uri uri): 解析Uri中的末尾id。成功返回id,失败则返回-1。
Uri withAppendedId(Uri uri, long id): 将id追加到uri中,并返回追加id后的uri。
Uri.Builder appendId(Uri.Builder builder, long id): 将id追加到builder中,并返回追加id后的builder。
2.2 UriMatcher
UriMatcher用于匹配Uri。它的用法如下:
(01) 创建UriMatcher对象。
(02) 把你需要匹配Uri路径通过addURI()注册到UriMatcher对象上。
(03) 注册成功后,ContentProvider就可以通过UriMatcher监听你注册的Uri。当有匹配的Uri动作(如插入)时,再就可以通过UriMatcher的match()函数来获取Uri的一个标识,该标识是在addURI()时传入的。这个标识的作用是方便在switch语句中对不同的Uri进行处理。
UriMatcher的主要API说明:
void addURI(String authority, String path, int code): 将"authority"+"path"注册的Uri注册到UriMatcher中,code是该Uri对应的标识。
int match(Uri uri): 匹配Uri,并返回Uri对应的标识。这里返回的标识与addURI中的标识对应。
ContentProvider
1.1ContentProvider简介
ContentProvider通常用于共享数据。
当其他程序需要访问本程序的数据,并且数据的结构比较复杂时,就可以使用ContentProvider来共享数据。如果数据不需要跨程序访问,使用数据库即可;如果数据结构比较简单,可以考虑前面提到的通过Intent共享文本等简单数据,或者通过FileProvider共享文件。
1.2ContentProvider示例
接下来,实现一个ContentProvider。该ContentProvider包括两部分:ContentProvider提供者APK 和 ContentProvider测试APK。
(01) ContentProvider提供者:自定义一个ContentProvider,并监听相应的URI。客户可以通过URI插入/删除/更新/查询数据。ContentProvider中的数据记录的是人的信息,包括"姓名,出生年月,email,性别"等信息。
(02) ContentProvider测试APK:通过URI向ContentProvider发起插入/删除/更新/查询等操作。
下面介绍ContentProvider的实现步骤。
2. ContentProvider提供者APK
2.1 ContentProvider的存储表格
根据ContentProvider的数据特性,我们建立一张表,表格包括"id/姓名/出生年月/email/性别"这些信息。表对应的类如下:
public final class MyContract {
public MyContract() {}
/**
* BaseColumns类中有两个属性:_ID 和 _COUNT
*/
public static abstract class Entry implements BaseColumns {
public static final String TABLE_NAME = "mytable01";
public static final String NAME = "name";
public static final String BIRTH_DAY = "birthday";
public static final String EMAIL = "email";
public static final String GENDER = "gender";
}
}
说明:BaseColumns是Android自带的类,它集成了"_ID"和"_COUNT"两个属性。
2.2 ContentProvider对应的manifest
在manifest中声明我们自定义的ContentProvider。
<application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
<provider android:name="MyProvider" android:authorities="com.skw.myprovider" />
</application>
2.3 自定义的ContentProvider类
完成ContentProvider类,主要需要注意以下几点:
(01) ContentProvider的数据一般是以"数据库"或"网络数据"的方式存储的。如果是数据库,则需要实现SQLiteOpenHelper类。通过SQLiteOpenHelper类新建/管理数据库。
(02) ContentProvider主要是以Uri的形式方式访问的(也可以通过Intent)。要通过UriMatcher注册ContentProvider监听的Uri。
(03) ContentProvider是一个抽象类。当我们需要以继承ContentProvider的方式自定义ContentProvider时,需要实现query(), insert(), update(), delete(), getType(), onCreate()这六个函数。
2.3.1 数据库
下面,先介绍ContentProvider的数据库的实现。
// 创建表格的SQL语句
private static final String SQL_CREATE_ENTRIES =
"CREATE TABLE " + Entry.TABLE_NAME + " (" +
Entry._ID + " INTEGER PRIMARY KEY," +
Entry.NAME + " TEXT NOT NULL, " +
Entry.BIRTH_DAY + " TEXT, " +
Entry.EMAIL + " TEXT, " +
Entry.GENDER + " INTEGER " +
" )";
private class DBLiteHelper extends SQLiteOpenHelper {
public static final int DATABASE_VERSION = 1;
public static final String DATABASE_NAME = "MyProvider.db";
public DBLiteHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(SQL_CREATE_ENTRIES);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
3.3.2 注册Uri
// Uri的authority
public static final String AUTHORITY = "com.skw.myprovider";
// Uri的path
public static final String PATH = "table01";
// UriMatcher中URI对应的序号
public static final int ITEM_ALL = 1;
public static final int ITEM_ID = 2;
private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
static {
URI_MATCHER.addURI(AUTHORITY, PATH, ITEM_ALL);
URI_MATCHER.addURI(AUTHORITY, PATH+"/#", ITEM_ID);
}
说明:通过addURI()就可以将URI注册到UriMatcher中,从而实现ContentProvider对URI的监听。这里的AUTHORITY与manifest中的android:authorities一致!
例如, URI_MATCHER.addURI(AUTHORITY, PATH, ITEM_ALL); 意味着ContentProvider对"content://con.skw.myprovider/table01"进行监听。
例如, URI_MATCHER.addURI(AUTHORITY, PATH+"/#", ITEM_ALL); 意味着ContentProvider对"content://con.skw.myprovider/table01/5"进行监听。
3.3.2 实现ContentProvider的抽象函数
onCreate()
@Override
public boolean onCreate() {
mDbHelper = new DBLiteHelper(this.getContext());
Log.d(TAG, "open/create table");
return true;
}
说明:onCreate()中新建SQLiteOpenHelper对象。
delete()
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = mDbHelper.getWritableDatabase();
int count = 0;
switch (URI_MATCHER.match(uri)) {
case ITEM_ALL:
count = db.delete(Entry.TABLE_NAME, selection, selectionArgs);
Log.d(TAG, "delete ITEM uri="+uri+", count="+count);
break;
case ITEM_ID:
// 获取id列的值
String id = uri.getPathSegments().get(1);
count = db.delete(Entry.TABLE_NAME, Entry._ID+"=?", new String[]{id});
Log.d(TAG, "delete ITEM_ID id="+id+", uri="+uri+", count="+count);
break;
default:
throw new IllegalArgumentException("Unknown URI"+uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
说明:delete()中会通过match()获取uri对应的编码。这里的编码就是和addURI()注册uri的编码是相对应的。此外,notifyChange()的作用是通常数据库变化,若有ContentObserver监听该Uri,则notifyChange()最终会将消息传递给监听者。
insert(), update(), query()的实现与delete()类似,就不再说明。
getType()
@Override
public String getType(Uri uri) {
switch (URI_MATCHER.match(uri)) {
case ITEM_ALL:
return "skw.myprovider.dir/table01";
case ITEM_ID:
return "skw.myprovider.item/table01";
default:
throw new IllegalArgumentException("Unknown URI"+uri);
}
}
说明:getType()是返回Uri对应的数据类型。
4. ContentProvider测试APK
public class ProviderTest extends Activity
implements View.OnClickListener {
private static final String TAG = "##ProviderTest##";
// 数据库的属性,与MyProvider的表格属性一致
public static final String NAME = "name";
public static final String BIRTH_DAY = "birthday";
public static final String EMAIL = "email";
public static final String GENDER = "gender";
// 数据库的URI
public static final Uri CONTENT_URI = Uri.parse("content://com.skw.myprovider/table01");
private ContentResolver mContentResolver = null;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
((Button)findViewById(R.id.insert)).setOnClickListener(this);
((Button)findViewById(R.id.deleteFirst)).setOnClickListener(this);
((Button)findViewById(R.id.deleteKate)).setOnClickListener(this);
((Button)findViewById(R.id.deleteAll)).setOnClickListener(this);
((Button)findViewById(R.id.update)).setOnClickListener(this);
((Button)findViewById(R.id.show)).setOnClickListener(this);
// 删除第一行,然后全部打印出来
mContentResolver = getContentResolver();
}
@Override
public void onClick(View view) {
switch(view.getId()) {
case R.id.insert:
// 添加
insert("Jimmy", "20020201", "Jimmy20020201@126.com", 1);
insert("Kate", "20030104", "kate20030104@126.com", 0);
insert("Li Lei", "20021124", "lilei20101124@126.com", 1);
insert("Lucy", "20010624", "lucy20101124@126.com", 0);
break;
case R.id.deleteFirst:
ContentUris cus = new ContentUris();
Uri uri = cus.withAppendedId(CONTENT_URI, 1);
Log.d(TAG, "delete uri="+uri);
mContentResolver.delete(uri, null, null);
break;
case R.id.deleteKate:
// 删除“username=Kate”的行,然后全部打印出来
mContentResolver.delete(CONTENT_URI, NAME+"=?", new String[]{"Kate"});
break;
case R.id.deleteAll:
// 删除全部的行,然后全部打印出来
deleteAll() ;
break;
case R.id.update:
// 更新第1个值,然后全部打印出来
updateItem() ;
break;
case R.id.show:
// 打印全部的值
printAll() ;
break;
default:
// 查找第2个值
//querySecondItem() ;
break;
}
}
/*
* 通过ContentResolver,将值插入到MyProvider中
*/
private void insert(String name, String date, String email, int gender) {
ContentResolver cr = getContentResolver();
ContentValues cv = new ContentValues();
cv.put(NAME, name);
cv.put(BIRTH_DAY, date);
cv.put(EMAIL, email);
cv.put(GENDER, gender);
Uri uri = cr.insert(CONTENT_URI, cv);
Log.d(TAG, "insert uri="+uri);
}
private void updateItem() {
ContentResolver cr = getContentResolver();
ContentUris cus = new ContentUris();
Uri uri = cus.withAppendedId(CONTENT_URI, 1);
ContentValues cv = new ContentValues();
cv.put(NAME, "update_name");
cv.put(BIRTH_DAY, "update_date");
cv.put(EMAIL, "update_email");
cv.put(GENDER, 1);
cr.update(uri, cv, null, null);
}
/*
* 通过ContentResolver,将MyProvider中的值全部删除
*/
private void deleteAll() {
Log.d(TAG, "delete all value!");
ContentResolver cr = getContentResolver();
cr.delete(CONTENT_URI, null, null);
}
private void querySecondItem() {
ContentResolver cr = getContentResolver();
ContentUris cus = new ContentUris();
Uri uri = cus.withAppendedId(CONTENT_URI, 2);
String[] proj = new String[] { NAME, BIRTH_DAY, EMAIL, GENDER};
Cursor cursor = cr.query(uri, proj, null, null, null);
int index = 0;
while (cursor.moveToNext()) {
Log.d(TAG, "querySecondItem--"+index+"--"
+", email=" + cursor.getString(cursor.getColumnIndex(EMAIL))
+", username=" + cursor.getString(cursor.getColumnIndex(NAME))
+", date=" + cursor.getString(cursor.getColumnIndex(BIRTH_DAY))
+", gender=" + cursor.getInt(cursor.getColumnIndex(GENDER)));
index++;
}
}
private void printAll() {
//通过contentResolver进行查找
ContentResolver cr = getContentResolver();
Log.d(TAG, "print all value!");
// query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
// 返回的列
String[] proj = new String[] { NAME, BIRTH_DAY, EMAIL, GENDER};
Cursor cursor = cr.query(
CONTENT_URI, proj, null, null, null);
int index = 0;
while (cursor.moveToNext()) {
Log.d(TAG, "printAll--"+index+"--"
+", email=" + cursor.getString(cursor.getColumnIndex(EMAIL))
+", username=" + cursor.getString(cursor.getColumnIndex(NAME))
+", date=" + cursor.getString(cursor.getColumnIndex(BIRTH_DAY))
+", gender=" + cursor.getString(cursor.getColumnIndex(GENDER)));
index++;
}
startManagingCursor(cursor); //查找后关闭游标
}
}
Permission介绍
<permission
android:name="com.skw.permission.myprovider"
android:protectionLevel="normal"
android:label="@string/permission_label"
android:description="@string/permission_description"
/>
说明:permission常用的几个属性如下:
(01) android:name: 必需的。权限的名称,通常应遵循android 命名方案(.permission.)。
(02) android:protectionLevel: 必需的。权限的安全级别,共包括"normal, dangerous, signature, signatureOrSystem"四种。
normal 表示权限是低风险的,不会对系统、用户或其他应用程序造成危害;
dangerous 表示权限是高风险的,系统将可能要求用户输入相关信息,才会授予此权限;
signature 表示只有当应用程序所用数字签名与声明引权限的应用程序所用数字签名相同时,才能将权限授给它;
signatureOrSystem 表示将权限授给具有相同数字签名的应用程序或android 包类。
(03) android:permissionGroup: 非必需的。可以将权限放在一个组中,但对于自定义权限,应该避免设置此属性。
(04) android:label: 非必需的。标签。
(05) android:description: 非必需的。描述。
(06) android:icon非必需的。图标。
自定义Permission示例
1. 设置权限
<application
android:exported="true"
android:label="@string/app_name"
android:icon="@drawable/ic_launcher">
<provider
android:name="MyProvider"
android:authorities="com.skw.myprovider"
android:permission="com.skw.permission.myprovider">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</provider>
</application>
<permission
android:name="com.skw.permission.myprovider"
android:protectionLevel="normal"
android:label="@string/permission_label"
android:description="@string/permission_description"
/>
说明:上面是自定义权限的manifest文件内容。
(01) 首先,在需要定义权限的ContentProvider中声明权限android:permission="com.skw.permission.myprovider"
(02) 接着,再定义permission。permission对应的name与声明的权限对应。
2. 获取权限
在需要使用ContentProvider的APK的manifest中需要声明使用该权限。声明方法如下:
<uses-permission android:name="com.skw.permission.myprovider" />
[Android]Android四大组件之ContentProvider的更多相关文章
- Android实训案例(五)——四大组件之一ContentProvider的使用,通讯录的实现以及ListView的优化
Android实训案例(五)--四大组件之一ContentProvider的使用,通讯录的实现 Android四大组件是啥这里就不用多说了,看图吧,他们之间通过intent通讯 我们后续也会一一的为大 ...
- Android 四大组件之" ContentProvider "
前言 ContentProvider作为Android的四大组件之一,是属于需要掌握的基础知识,可能在我们的应用中,对于Activity和Service这两个组件用的很常见,了解的也很多,但是对Con ...
- Android四大组件之——ContentProvider(一)
Android四大组件之--ContentProvider(一) 本人邮箱:JohnTsai.Work@gmail.com,欢迎交流讨论. 欢迎转载,转载请注明网址:http://www.cnblog ...
- 【Android开发日记】之入门篇(九)——Android四大组件之ContentProvider
数据源组件ContentProvider与其他组件不同,数据源组件并不包括特定的功能逻辑.它只是负责为应用提供数据访问的接口.Android内置的许多数据都是使用ContentProvider形式,供 ...
- Android四大组件之contentProvider
Activity,Service,broadcast and Contentprovider android 4 大组件. ContentProvider:使用: public class Image ...
- Android的四大组件
Android的四大组件:Activity.Service.BroadcastReceiver.Content Provider. Content Provider 属于Android应用程序的组件之 ...
- Android开发四大组件概述
这个文章主要是讲Android开发的四大组件,本文主要分为 一.Activity具体解释 二.Service具体解释 三.Broadcast Receiver具体解释 四.Content Provid ...
- Android之四大组件、六大布局、五大存储 总结
Android之四大组件.六大布局.五大存储 一.四大组件:Android四大组件分别为activity.service.content provider.broadcast receiver. ...
- Android的四大组件之Activity
Android的四大组件之Activity Activity:是Android组件中最基本也是最为常见用的四大组件(Activity,Service服务,Content Provider内容提供者,B ...
- android中四大组件之间相互通信
好久没有写有关android有关的博客了,今天主要来谈一谈android中四大组件.首先,接触android的人,都应该知道android中有四大组件,activity,service,broadca ...
随机推荐
- 【详解】苹果AppStore审核被拒,原因终逃不过这些!
近日,相信很多开发者都留意到了: 苹果针对应用标题的审核确有明显的变严趋势!我们在<惊!苹果再次加强审核力度,众App纷纷止步应用标题>中也对该现象进行了详细的分析,并给出了相应的解决方案 ...
- EOF的使用
1.我疑惑了 char a[20]; while(scanf("%s",a)!=EOF){ cout<<"hello"<<endl; } ...
- 【POJ 3580】SuperMemo Splay
题意 给定$n$个数,$m$个询问,每次在$[L,R]$区间加上一个数,或者反转一个区间$[L,R]$,或者循环右移区间$[L,R]$共$T$次,或者在第$x$个数后插入一个数$p$,或者删除第$x$ ...
- 「LuoguP1429」 平面最近点对(加强版)
题目描述 给定平面上n个点,找出其中的一对点的距离,使得在这n个点的所有点对中,该距离为所有点对中最小的 输入输出格式 输入格式: 第一行:n:2≤n≤200000 接下来n行:每行两个实数:x y, ...
- The Django Book 2.0--中文版
Table of contents 2.0, English -> Chinese 第一章:介紹Django阅读 01 第二章 入门阅读 02 第三章 视图和URL配置阅读 03 第四章:模版阅 ...
- Mysql 创建存储过程 更新表
DELIMITER // use protocoldb// drop procedure if exists sp_protocol_Update// create procedure sp_prot ...
- kindle3 破解字体
在万能的链接里下载kindle-fonts-4.4.N-k3.zip,update后kindle里出现linkfonts/fonts,这里就是存放字体的位置,字体格式需用.ttf. 在linkfont ...
- 查看MySql数据库物理文件存放位置
查找数据库文件位置使用命令 show global variables like "%datadir%";
- java-并发-进程和线程
浏览以下内容前,请点击并阅读 声明 软件的并发是指同时做多件事情,java平台一开始就支持并发编程,java编程语言以及类库含有对并发最基本的支持,从5.0版本开始,java平台开始包含一些高并发的A ...
- vmware 虚拟网卡配置
VMware虚拟机进阶:[1]网卡配置 1. 可以在下面这个图中看到几种网络设置模式,一直以来我们都喜欢用桥接模式和NAT模式,因为这两种模式是最符合一般人上网设置的模式,下面我们就一种种介绍个大家 ...