您的位置:首页 > 编程语言 > PHP开发

Activity_ContentProvider

2016-06-08 14:14 399 查看

Activity_ContentProvider

内容提供器(Content Provider)在Android中存在的意义就是,我们写的程序可以访问别人分享的数据,我们也可以将自己的程序用的基础数据进行一份分享的功能。比如现在很多程序都可以访问电话簿、短信什么的,这个就说明电话簿、短信、媒体库等程序都实现了夸程序数据分享的功能。

内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访问数据的安全性。使用内容提供器是Android实现跨程序共享数据的标准方式。

访问其他程序中的数据

当我们想要访问其他程序中的代码的时候,我们需要使用到Context类提供的一个方法getContentResolver()方法,使用该方法可以获取到一个ContentResolve的对象,有了这个对象,我们就可以实现增删改查了。ContentResolver中的增删改查方法都不接收表名参数的,而是使用一个Uri参数代替的,这个参数被称为内容Uri。(Uri参数让我想起来了当我们使用隐士意图调用系统的拨打电话界面事,我们使用intent.setData()方法中就是接收的一个Uri参数。

内容Uri是内容提供器中的数据 建立的唯一标识。它主要由两部分组成,第一部分:权限(authority)是用于对不同的应用程序做的区分的,一般为了避免冲突,都会采用程序包名的方式来进行命名。比如某个程序的包名是com.example.app,那么该程序对应的权限就可以命名为com.example.app.provider第二部分:路径是用于对同一应用程序中不同的表做区分的,通常都会添加到权限的后面。比如某个程序的数据库里存在两张表,table1和table2,这是就可以将路径分别命名为/table1和/table2,然后把权限和路径进行组合,内容URL就变成了:com.example.aap.provider/table1和com.example.aap.provider/table2。不过,目前还很难辨认出这两个字符串就是两个内容URL,我们还需要在字符串的头部添加上协议声明。内容URL最标准的格式写法如下:

content://com.example.app.provider/table1

content://com.example.app.provider/table2

得到一个Uri对象:

Uri uri=Uri.parse("content://com.example.app.provider/table1");


读取系统联系人

启动程序,获取电话簿中的联系人的姓名和手机号,并以ListView的形式展现出来。

1、activity_main.xml文件中的布局代码:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<ListView
android:id="@+id/contacts_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>


2、MainActivity中的逻辑代码:

public class MainActivity extends Activity {
private ListView lvContacts;
private ArrayAdapter<String> adapter;
private List<String> data =new ArrayList<String>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
lvContacts=(ListView) findViewById(R.id.contacts_view);
adapter=new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, data);
lvContacts.setAdapter(adapter);
//获取电话簿中联系人的数据
readContacts();

}
private void readContacts() {
Cursor cursor=null;
//系统中电话簿的Uri
Uri uri=ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
try {
cursor=getContentResolver().query
(uri, null, null, null, null);
if (cursor!=null) {
while(cursor.moveToNext()){
//获取联系人姓名
String name=cursor.getString(cursor.getColumnIndex
(ContactsContract.CommonDataKinds.Phone.
DISPLAY_NAME));
//获取联系人手机号
String number =cursor.getString(cursor.getColumnIndex
(ContactsContract.CommonDataKinds.
Phone.NUMBER));
//将获取到的数据添加到ArrayList中
data.add(name + "\n" + number);
}
}
} catch (Exception e) {
e.printStackTrace();
}finally{
if (cursor!=null) {
cursor.close();
}
}
}
}


3、读取手机中的联系人操作是一个敏感操作,所以需要权限:

<uses-permission android:name="android.permission.READ_CONTACTS"/>


作为无私的程序猿的我们来说,我们写的程序也可以给其他程序提供一些数据,那么我们应该如何将我们程序中的数据提供给其他程序呢?

创建自己的内容提供器

1、创建自己的内容提供器,肯定会继承ContentProvider这个类了,继承这个类,需要重写6个方法,下面就看看这6个方法分别是什么?并且他们各自的方法作用是什么?

