内容提供者(ContentProvider)是Android系统四大组件之一,用于保存和检索数据,是Android系统中不同应用程序之间共享数据的接口。在Android系统中,应用程序之间是相互独立的,分别运行在自己的进程中,相互之间没有数据交换。若应用程序之间需要共享数据,就需要用到ContentProvider。ContentProvider是不同应用程序之间进行数据交换的标准API,它以Uri的形式对外提供数据,允许其他应用操作本应用数据。其他应用则使用ContentResolver,并根据ContentProvider提供的Uri操作指定数据。接下来通过一个案例“读取联系人信息”使用内容提供者暴露数据。该案例实现了查询自己暴露的数据,并将数据捆绑到ListView控件中的功能。
activity.xml
创建数据库
package com.example.lenovo.contentprovider;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
/**
* Created by lenovo on 2017/6/8.
*/
public class PersonSQLiteOpenHelper extends SQLiteOpenHelper {
private static final String TAG="PersonSQLiteOpenHelper";
//数据库的构造方法,用来定义数据库的名称、数据库查询的结果集、数据库的版本
public PersonSQLiteOpenHelper(Context context){
super(context,"person.db",null,3);
}
//数据库第一次被创建的时候调用的方法
public void onCreate(SQLiteDatabase db){
//初始化数据库的表结构
db.execSQL("create table person(id integer primary key autoincrement,name varchar(20),number varchar(20))");
}
//当数据库的版本号发生变化的时候(增加的时候)调用
public void onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion){
Log.i(TAG,"数据库的版本变化了......");
}
}
创建Person类,用于封装id、name和number属性
package com.example.lenovo.contentprovider;
/**
* Created by lenovo on 2017/6/8.
*/
public class Person {
private int id;
private String name;
private String number;
public Person() {
}
public String toString() {
return "Person [id=" + id + ", name=" + name + ", number=" + number
+ "]";
}
public Person(int id, String name, String number) {
this.id = id;
this.name = name;
this.number = number;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
}
创建内容提供者
创建PersonDBProvider类继承ContentProvider,用于实现暴露数据库程序的功能
package com.example.lenovo.contentprovider;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
/**
* Created by lenovo on 2017/6/8.
*/
public class PersonDBProvider extends ContentProvider {
// 定义一个uri的匹配器 用于匹配uri 如果路径不满足条件 返回 -1
private static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
private static final int INSERT = 1; //添加数据匹配Uri路径成功时返回码
private static final int DELETE = 2; //删除数据匹配Uri路径成功时返回码
private static final int UPDATE = 3; //更改数据匹配Uri路径成功时返回码
private static final int QUERY = 4; //查询数据匹配Uri路径成功时返回码
private static final int QUERYONE = 5; //查询一条数据匹配Uri路径成功时返回码
//数据库操作类的对象
private PersonSQLiteOpenHelper helper;
static {
// 添加一组匹配规则.
matcher.addURI("com.example.lenovo.db.personprovider", "insert", INSERT);
matcher.addURI("com.example.lenovo.db.personprovider", "delete", DELETE);
matcher.addURI("com.example.lenovo.db.personprovider", "update", UPDATE);
matcher.addURI("com.example.lenovo.db.personprovider", "query", QUERY);
//这里的“#”号为通配符凡是符合”query/”皆返回QUERYONE的返回码
matcher.addURI("com.example.lenovo.contentprovider.personprovider", "query/#", QUERYONE);
}
//当内容提供者被创建的时候 调用 适合 数据的初始化
public boolean onCreate() {
helper = new PersonSQLiteOpenHelper(getContext());
return false;
}
//查询数据操作
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
if (matcher.match(uri) == QUERY) { //匹配查询的Uri路径
//匹配成功 ,返回查询的结果集
SQLiteDatabase db = helper.getReadableDatabase();
//调用数据库操作的查询数据的方法
Cursor cursor = db.query("person", projection, selection,
selectionArgs, null, null, sortOrder);
return cursor;
} else if (matcher.match(uri) == QUERYONE) {
//匹配成功,根据id查询数据
long id = ContentUris.parseId(uri);
SQLiteDatabase db = helper.getReadableDatabase();
Cursor cursor = db.query("person", projection, "id=?",
new String[]{id+""}, null, null, sortOrder);
return cursor;
} else {
throw new IllegalArgumentException("路径不匹配,不能执行查询操作");
}
}
//获取当前Uri的数据类型
public String getType(Uri uri) {
if (matcher.match(uri) == QUERY) {
// 返回查询的结果集
return "vnd.android.cursor.dir/person";
} else if (matcher.match(uri) == QUERYONE) {
return "vnd.android.cursor.item/person";
}
return null;
}
//添加数据
public Uri insert(Uri uri, ContentValues values) {
if (matcher.match(uri) == INSERT) {
//匹配成功 返回查询的结果集
SQLiteDatabase db = helper.getWritableDatabase();
db.insert("person", null, values);
} else {
throw new IllegalArgumentException("路径不匹配,不能执行插入操作");
}
return null;
}
//删除数据
public int delete(Uri uri, String selection, String[] selectionArgs) {
if (matcher.match(uri) == DELETE) {
//匹配成功 返回查询的结果集
SQLiteDatabase db = helper.getWritableDatabase();
db.delete("person", selection, selectionArgs);
} else {
throw new IllegalArgumentException("路径不匹配,不能执行删除操作");
}
return 0;
}
//更新数据
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
if (matcher.match(uri) == UPDATE) {
//匹配成功 返回查询的结果集
SQLiteDatabase db = helper.getWritableDatabase();
db.update("person", values, selection, selectionArgs);
} else {
throw new IllegalArgumentException("路径不匹配,不能执行修改操作");
}
return 0;
}
}
从上述代码中可以看出,在暴露数据的增、删、改、查方法之前,首先需要添加一组用于请求数据操作的Uri,然后在相应的增删改查方法中匹配Uri,匹配成功才能对数据进行操作。
创建数据库逻辑操作类
package com.example.lenovo.contentprovider;
import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
/**
* Created by lenovo on 2017/6/8.
*/
public class PersonDao2 {
private PersonSQLiteOpenHelper helper;
//在构造方法里面完成helper的初始化
public PersonDao2(Context context){
helper=new PersonSQLiteOpenHelper(context);
}
//添加一条记录到数据库
public long add(String name,String number,int money){
SQLiteDatabase db=helper.getWritableDatabase();
ContentValues values=new ContentValues();
values.put("name",name);
values.put("number",number);
long id=db.insert("person",null,values);
db.close();
return id;
}
}
package com.example.lenovo.contentprovider;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private ListView lv;
private List persons ;
//创建一个Handler对象用于线程间通信
private Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case 100://接收到数据查询完毕的消息
//UI线程适配ListView
lv.setAdapter(new MyAdapter());
break;
}
};
};
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv = (ListView) findViewById(R.id.lv);
//由于添加数据、查询数据是比较耗时的,因此需要在子线程中做这两个操作
new Thread(){
public void run() {
//添加数据
AddData();
//获取persons集合
getPersons();
//如果查询到数据 则向UI线程发送消息
if(persons.size() > 0){
handler.sendEmptyMessage(100);
}
};
}.start();
}
//往person表中插入10条数据
public void AddData(){
PersonDao2 dao = new PersonDao2(this);
long number = 885900000l;
Random random = new Random();
for(int i=0;i<10;i++){
dao.add("wangwu"+i, Long.toString(number+i), random.nextInt(5000));
}
}
//利用ContentResolver对象查询本应用程序使用ContentProvider暴露的数据
private void getPersons() {
//首先要获取查询的uri
String url = "content://com.example.lenovo.db.personprovider/query";
Uri uri = Uri.parse(url);
//获取ContentResolver对象 这个对象的使用后面会详细讲解
ContentResolver contentResolver = getContentResolver();
//利用ContentResolver对象查询数据得到一个Cursor对象
Cursor cursor = contentResolver.query(uri, null, null, null, null);
persons = new ArrayList();
//如果cursor为空立即结束该方法
if(cursor == null){
return;
}
while(cursor.moveToNext()){
int id = cursor.getInt(cursor.getColumnIndex("id"));
String name = cursor.getString(cursor.getColumnIndex("name"));
String number = cursor.getString(cursor.getColumnIndex("number"));
Person p = new Person(id, name, number);
persons.add(p);
}
cursor.close();
}
//适配器
private class MyAdapter extends BaseAdapter{
private static final String TAG = "MyAdapter";
// 控制listview里面总共有多个条目
public int getCount() {
return persons.size(); //条目个数 == 集合的size
}
public Object getItem(int position) {
return persons.get(position);
}
public long getItemId(int position) {
return 0;
}
public View getView(int position, View convertView, ViewGroup parent) {
//得到某个位置对应的person对象
Person person = persons.get(position);
View view = View.inflate(MainActivity.this, R.layout.list_item, null);
//一定要在view对象里面寻找孩子的id
//姓名
TextView tv_name = (TextView) view.findViewById(R.id.tv_name);
tv_name.setText("姓名:"+person.getName());
//电话
TextView tv_phone = (TextView) view.findViewById(R.id.tv_phone);
tv_phone.setText("电话:"+person.getNumber());
return view;
}
}
}
上述代码使用ContentResolver查询本程序使用ContentProvider暴露的数据。
需要注意的是,当执行耗时操作时,应创建一个子线程将耗时操作放在子线程中,然后使用handler实现子线程与UI线程的通信。
在清单文件中注册PersonDBProvider
运行程序能看到如图所示