Android ContentProvider
應用場景:
在Android官方指出的Android的數據存儲方式總共有五種,分別是:Shared Preferences、網絡存儲、文件存儲、外儲存儲、SQLite。但是我們知道一般這些存儲都只是在單獨的一個應用程序之中達到一個數據的共享,而且這些知識在前面我都有介紹,有時候我們需要操作其他應用程序的一些數據,例如我們需要操作系統里的媒體庫、通訊錄等,這時我們就可能通過ContentProvider來滿足我們的需求了
ContentProvider概述:
ContentProvider向我們提供了我們在應用程序之前共享數據的一種機制,而我們知道每一個應用程序都是運行在不同的應用程序的,數據和文件在不同應用程序之間達到數據的共享不是沒有可能,而是顯得比較復雜,而正好Android中的ContentProvider則達到了這一需求,比如有時候我們需要操作手機里的聯系人,手機里的多媒體等一些信息,我們都可以用到這個ContentProvider來達到我們所需。
如何理解ContentProvider
上面說了一大堆ContentProvider的概述,可能大家還是不太特別理解ContentProvider到底是干什么的,那么我們以一個網站來形象的描述這個ContentProvider吧,可以這么理解為ContentProvider就是一個網站,它向我們去訪問網站這里的數據達到了一種可能,它就是一個向外提供數據的接口。那么既然它是向外提供數據,我們有時候也需要去修改數據,這時我們就可以用到另外一個類來實現這個對數據的修改ContentResolver類,這個類就可以通過URI來操作數據。至于這些類的作用及描述在下面就會一一的介紹到。
如何實現ContentProvider
理解了ContentProvider類,那么我們怎么去實現ContentProvider呢?怎么樣讓外部程序去訪問或者修改我們的數據呢?這樣的一個操作其實是非常簡單的,我們只需要下面的兩步就可以實現ContentProvider
1、? 編寫一個實現ContentProvider的子類,這個子類必須要實現一些必須實現的方法,在ContentProvider類里面,有一系列針對于數據的增、刪、改、查等方法
2、? ContentProvider也是Android中的四大組件,因此也必須在AndroidMainfest.xml中完成對ContentProvider的注冊。注冊方式為:
?
與ContentProvider相關操作的類介紹
從Uri談起
什么是Uri?
Uri是指通用資源標志符
A:前綴表明數據受控于一個內容提供者。它從不修改,也就是schema
B:是指在AndroidMainfest.xml中我們注冊的provider中的android:authorities屬性所對應的
C:具體操作于哪個條目
D:具體指定到哪個條目下的哪條記錄
再看它的類結構和常用方法:
Uri
在這個里它是沒有構造方法的,它通常通過下面的這個方法來返回一個Uri對象
?
|
方法名稱 |
描述 |
|
public static Uri parse (String uriString) |
通過一個傳入的字符串來構造一個Uri對象 |
?
熟悉完Uri類再看與之相關的另外兩個類
UriMatcher類:
因為Uri代表了要操作的數據,所以我們經常需要解析Uri,并從Uri中獲取數據。Android系統提供了兩個用于操作Uri的工具類,分別為UriMatcher 和ContentUris 。掌握它們的使用,會便于我們的開發工作。
先看下它比較常用的幾個方法:
?
|
方法名稱 |
描述 |
|
public void addURI (String authority, String path, int code) |
往UriMatcher類里添加一個拼湊的Uri,在此我們可以理解為UriMatcher為一個Uri的容器,為個容器里面包含著我們即將可能要操作的Uri,它用于我們業務邏輯的處理,特別是第三個參數code,如果通過下面的match()方法匹配成功就返回這個code值 |
|
public int match (Uri uri) |
與傳入的Uri匹配,它會首先與找我們之前通過addURI方法添加進來的Uri匹配,如果匹配成功就返回之前我們設置的code值,否則返回一個UriMatcher.NO_MATCH常量值為-1 |
?
熟悉完上面的方法,那么我們再來看它如何使用:
UriMatcher類用于匹配Uri,它的用法如下:
UriMatcher類的用法
首先第一步把你需要匹配Uri路徑全部給注冊上,如下:
//常量UriMatcher.NO_MATCH表示不匹配任何路徑的返回碼
UriMatcher? sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//如果match()方法匹配content:// com.jiahui.provider.myprovider/person路徑,返回匹配碼為1
sMatcher.addURI(“com.jiahui.provider.myprovider”, “person”, 1);//添加需要匹配uri,如果匹配就會返回匹配碼
//如果match()方法匹配content:// com.jiahui.provider.myprovider /person/230路徑,返回匹配碼為2
sMatcher.addURI(“com.jiahui.provider.myprovider”, “person/#”, 2);//#號為通配符
switch (sMatcher.match(Uri.parse("content:// com.jiahui.provider.myprovider /person/10" ) )) {
?? case 1
??? break;
?? case 2
??? break;
?? default://不匹配
??? break;
}
注冊完需要匹配的Uri后,就可以使用sMatcher.match(uri)方法對輸入的Uri進行匹配,如果匹配就返回匹配碼,匹配碼是調用addURI()方法傳入的第三個參數,假設匹配content://cn.itcast.provider.personprovider/person路徑,返回的匹配碼為1
再看另外一個工具類
ContentUris:
它用于在Uri后面追加一個ID或者解析出傳入的Uri所帶上的ID值,常用的兩個方法如下:
?
|
方法名稱 |
描述 |
|
public static Uri withAppendedId (Uri contentUri, long id) |
用于為路徑加上ID部分 |
|
public static long parseId (Uri contentUri) |
從路徑中獲取ID部分 |
?
熟悉完上面所提及的相關的類,接下來我們再看這個ContentProvider核心類
ContentProvider
常用方法
?
|
方法名稱 |
描述 |
|
public abstract boolean onCreate () |
在ContentProvider創建后被調用。 |
|
public abstract Uri insert (Uri uri, ContentValues values) |
根據Uri插入values對就的數據 |
|
public abstract int delete (Uri uri, String selection, String[] selectionArgs) |
根據Uri刪除selection指定的條件所匹配的全部記錄 |
|
public abstract int update (Uri uri, ContentValues values, String selection, String[] selectionArgs) |
根據Uri修改selection指定的條件所匹配的全部記錄 |
|
public abstract Cursor query (Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) |
根據Uri查詢出selection指定的條件所匹配的全部記錄,并且可以指定查詢哪些列(projection),以什么方式(sortOrder)排序 |
|
public abstract String getType (Uri uri) |
返回當前Uri所數據的MIME類型,如果該Uri對應的數據可能包括多條記錄,那么MIME類型字符串就是以vnd.android.cursor.dir/開頭,如果Uri對應的數據只包含一條記錄,那么MIME類型字符串就是以vnd.android.cursor.item/開頭 |
?
既然我們知道了ContentProvider類是向外提供數據的一種機制,那么在之前我們也說過要想來操作這個對外提供的數據,我們就用到了另外一個類:
ContentResolver
在這個類里面也定義了一系列的增、刪、改、查方法,與其ContentProvider定義的方法基本上相同,在此不再復核。讀者可以自己查閱相關文檔。
可能大家在這里還是有點理不清這些類的一些關系,特別是ContentResolver與ContentProvider與Uri類的關系,那么我上張圖吧,或許對大家有所幫助:
好了熟悉完上面所述的這么多類那么我們就在實踐中見證真理吧:
實例:
實現效果:
代碼實現:
?
先開發我們自己的ContentProvider:
千萬別忘記了要在AndroidMainfest.xml文件中注冊這個組件哦:
然后在一個主Activity編寫一些實現代碼:
package com.jiahui.provider;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.Toast;
import com.jiahui.model.Person;
public class ContentProviderDemoActivity extends Activity {
private Button btnadd, btnqueryall;
private EditText edtname, edtage;
private ListView lvall;
private List<Person> persons;
private SimpleAdapter simpleAdapter;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
List<Map<String, Object>> data = (List<Map<String, Object>>) msg.obj;
System.out.println(data.size());
simpleAdapter = new SimpleAdapter(
ContentProviderDemoActivity.this, data, R.layout.list_item,
new String[] { "id", "name", "age" }, new int[] {
R.id.tvId, R.id.tvname, R.id.tvage });
lvall.setAdapter(simpleAdapter);
}
};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
persons = new ArrayList<Person>();
btnqueryall = (Button) this.findViewById(R.id.btnqueryall);
btnadd = (Button) this.findViewById(R.id.btnadd);
edtname = (EditText) this.findViewById(R.id.edtname);
edtage = (EditText) this.findViewById(R.id.edtage);
lvall = (ListView) this.findViewById(R.id.lvall);
btnadd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ContentResolver contentResolver = ContentProviderDemoActivity.this
.getContentResolver();
Uri url = Uri
.parse("content://com.jiahui.provider.myprovider/person");
ContentValues values = new ContentValues();
values.put("name", edtname.getText().toString());
values.put("age", edtage.getText().toString());
Uri result = contentResolver.insert(url, values);
System.out.println(result.toString());
if (ContentUris.parseId(result)>0) {
Toast.makeText(ContentProviderDemoActivity.this, "添加成功", Toast.LENGTH_LONG).show();
//添加成功后再啟動線程查詢
MyThread thread = new MyThread(ContentProviderDemoActivity.this);
thread.start();
}
}
});
//查詢所有
btnqueryall.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MyThread thread = new MyThread(ContentProviderDemoActivity.this);
thread.start();
}
});
lvall.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// Toast.makeText(ContentProviderDemoActivity.this, position,
// Toast.LENGTH_LONG).show();
System.out.println("position:" + position);
Person person = persons.get(position);
Bundle bundle = new Bundle();
bundle.putInt("id", person.getId());
bundle.putString("name", person.getName());
bundle.putInt("age", person.getAge());
Intent intent = new Intent(ContentProviderDemoActivity.this,
ItemActivity.class);
intent.putExtra("item", bundle);
startActivityForResult(intent, 1);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode==2) {
MyThread thread = new MyThread(ContentProviderDemoActivity.this);
thread.start();
}
}
class MyThread extends Thread {
Context context;
public MyThread(Context context) {
//一定要清空。否則會 有問題,每執行一次都會把之前的全部的item加進去
persons.clear();
lvall.setAdapter(null);
this.context = context;
}
@Override
public void run() {
Uri url = Uri
.parse("content://com.jiahui.provider.myprovider/person");
Cursor cursor = context.getContentResolver().query(url,
new String[] { "_id", "name", "age" }, null, null, "_id");
while (cursor.moveToNext()) {
// System.out.println("_id:"
// + cursor.getInt(cursor.getColumnIndex("_id")));
// System.out.println("name:"
// + cursor.getString(cursor.getColumnIndex("name")));
// System.out.println("age:"
// + cursor.getInt(cursor.getColumnIndex("age")));
Person person = new Person();
person.setId(cursor.getInt(cursor.getColumnIndex("_id")));
person.setName(cursor.getString(cursor.getColumnIndex("name")));
person.setAge(cursor.getInt(cursor.getColumnIndex("age")));
persons.add(person);
}
cursor.close();
List<Map<String, Object>> data = new ArrayList<Map<String, Object>>();
Map<String, Object> map=null;
for (int i = 0; i < persons.size(); i++) {
map = new HashMap<String, Object>();
map.put("id", persons.get(i).getId());
map.put("name", persons.get(i).getName());
map.put("age", persons.get(i).getAge());
data.add(map);
}
if (data.size()>=persons.size()) {
}
Message msg = handler.obtainMessage();
msg.obj = data;
handler.sendMessage(msg);
}
}
}
ItemActivity代碼:
?
??
?
?在手機開發中SQLite的使用demo
簡單的bean類
package com.easyway.android.sql;
/**
* 普通JavaBean
* @author longgangbai
*
*/
public class CityBean {
public static final String ID = "_id";
public static final String CITY = "city";
public static final String CODE = "code";
private String id;
private String city;
private String code;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
?
?
界面展現類:
package com.easyway.android.sql;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
/**
*
*
* 本例實現SQLite數據庫增加、刪除、修改、模糊查詢操作。這里不是最好的實現方法,
* 如想研究SQL如何封裝,請詳細查看SQLiteDatebase類.
* 查看SQL語句:String sql = SQLiteQueryBuilder.buildQueryString();
*
*
* @author longgangbai
*/
public class AndroidSQL extends Activity {
private static String DB_NAME = "mycity.db";
private static int DB_VERSION = 1;
private static int POSTION;
private ListView listview;
private Cursor cursor;
private SQLiteDatabase db;
private SQLiteHelper dbHelper;
private ListAdapter listAdapter;
private EditText etCity;
private EditText etCode;
private Button bt_add;
private Button bt_modify;
private Button bt_query;
private List<CityBean> cityList = new ArrayList<CityBean>();
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
etCity = (EditText) findViewById(R.id.etCity);
etCode = (EditText) findViewById(R.id.etCode);
bt_add = (Button) findViewById(R.id.bt_add);
bt_modify = (Button) findViewById(R.id.bt_modify);
bt_query = (Button) findViewById(R.id.bt_query);
try{
/* 初始化并創建數據庫 */
dbHelper = new SQLiteHelper(this, DB_NAME, null, DB_VERSION);
/* 創建表 */
db = dbHelper.getWritableDatabase(); //調用SQLiteHelper.OnCreate()
/* 查詢表,得到cursor對象 */
cursor = db.query(SQLiteHelper.TB_NAME, null, null, null, null, null, CityBean.CODE + " DESC");
cursor.moveToFirst();
while(!cursor.isAfterLast() && (cursor.getString(1) != null)){
CityBean city = new CityBean();
city.setId(cursor.getString(0));
city.setCity(cursor.getString(1));
city.setCode(cursor.getString(2));
cityList.add(city);
cursor.moveToNext();
}
}catch(IllegalArgumentException e){
//當用SimpleCursorAdapter裝載數據時,表ID列必須是_id,否則報錯column '_id' does not exist
e.printStackTrace();
//當版本變更時會調用SQLiteHelper.onUpgrade()方法重建表 注:表以前數據將丟失
++ DB_VERSION;
dbHelper.onUpgrade(db, --DB_VERSION, DB_VERSION);
// dbHelper.updateColumn(db, SQLiteHelper.ID, "_"+SQLiteHelper.ID, "integer");
}
listview = (ListView)findViewById(R.id.listView);
listAdapter = new ListAdapter();
listview.setAdapter(listAdapter);
listview.setOnItemClickListener(new ListView.OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> parent, View view, int postion,
long arg3) {
setSelectedValues(postion);
}
});
/* 插入表數據并ListView顯示更新 */
bt_add.setOnClickListener(new Button.OnClickListener(){
@Override
public void onClick(View arg0) {
if(etCity.getText().length() > 1 && etCode.getText().length() >1){
ContentValues values = new ContentValues();
values.put(CityBean.CITY, etCity.getText().toString().trim());
values.put(CityBean.CODE, etCode.getText().toString().trim());
//插入數據 用ContentValues對象也即HashMap操作,并返回ID號
Long cityID = db.insert(SQLiteHelper.TB_NAME, CityBean.ID, values);
CityBean city = new CityBean();
city.setId(""+cityID);
city.setCity(etCity.getText().toString().trim());
city.setCode(etCode.getText().toString().trim());
cityList.add(city);
listview.setAdapter(new ListAdapter());
resetForm();
}
}
});
/* 查詢表,模糊條件查詢 */
bt_query.setOnClickListener(new Button.OnClickListener(){
@Override
public void onClick(View view) {
cityList.removeAll(cityList);
String sql = null;
String sqlCity = etCity.getText().length() > 0 ? CityBean.CITY + " like '%" + etCity.getText().toString().trim() + "%'" : "";
String sqlCode = etCode.getText().length() > 0 ? CityBean.CITY + " like '%" + etCity.getText().toString().trim() + "%'" : "";
if( (!"".equals(sqlCity)) && (!"".equals(sqlCode)) ){
sql = sqlCity + " and" + sqlCode;
}else if(!"".equals(sqlCity)){
sql = sqlCity;
}else if(!"".equals(sqlCode)){
sql = sqlCode;
}
cursor = db.query(true, SQLiteHelper.TB_NAME,
new String[]{CityBean.ID, CityBean.CITY, CityBean.CODE},
sql,
null, null, null, null, null);
cursor.moveToFirst();
while(!cursor.isAfterLast() && (cursor.getString(1) != null)){
CityBean city = new CityBean();
city.setId(cursor.getString(0));
city.setCity(cursor.getString(1));
city.setCode(cursor.getString(2));
cityList.add(city);
cursor.moveToNext();
}
listview.setAdapter(new ListAdapter());
resetForm();
}
});
/* 修改表數據 */
bt_modify.setOnClickListener(new Button.OnClickListener(){
@Override
public void onClick(View arg0) {
ContentValues values = new ContentValues();
values.put(CityBean.CITY, etCity.getText().toString().trim());
values.put(CityBean.CODE, etCode.getText().toString().trim());
db.update(SQLiteHelper.TB_NAME, values, CityBean.ID + "=" + cityList.get(POSTION).getId(), null);
cityList.get(POSTION).setCity(etCity.getText().toString().trim());
cityList.get(POSTION).setCode(etCode.getText().toString().trim());
listview.setAdapter(new ListAdapter());
resetForm();
}
});
}
/* 設置選中ListView的值 */
public void setSelectedValues(int postion){
POSTION = postion;
etCity.setText(cityList.get(postion).getCity());
etCode.setText(cityList.get(postion).getCode());
}
/* 重值form */
public void resetForm(){
etCity.setText("");
etCode.setText("");
}
@Override
protected void onDestroy() {
db.delete(SQLiteHelper.TB_NAME, null, null);
super.onDestroy();
}
private class ListAdapter extends BaseAdapter{
public ListAdapter(){
super();
}
@Override
public int getCount() {
return cityList.size();
}
@Override
public Object getItem(int postion) {
return postion;
}
@Override
public long getItemId(int postion) {
return postion;
}
@Override
public View getView(final int postion, View view, ViewGroup parent) {
view = getLayoutInflater().inflate(R.layout.listview, null);
TextView tv = (TextView) view.findViewById(R.id.tvCity);
tv.setText("" + cityList.get(postion).getCity());
TextView bu = (TextView) view.findViewById(R.id.btRemove);
bu.setText(R.string.delete);
bu.setId(Integer.parseInt(cityList.get(postion).getId()));
/* 刪除表數據 */
bu.setOnClickListener(new Button.OnClickListener(){
@Override
public void onClick(View view) {
try{
db.delete(SQLiteHelper.TB_NAME, CityBean.ID + "=" + view.getId(), null);
cityList.remove(postion);
listview.setAdapter(new ListAdapter());
}catch(Exception e){
e.printStackTrace();
}
}
});
return view;
}
}
}
?
?
?
SQLite實現類:
package com.easyway.android.sql;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
/**
* 實現對表的創建、更新、變更列名操作
*
* 在Android 中針對少量數據的操作在SQLite操作實現相關功能功能
* ,但是必須繼承SQLiteOpenHelper,實現相關的功能。
*
*
* @author longgangbai
*
*/
public class SQLiteHelper extends SQLiteOpenHelper {
public static final String TB_NAME = "citys";
public SQLiteHelper(Context context, String name, CursorFactory factory,
int version) {
super(context, name, factory, version);
}
/**
* 創建新表
*/
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS " +
TB_NAME + "(" +
CityBean.ID + " integer primary key," +
CityBean.CITY + " varchar," +
CityBean.CODE + " integer"+
")");
}
/**
* 當檢測與前一次創建數據庫版本不一樣時,先刪除表再創建新表
*/
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TB_NAME);
onCreate(db);
}
/**
* 變更列名
* @param db
* @param oldColumn
* @param newColumn
* @param typeColumn
*/
public void updateColumn(SQLiteDatabase db, String oldColumn, String newColumn, String typeColumn){
try{
db.execSQL("ALTER TABLE " +
TB_NAME + " CHANGE " +
oldColumn + " "+ newColumn +
" " + typeColumn
);
}catch(Exception e){
e.printStackTrace();
}
}
}
?
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