public class MyContentProvider extends ContentProvider {
@Override
public boolean onCreate() {
//初始化内容提供器的时候调用。通常会在这里完成对数据库的创建和升级等操作,
//返回true表示内容提供器初始化成功,返回false表示失败。只有当存在
//ContentResolver尝试访问我们程序中的数据时,内容提供器才会被初始化。
return false;
}
@Override
public Uri insert(Uri uri, ContentValues values) {

return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
//从内容提供器中删除数据。使用Uri参数来确定删除哪一张表中的数据,selection和
//selectionArgs参数用于约束删除那些行,被删除的行数将作为返回值返回。
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
//更新内容提供器中已有的数据。使用Uri参数来确定更新哪一张表中的数据,新数据保存在values参数中,
//selection和selectionArgs参数用于约束更新那些行,受影响的行数将最为返回值返回。
return 0;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
//向内容提供器中查询数据。使用Uri参数来确定查询哪张表,projection参数
//用于确定查询那些列,selection和selectionArgs参数用于约束查询那些行
//sortOrder参数用于对结果进行排序,查询的结果存放在Cursor对象中返回。
return null;
}
@Override
public String getType(Uri uri) {
//根据传入的Uri返回相应的MINE类型。一个内容URI所对应的MIME字符串主要由三部分组分,
//1、必须以vnd开头。2、如果内容URI以路径结尾,则后接android.cursor.dir,
//如果内容URI以id结尾,则后接android.cursor.item/
//3、最后接上vnd.<authority>.<path>
//例如:content://com.example.app.provider/table1所对应的MIME类型:
//vnd.android.cursor.dir/vnd.com.example.app.provider.table1
//例如:content://com.example.app.provider/table/1所对应的MIME类型:
//vnd.android.cursor.item/vnd.com.example.app.provider.table1
return null;
}
}


2、当调用方调用我们的程序中的数据时,我们需要对调用方传来的Uri进行一定的解析与了解。内容Uri的格式主要有2种方式

content://com.example.app.provider/table1

content://com.example.app.provider/table/1

以路径结尾表示期望访问该表中所有的数据,以id结尾表示访问该表中拥有相应id的数据。我们可以使用通配符的方式来分别匹配这两种格式的内容Uri:

1、*:表示匹配任意长度的任意字符串

2、#:表示匹配任意长度的数字

content://com.example.app.provider/*

content://com.example.app.provider/table/#

3、当我们了解完Uri的格式后,我们可以使用UriMatcher这个类就可以轻松地实现匹配内容URI的功能。UriMatcher中提供了一个addURI()方法,该方法接收三个参数:分别是权限、路径、自定义码。当调用UriMatcher的match()方法时,就可以将一个Uri对象传入,返回值是某个能够匹配这个Uri对象所对应的自定义码,利用这个代码,我们就可以判断出调用方期望访问的是哪张表中的数据了。你在这个时候,你可能已经晕菜了,让我们看代码,来更好的理解这些东西吧。

实现跨程序数据共享

我为了方便起见,我就用我从前写的代码,如果你是第一次看这篇blog,你可以去我的那篇有关对SQLite数据库介绍中沾代码。

1、我先将我从前的那个SQLiteTest项目卸载,以免造成影响。

2、我在SQLiteTest这个项目中新建一个 DatabaseProvider类继承ContentProvider。

3、在AndroidManifest.xml文件中注册该ContentProvider。

4、创建一个新的项目ProviderTest项目,用于访问SQLiteTest中DatabaseProvider提供的数据。

5、编写ProviderTest的布局文件。

6、编写ProviderTest中MainActivity中的代码。

第一步:

public class DatabaseProvider extends ContentProvider {
// 访问book表中的所有数据
public static final int BOOK_DIR = 0;
// 访问book表中的单条数据
public static final int BOOK_ITEM = 1;
// 访问category表中的所有数据
public static final int CATEGORY_DIR = 2;
// 访问category表中的单条数据
public static final int CATEGORY_ITEM = 3;
public static final String AUTHORITY = "com.example.sqlitetest.provider";
private static UriMatcher uriMatcher;
private MyDatabaseHelper dbHelper;
static {
//构造方法,参数通常使用UriMathcer.NO_MATCH,表示验证Uri时,如果不匹配,则返回该值
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);
uriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);
uriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);
uriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);
}

@Override
public boolean onCreate() {
dbHelper = new MyDatabaseHelper(getContext(), "BookStore.db", null, 2);
return true;
}

@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// 查询数据
SQLiteDatabase queryDatabase = dbHelper.getReadableDatabase();
Cursor cursor = null;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
cursor = queryDatabase.query("book", projection, selection,
selectionArgs, null, null, sortOrder);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
cursor = queryDatabase.query("book", projection, "id = ?",
new String[] { bookId }, null, null, sortOrder);
break;
case CATEGORY_DIR:
cursor = queryDatabase.query("category", projection, selection,
selectionArgs, null, null, sortOrder);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
cursor = queryDatabase.query("book", projection, "id = ?",
new String[] { categoryId }, null, null, sortOrder);
break;
default:
break;
}
return cursor;
}

