[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 ...
随机推荐
- TS流分析
http://blog.csdn.net/zxh821112/article/details/17587215 一 从TS流开始 数字电视机顶盒接收到的是一段段的码流,我们称之为TS(Transpor ...
- 分享知识-快乐自己:初始 Struts2 (基本概念)及 搭建第一个Demo
1):struts2 的基本概念: 1-1):Struts2 是什么? 1.Struts2是一个基于MVC设计模式的Web应用框架,它本质上相当于一个servlet,在MVC设计模式中,Struts2 ...
- python-函数用法
Python 一.lower() 方法转换字符串中所有大写字符为小写. lower()方法语法: str.lower() 参数 无. 返回值 返回将字符串中所有大写字符转换为小写后生成的字符串. 练习 ...
- jquery操作全选、批量删除、加减行
--------------------------------------------------------------------------------------- html静态页面 --- ...
- VC++动态链接库(DLL)编程深入浅出(转帖:基础班)
1.概论 先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量.函数或类.在仓库的发展史上经历了“无库-静 ...
- Nginx均衡负载配置
前言:Nginx也是一种服务器,反向代理服务器.单一tomcat能承受的并发访问量在150-200之间,还是在比较理想的情况下,当并发量超出这个范围,便需要Nginx实现多个tomcat的均衡负载,但 ...
- 安装时后的idea,项目不能运行,pom.xml文件不能下载到本地仓库,maven配置是正确的
安装时后的idea,项目不能运行,pom.xml文件不能下载到本地仓库,maven配置是正确的 项目上传到svn后,同事下载项目后,没有识别出来mavn中的pom.xml文件,导致idea不能自动下载 ...
- [hiho1586]Minimum
题意:区间内乘积最小值,带修改.解题关键:线段树裸题,考场上以为x y必须不等,还维护了次小值,尼玛嗨尼玛嗨,划水一整场,心态爆炸. 注意坐标需要+1 #include<cstdio> # ...
- JavaWeb_打包web应用war
使用下面的语句进行打包 jar -cvf aa.war news 打包之后的文件可以直接放在tomcat的webapps里面,一旦启动tomcat,会自动解压aa.war文件.
- 蓝桥杯 正则问题(dfs)
1607: 正则问题 时间限制: 1 Sec 内存限制: 256 MB提交: 34 解决: 13[提交][状态][讨论版] 题目描述 考虑一种简单的正则表达式:只由 x ( ) | 组成的正则表达 ...