之前说到的持久化技术只能提供本程序使用,是不能分享给其他程序使用的。比如电话簿信息,短信,媒体库等。都得实现跨程序数据分享的功能。这时候就涉及到内容提供器了。
Content Provider主要用于在不同应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另外一个程序中的数据,同时保证被访问数据的安全性。
内容提供器用法有两种:
(1)利用现有的内容提供器获取其它程序里面的数据。
(2)创建自己的内容提供器,供其它程序访问我们的数据。
如果已经会使用SQLite了,那么使用ContentProvider的套路都是一样的,只是某些参数不太一样。
下面通过一个小例子说明,我们想在自己的应用中获取手机联系人信息:
首先,设计布局如下:
<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"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<ListView
android:id="@+id/contactslist"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
</ListView>
</RelativeLayout>
活动中代码如下:
public class MainActivity extends Activity {
private ListView contactList;
List<String> list = new ArrayList<String>();
ArrayAdapter<String> adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
contactList = (ListView)findViewById(R.id.contactslist);
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,list);
contactList.setAdapter(adapter);
readContacts();
}
private void readContacts(){
Cursor cursor = null;
cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
//第一个参数为路径,系统默认了一些路径
//路径格式为:content://com.example.app.provider/table1 协议://一般是包名/数据表名
while(cursor.moveToNext()){
//获取用户名和 手机号
String name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
list.add(name+"\n"+number);
}
if(cursor != null){
cursor.close();
}
}
}
获取联系人信息,也需要权限申请:
<uses-permission android:name="android.permission.READ_CONTACTS"/>
ok! 这样就可以获取到手机联系人信息了。 总结起来就是知道其它程序的数据访问路径,然后调用接口就ok了。
那么既然知道如何获取其它应用的数据了,如何对外暴露自身的数据呢?基于之前的SQLite项目进行修改,希望对外提供数据操作接口。
那么首先需要编写一个ContentProvider:
//对外提供内容共享接口
public class MyContentProvider extends ContentProvider {
public static final int BOOK_DIR = 0;
public static final int BOOK_ITEM = 1;
public static final int CATEGORY_DIR = 2;
public static final int CATEGORY_ITEM = 3;
//这些常量用于定义外人访问数据的类型,是访问整个表还是访问里面的某一条数据
public static final String AUTHORITY = "com.example.databasetest.provider";
//用于方法getType中,组织资源的MIME编码
public static UriMatcher uriMatcher;
//用于匹配URI的时候使用,判断别人调用数据的时候选择的是什么内容
private MyDatabaseHelper dbHelper;
//用于后续的操作数据库
static{
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);
uriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);
uriMatcher.addURI(AUTHORITY, "category", BOOK_DIR);
uriMatcher.addURI(AUTHORITY, "category/#", BOOK_DIR);
} //初始化所有的URI
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
@Override
public String getType(Uri uri) {
//针对uri给出每一个内容的MIME编码
switch(uriMatcher.match(uri)){
case BOOK_DIR:
return "vnd.android.cursor.dir/vnd.com.example.databasetest.provider.book";
//对于目录型的MIME 格式:vnd + ".android.cursor.dir" + "/vnd" + authority + "数据表名"
case BOOK_ITEM:
return "vnd.android.cursor.item/vnd.com.example.databasetest.provider.book";
//对于涉及到具体数据 其MIME编码也有一定的格式
case CATEGORY_DIR:
return "vnd.android.cursor.dir/vnd.com.example.databasetest.provider.category";
case CATEGORY_ITEM:
return "vnd.android.cursor.item/vnd.com.example.databasetest.provider.category";
}
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
//参数:uri 插入数据
SQLiteDatabase db = dbHelper.getWritableDatabase();
//获取数据库操作对象
Uri uriReturn = null;
switch (uriMatcher.match(uri)) {
case BOOK_ITEM:
long newBookId = db.insert("Book", null, values);
uriReturn = Uri.parse("content://"+AUTHORITY+"/book/"+newBookId);
break;
case CATEGORY_ITEM:
long categoryId = db.insert("Category", null, values);
uriReturn = Uri.parse("content://"+AUTHORITY+"/category/"+categoryId);
break;
default:
break;
}
//从上面的代码可以看出,其实思路都非常的简单,就是先通过URI判断其他人想要干什么事情,然后通过本地的数据库操作,修改相应的数据。
//其实就是该类说自己可以对外提供数据服务,但是到了实际操作时还是依赖于数据库操作类
return uriReturn;
}
@Override
public boolean onCreate() {
//初始化的时候调用 当被那些ContentResolver访问数据时就执行该函数
dbHelper = new MyDatabaseHelper(getContext(), "BookStore.db", null, 1);
return true;
}
@Override
public Cursor query(Uri uri, String[] columns, String selection, String[] selectionArgs,
String sortOrder) {
//数据地址 访问列 选择条件 选择数据 排序方式
return null;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
}
我既然对外提供服务,那么就要在配置文件中配置信息如下:
<provider android:name="com.example.databasetest.MyContentProvider"
android:authorities="com.example.databasetest.provider"></provider>
在另外的程序中,如何消费这一个数据服务?
addBook = (Button)findViewById(R.id.addBook);
addBook.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Uri uri = Uri.parse("content://com.example.databasetest.provider/book");
//明确资源路径 在哪增加数据
ContentValues values = new ContentValues();
values.put("name", "官路风流");
values.put("pages", 200);
getContentResolver().insert(uri, values);
}
});
这样就ok了。