@Override
public Uri insert(Uri uri, ContentValues values) {
// 添加数据
SQLiteDatabase insertDatabase = dbHelper.getReadableDatabase();
Uri uriReturn = null;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
case BOOK_ITEM:
long newBookId = insertDatabase.insert("book", null, values);
uriReturn = Uri.parse("content://" + AUTHORITY + "/book/"
+ newBookId);
break;
case CATEGORY_DIR:
case CATEGORY_ITEM:
long newCategoryId = insertDatabase
.insert("category", null, values);
uriReturn = Uri.parse("content://" + AUTHORITY + "/category/"
+ newCategoryId);
break;
default:
break;
}
return uriReturn;
}

@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// 更新数据
SQLiteDatabase updateDatabase = dbHelper.getReadableDatabase();
int upDatedRows = 0;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
upDatedRows = updateDatabase.update("book", values, selection,
selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
upDatedRows = updateDatabase.update("book", values, "id = ?",
new String[] { bookId });
break;
case CATEGORY_DIR:
upDatedRows = updateDatabase.update("category", values, selection,
selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
upDatedRows = updateDatabase.update("book", values, "id = ?",
new String[] { categoryId });
break;
default:
break;
}
return upDatedRows;
}

@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase deleteDatabase = dbHelper.getReadableDatabase();
int deletedRows = 0;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
deletedRows = deleteDatabase.delete("book", selection,
selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
deletedRows = deleteDatabase.delete("book", "id = ?",
new String[] { bookId });
break;
case CATEGORY_DIR:
deletedRows = deleteDatabase.delete("category", selection,
selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
deletedRows = deleteDatabase.delete("category", "id = ?",
new String[] { categoryId });
break;
default:
break;
}
return deletedRows;
}

@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
return "vnd.android.cursor.dir/vnd.com.example.sqlitetest.provider.book";
case BOOK_ITEM:
return "vnd.android.cursor.item/vnd.com.example.sqlitetest.provider.book";
case CATEGORY_DIR:
return "vnd.android.cursor.dir/vnd.com.example.sqlitetest.provider.category";
case CATEGORY_ITEM:
return "vnd.android.cursor.dir/vnd.com.example.sqlitetest.provider.category";
default:
break;
}
return null;
}
}


第二步:

<provider
android:name="com.example.sqlitetest.DatabaseProvider"
android:authorities="com.example.sqlitetest.provider"
android:exported="true"
android:enabled="true">
</provider>


第三步:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >
<Button
android:id="@+id/add_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Add To Book" />
<Button
android:id="@+id/query_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Query from Book" />
<Button
android:id="@+id/update_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="update Book" />
<Button
android:id="@+id/delete_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Delete from Book" />
</LinearLayout>


第四步:

public class MainActivity extends Activity implements OnClickListener {
private Button addBtn,queryBtn,updateBtn,deleteBtn;
private String newId;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
addBtn=(Button) findViewById(R.id.add_data);
queryBtn=(Button) findViewById(R.id.query_data);
updateBtn=(Button) findViewById(R.id.update_data);
deleteBtn=(Button) findViewById(R.id.delete_data);
addBtn.setOnClickListener(this);
queryBtn.setOnClickListener(this);
updateBtn.setOnClickListener(this);
deleteBtn.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
//添加数据
case R.id.add_data:
Uri uriAdd=Uri.parse("content://com.example.sqlitetest.provider/book");
ContentValues insertValues =new ContentValues();
insertValues.put("name", "A Clash Kings");
insertValues.put("author", "GoogleDeveloper");
insertValues.put("pages", 1040);
insertValues.put("price", 22.85);
Uri newUri=getContentResolver().insert(uriAdd, insertValues);
newId =newUri.getPathSegments().get(1);
Log.i("providertest", "newId :"+newId);
break;
//查询数据
case R.id.query_data:
Uri uriQuery =Uri.parse("content://com.example.sqlitetest.provider/book");
Cursor cursor=getContentResolver().query(uriQuery, null, null, null, null);
if (cursor!=null) {
while(cursor.moveToNext()){
String name =cursor.getString(cursor.getColumnIndexOrThrow("name"));
String author=cursor.getString(cursor.getColumnIndex("author"));
int pages =cursor.getInt(cursor.getColumnIndex("pages"));
double price =cursor.getDouble(cursor.getColumnIndex("price"));
Log.i("providertest", "name :"+name);
Log.i("providertest", "author :"+author);
Log.i("providertest", "pages :"+pages);
Log.i("providertest", "price :"+price);
}
cursor.close();
}
break;
//更新数据
case R.id.update_data:
Uri uriUpdate=Uri.parse("content://com.example.sqlitetest.provider/book/" + newId);
ContentValues updateValues=new ContentValues();
updateValues.put("name", "A Storm of Swords");
updateValues.put("pages", 1216);
updateValues.put("price", "24.58");
int updateRows=getContentResolver().update(uriUpdate, updateValues, null, null);
Log.i("providertest", "update lineNumber :"+updateRows);
break;
//删除数据
case R.id.delete_data:
Uri uriDelete =Uri.parse("content://com.example.sqlitetest.provider/book/" + newId);
int deleteRow=getContentResolver().delete(uriDelete, null, null);
Log.i("providertest", "delete lineNumber :"+deleteRow);
break;
default:
break;
}

}
}


