內容提供者(ContentProvider)是Android系統四大組件之一,用於保存和檢索數據,是Android系統中不同應用程序之間共享數據的接口。在Android系統中,應用程序之間是相互獨立的,分別運行在自己的進程中,相互之間沒有數據交換。若應用程序之間需要共享數據,就需要用到ContentProvider。ContentProvider是不同應用程序之間進行數據交換的標準API,它以Uri的形式對外提供數據,允許其他應用操作本應用數據。其他應用則使用ContentResolver,並根據ContentProvider提供的Uri操作指定數據。接下來通過一個案例“讀取聯繫人信息”使用內容提供者暴露數據。該案例實現了查詢自己暴露的數據,並將數據捆綁到ListView控件中的功能。
activity.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/ll_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.lenovo.contentprovider.MainActivity">
<ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
</LinearLayout>
在layout文件夾下創建一個list_item.xml文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="60dip"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dip"
android:src="@drawable/default_avatar"/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="60dip"
android:layout_marginLeft="20dip"
android:gravity="center_vertical"
android:orientation="vertical">
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dip"
android:text="姓名"
android:textColor="#000000"
android:textSize="18sp"/>
<TextView
android:id="@+id/tv_phone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dip"
android:layout_marginTop="3dp"
android:text="電話"
android:textColor="#88000000"
android:textSize="16sp" />
</LinearLayout>
</LinearLayout>
在drawble文件夾下添加一張名爲default_avater的圖片作爲聯繫人的圖片
創建數據庫
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;
}
}
MainActivity
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<Person> 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<Person>();
//如果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
<provider
android:authorities="com.example.lenovo.db.personprovider"
android:name="com.example.lenovo.contentprovider.PersonDBProvider">
</provider>
運行程序能看到如圖所示