经过上面四步的完成,我们已经能够成功的访问SQLiteTest提供的DatabaseProvider这个类中的方法。为了证明成功还是那句话有图有真相:

1、当我点击Add To Book 按钮的时候,我将SQLiteTest项目下的BookStore.db数据库导出,看图:



并且看一下我打印的Log日志:

06-08 10:51:54.672: I/providertest(5681): newId :1


2、当我点击Query From Book 按钮的时候,让我们来看一下log日志吧:

06-08 10:53:22.936: I/providertest(5681): name :A Clash Kings
06-08 10:53:22.936: I/providertest(5681): author :GoogleDeveloper
06-08 10:53:22.936: I/providertest(5681): pages :1040
06-08 10:53:22.936: I/providertest(5681): price :22.85


3、当我点击Update Book 按钮的时候,让我们来看一下Log日志吧:

06-08 10:54:27.814: I/providertest(5681): update lineNumber :1
06-08 10:55:26.523: I/providertest(5681): name :A Storm of Swords
06-08 10:55:26.523: I/providertest(5681): author :GoogleDeveloper
06-08 10:55:26.523: I/providertest(5681): pages :1216
06-08 10:55:26.523: I/providertest(5681): price :24.58


4、当我点击Delete from Book 按钮的时候,让我们来看一下log日志吧:

06-08 10:55:48.323: I/providertest(5681): delete lineNumber :1


小笔记

ContentProvider是ANDROID系统的核心组件之一,用于向其它应用程序提供自身的数据的访问方式(可以包含增删改查),与其它系统组件相同,ContentProvider需要实现与系统的ContentProvider的继承关系,并且需要在AndroidManifest.xml文件中注册,注册时,除了android:name属性外,还必须配置android:authority属性,在偏高版本的ANDROID系统中,还需要配置android:exprotedandroid:enabled属性,均需要设置为true

其中,android:authority属性是该ContentProvider的“网址”,在全设备中应该是唯一的,其值也是其它的访问者对数据进行访问时必要的参数之一

注意:

1) 尽管ContentProivder中定义了增删改查的抽象方法,但是开发者可以选择性的具体实现,对于不希望提供数据共享的方法,可以不添加实际代码,或者直接抛出异常表示不支持

2) 尽管ContentProvider中实现增删改查的方法与SQLiteDatabase中的方法非常相似,但并不代表内容提供者的数据必须来源于SQLite数据库

URI

在使用ContentProvider时,需要使用到Uri类型的数据,该数据本质是一个字符串,格式为:content://authority/path

通过Uri Uri.parse(String uriString)可以将String转换为Uri格式的数据

通过Uri ContentUris.withAppendedId(Uri uri, long id)方法可以为已知的Uri添加id

通过long ContentUris.parseId(Uri uri)可以提取Uri中的id

一个Uri的实例调用getpathSegments()方法:

–getPathSegments得到uri的path部分,并拆分,去掉”/”,取到第一个元素(从第0个开始)。

比如这个项目中的:

content://com.example.sqlitetest.provider/book/1这个Uri的Path部分就是 /book/1,去掉”/”,就是 book/1,

getPathSegments.get(0) 得到的就是 book

getPathSegments.get(1)得到的就是 1
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: