ContentProvider(内容提供者)是Android中的四大组件之一,主要用于对外共享数据库。
数据可以存储于文件系统、SQLite数据库或SharePreferences。 ContentProvider的数据主要存储于SQLite数据库中。 内容提供者继承于ContentProvider 基类,为其它应用程序取用和存储它管理的数据实现了一套标准方法。
然而,应用程序并不直接调用这些方法,而是使用一个 ContentResolver 对象,调用它的方法作为替代。ContentResolver可以与任意ContentProvider进行会话,与其合作来对所有相关交互通讯进行管理。
ContentResolver通过指定的URI来确定与其通信的是哪一个ContentResolver对象。
1、写一个Provider继承系统的ContentProvider
AndroidManifest.xml,这里的authorities作为每个ContentProvider的唯一标识符(一般是以包名加类名的方式命名)。类似我们访问网页的时候,输入的网址。
<provider
android:name="com.android.pbs.PBSProvider"
android:authorities="com.android.pbs.Provider"
android:exported="true" />
PBSProvider这个类是继承ContentProvider,需要实现ContentProvider的6个接口,即 onCreate()、 getType()、 insert()、delete()、 query()、 update()。
包含数据库的插入,删除,查询,更新所有动作。具体的实现如下:
package com.android.pbs;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
public class PBSProvider extends ContentProvider {
private static final String TAG = "PBSProvider";
private DatebaseHelper mDBOpenHelper = null;
private SQLiteDatabase mDb;
private static final int MULTIPLE = 1;
private static final int SINGLE = 2;
private static final UriMatcher mUriMatcher;
static {
mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
mUriMatcher.addURI(DBInfo.AUTHORITY, DBInfo.PATH_MULTIPLE, MULTIPLE);
mUriMatcher.addURI(DBInfo.AUTHORITY, DBInfo.PATH_SINGLE, SINGLE);
}
@Override
public boolean onCreate() {
Context context = getContext();
mDBOpenHelper = new DatebaseHelper(context, DBInfo.DB_NAME, DBInfo.DB_VERSION);
mDb = mDBOpenHelper.getWritableDatabase();
if (mDb == null) {
XLog.i(TAG, "getWritableDatabase() fail!");
return false;
}
XLog.i(TAG, "onCreate()");
return true;
}
public String getType(Uri uri) {
XLog.i(TAG, "getType()");
switch (mUriMatcher.match(uri)) {
case MULTIPLE:
return DBInfo.MIME_TYPE_MULTIPLE;
case SINGLE:
return DBInfo.MIME_TYPE_SINGLE;
default:
XLog.i(TAG, "unknow uri");
return null;
}
}
@Override
public Uri insert(Uri uri, ContentValues values) {
XLog.i(TAG, "insert()");
try {
long id = mDb.insert(DBInfo.DB_TABLE, null, values);
if (id > 0) {
Uri newUri = ContentUris.withAppendedId(DBInfo.CONTENT_URI, id);
getContext().getContentResolver().notifyChange(newUri, null);
return newUri;
}
} catch (SQLException e) {
XLog.i(TAG, " error insert values fail!");
}
return uri;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int type = mUriMatcher.match(uri);
XLog.i(TAG, "delete() type = " + type);
int count = 0;
try {
switch (type) {
case MULTIPLE:
count = mDb.delete(DBInfo.DB_TABLE, selection, selectionArgs);
break;
case SINGLE:
String segment = uri.getPathSegments().get(1);
count = mDb.delete(DBInfo.DB_TABLE, DBInfo.KEY_ID + "=" + segment, selectionArgs);
break;
default:
XLog.i(TAG, " unknow uri!");
return count;
}
} catch (SQLException e) {
XLog.i(TAG, " error, delete fail!");
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
int type = mUriMatcher.match(uri);
XLog.i(TAG, "query() type = " + type);
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(DBInfo.DB_TABLE);
switch (type) {
case SINGLE:
qb.appendWhere(DBInfo.KEY_ID + "=" + uri.getPathSegments().get(1));
break;
default:
break;
}
Cursor cursor = null;
try {
cursor = qb.query(mDb, projection, selection, selectionArgs, null, null, sortOrder);
cursor.setNotificationUri(getContext().getContentResolver(), uri);
} catch (SQLException e){
XLog.i(TAG, "error, query fail!");
}
return cursor;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
int count = 0;
int type = mUriMatcher.match(uri);
XLog.i(TAG, "update() type = " + type);
try {
switch (type) {
case MULTIPLE:
count = mDb.update(DBInfo.DB_TABLE, values, selection, selectionArgs);
break;
case SINGLE:
String segment = uri.getPathSegments().get(1);
count = mDb.update(DBInfo.DB_TABLE, values, DBInfo.KEY_ID + "=" + segment, selectionArgs);
break;
default:
XLog.i(TAG, "unknow uri");
return count;
}
} catch (SQLException e) {
XLog.i(TAG, "error,update fail");
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
}
Provider通过Android已经封装好的SQLiteOpenHelper对象来创建数据库和表格。
并且通过SQLiteOpenHelper的getWritableDatabase()得到SQLiteDatabase对象,这样子就可以对数据库操作了。
2、对应的SQLiteOpenHelper的实现如下:
package com.android.pbs;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
public class DatebaseHelper extends SQLiteOpenHelper {
private final String TAG = "DatebaseHelper";
private static final String DB_CREATE = "create table " + DBInfo.DB_TABLE
+ "(" + DBInfo.KEY_ID + " integer primary key autoincrement, "
+ DBInfo.KEY_SOURCE + " text not null, "
+ DBInfo.KEY_CHANNEL + " integer, "
+ DBInfo.KEY_STATE + " text);";
public DatebaseHelper(Context context, String dbName) {
this(context, dbName, DBInfo.DB_VERSION);
}
public DatebaseHelper(Context context, String dbName, int version) {
this(context, dbName, null, version);
}
public DatebaseHelper(Context context, String dbName, CursorFactory factory, int version) {
super(context, dbName, factory, version);
XLog.i(TAG, "DatebaseHelper() name = " + dbName + " version = " + version);
}
@Override
public void onCreate(SQLiteDatabase db) {
XLog.i(TAG, "onCreate()");
db.execSQL(DB_CREATE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int arg1, int arg2) {
XLog.i(TAG, "onUpgrade()");
db.execSQL("DROP TABLE IF EXISTS " + DBInfo.DB_TABLE);
onCreate(db);
}
}
2.1 首先是通过父类的SQLiteOpenHelper()构造函数来创建对应的.db文件,传入对应的db的名字和版本号。
2.2 然后在通过 SQLiteDatabase的execSQL来创建对应的表格 table。创建table的时候,表格里有那几项和对应的数据格式确定下来。
这样子对应的数据库的.db文件和对应的table就都创建好了。
同时Provider里面也已经写好了对数据库进行删除,插入,查询和更新的接口。就等具体的应用来调用了。
3、应用程序并不直接调用provider写好的这些方法。而是使用一个 ContentResolver 对象,调用它的方法作为替代。
怎么确保一定调用到我们写好的Provider? 答案是通过URI来确定。
在AndroidManifest.xml里面我们写好了Provide的唯一标识符 android:authorities="com.android.pbs.Provider"
ContentResolver在调用Provide的接口的时候,需要传一个URI,这个URI里面包含了我们Provide的唯一标识符。
3.1 我们事先把需要传入的URI先定义好,使用的时候不容易出错。
DBInfo.java
public static final String AUTHORITY = "com.android.pbs.Provider";
public static final String PATH_SINGLE = "pbsinfo/#";
public static final String PATH_MULTIPLE = "pbsinfo";
public static final String CONTENT_URI_STRING = "content://" + AUTHORITY + "/" + PATH_MULTIPLE;
public static final Uri CONTENT_URI = Uri.parse(CONTENT_URI_STRING);
3.2 查询的时候,只要填入正确的URI和对应的要查询的条件,通过Resolver的query()接口就可以了。
private String getSqlSelectionString() {
String selection = DBInfo.KEY_SOURCE + " = ?" + " AND " + DBInfo.KEY_CHANNEL + " = ?";
XLog.i(TAG, "getSqlSelectionString() " + selection);
return selection;
}
private String[] getSqlSelectArgs() {
mTvSource = mSourceSpinner.getSelectedItem().toString();
mChannel = mChannelSpinner.getSelectedItem().toString();
XLog.i(TAG, "getSqlSelectArgs() source = " + mTvSource);
XLog.i(TAG, "getSqlSelectArgs() channel = " + mChannel);
String[] selectionArg = new String[] {mTvSource, mChannel};
return selectionArg;
}
....
case R.id.btn_query: {
String selection = getSqlSelectionString();
String[] selectionArg = getSqlSelectArgs();
Cursor cursor = mContentResolver.query(DBInfo.CONTENT_URI,
new String[] {DBInfo.KEY_ID, DBInfo.KEY_STATE},
selection, selectionArg, null);
if (!cursor.moveToFirst()) {
XLog.i(TAG, "no record !");
String no_record = mResources.getString(R.string.no_record);
String info = mQueryStr + ":" + mTvSource + " " + mChannel + " " + no_record;
updateInfoView(info);
return;
}
String state;
do {
String id = cursor.getString(cursor.getColumnIndex(DBInfo.KEY_ID));
state = cursor.getString(cursor.getColumnIndex(DBInfo.KEY_STATE));
XLog.i(TAG, "id = " + id);
XLog.i(TAG, "state = " + state);
} while (cursor.moveToNext());
cursor.close();
String info = mQueryStr + ":" + mTvSource + " " + mChannel + " " + state;
updateInfoView(info);
return;
}
在这里我们是查询数据库的KEY_ID 和KEY_STATE两个字段。
Cursor cursor = mContentResolver.query(DBInfo.CONTENT_URI,
new String[] {DBInfo.KEY_ID, DBInfo.KEY_STATE},
selection, selectionArg, null)
查询完后,我们可以通过cursor.moveToFirst()是否为空来判断,数据库里面是否有对应的数据。
最后通过cursor.getString(cursor.getColumnIndex(" "))就可以拿到对应项的数据了。
4、应用通过ContentResolver的其他接口对provider的相关操作,基本上都是差不多的。
完整的MainActivity的code如下:
package com.android.pbs;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.res.Resources;
import android.database.Cursor;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ScrollView;
import android.widget.Spinner;
import android.widget.TextView;
import java.text.SimpleDateFormat;
public class MainActivity extends Activity implements OnClickListener {
private static final String TAG = "MainActivity";
private Spinner mSourceSpinner = null;
private Spinner mChannelSpinner = null;
private Spinner mStateSpinner = null;
private Button mInsertBtn = null;
private Button mQueryBtn = null;
private Button mDeleteBtn = null;
private Button mUpdateBtn = null;
private TextView mInfo = null;
private ScrollView mScrollView = null;
private String mTvSource = null;
private String mChannel = null;
private String mState = null;
private Resources mResources = null;
private String mInsertStr = null;
private String mQueryStr = null;
private String mUpdateStr = null;
private String mDeleteStr = null;
private ContentResolver mContentResolver = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
XLog.i(TAG, "onCreate()");
setContentView(R.layout.activity_main);
findViews();
setListeners();
initData();
}
private void initData() {
mContentResolver = getContentResolver();
mResources = getResources();
mInsertStr = mResources.getString(R.string.insert);
mQueryStr = mResources.getString(R.string.query);
mUpdateStr = mResources.getString(R.string.update);
mDeleteStr = mResources.getString(R.string.delete);
mInfo.setText("");
}
private void findViews() {
// mStateItem = (LinearLayout) findViewById(R.id.state_item);
mSourceSpinner = (Spinner) findViewById(R.id.sp_tv_source);
mChannelSpinner = (Spinner) findViewById(R.id.sp_tv_channel);
mStateSpinner = (Spinner) findViewById(R.id.sp_status);
mInsertBtn = (Button) findViewById(R.id.btn_insert);
mQueryBtn = (Button) findViewById(R.id.btn_query);
mDeleteBtn = (Button) findViewById(R.id.btn_delete);
mUpdateBtn = (Button) findViewById(R.id.btn_update);
mInfo = (TextView) findViewById(R.id.info);
mScrollView = (ScrollView) findViewById(R.id.scroll_view);
}
private void setListeners() {
mInsertBtn.setOnClickListener(this);
mQueryBtn.setOnClickListener(this);
mUpdateBtn.setOnClickListener(this);
mDeleteBtn.setOnClickListener(this);
}
private String getSqlSelectionString() {
String selection = DBInfo.KEY_SOURCE + " = ?" + " AND " + DBInfo.KEY_CHANNEL + " = ?";
XLog.i(TAG, "getSqlSelectionString() " + selection);
return selection;
}
private String[] getSqlSelectArgs() {
mTvSource = mSourceSpinner.getSelectedItem().toString();
mChannel = mChannelSpinner.getSelectedItem().toString();
XLog.i(TAG, "getSqlSelectArgs() source = " + mTvSource);
XLog.i(TAG, "getSqlSelectArgs() channel = " + mChannel);
String[] selectionArg = new String[] {mTvSource, mChannel};
return selectionArg;
}
@Override
public void onClick(View view) {
int viewId = view.getId();
XLog.i(TAG, "onClick() id = 0X" + Integer.toHexString(viewId));
switch (viewId) {
case R.id.btn_insert: {
mTvSource = mSourceSpinner.getSelectedItem().toString();
mChannel = mChannelSpinner.getSelectedItem().toString();
mState = mStateSpinner.getSelectedItem().toString();
ContentValues values = new ContentValues();
values.put(DBInfo.KEY_SOURCE, mTvSource);
values.put(DBInfo.KEY_CHANNEL, mChannel);
values.put(DBInfo.KEY_STATE, mState);
String selection = getSqlSelectionString();
String[] selectionArg = getSqlSelectArgs();
Cursor cursor = mContentResolver.query(DBInfo.CONTENT_URI,
new String[] {DBInfo.KEY_STATE}, selection, selectionArg, null);
//has record,just update value
if (cursor.moveToFirst()) {
int result = mContentResolver.update(DBInfo.CONTENT_URI, values, selection, selectionArg);
String resultStr = mResources.getString(R.string.fail);
if (result > 0) {
resultStr = mResources.getString(R.string.success);
}
String info = mUpdateStr + ":" + mTvSource + " " + mChannel + " " + mState + " " + resultStr;
updateInfoView(info);
return;
}
//no record, insert the values
mContentResolver.insert(DBInfo.CONTENT_URI, values);
String info = mInsertStr + ":" + mTvSource + " " + mChannel + " " + mState;
updateInfoView(info);
return;
}
case R.id.btn_query: {
String selection = getSqlSelectionString();
String[] selectionArg = getSqlSelectArgs();
Cursor cursor = mContentResolver.query(DBInfo.CONTENT_URI,
new String[] {DBInfo.KEY_ID, DBInfo.KEY_STATE},
selection, selectionArg, null);
if (!cursor.moveToFirst()) {
XLog.i(TAG, "no record !");
String no_record = mResources.getString(R.string.no_record);
String info = mQueryStr + ":" + mTvSource + " " + mChannel + " " + no_record;
updateInfoView(info);
return;
}
String state;
do {
String id = cursor.getString(cursor.getColumnIndex(DBInfo.KEY_ID));
state = cursor.getString(cursor.getColumnIndex(DBInfo.KEY_STATE));
XLog.i(TAG, "id = " + id);
XLog.i(TAG, "state = " + state);
} while (cursor.moveToNext());
cursor.close();
String info = mQueryStr + ":" + mTvSource + " " + mChannel + " " + state;
updateInfoView(info);
return;
}
case R.id.btn_update: {
mTvSource = mSourceSpinner.getSelectedItem().toString();
mChannel = mChannelSpinner.getSelectedItem().toString();
mState = mStateSpinner.getSelectedItem().toString();
ContentValues values = new ContentValues();
values.put(DBInfo.KEY_SOURCE, mTvSource);
values.put(DBInfo.KEY_CHANNEL, mChannel);
values.put(DBInfo.KEY_STATE, mState);
String selection = getSqlSelectionString();
String[] selectionArg = getSqlSelectArgs();
int result = mContentResolver.update(DBInfo.CONTENT_URI, values, selection, selectionArg);
String resultStr = mResources.getString(R.string.fail);
if (result > 0) {
resultStr = mResources.getString(R.string.success);
}
String info = mUpdateStr + ":" + mTvSource + " " + mChannel + " " + mState + resultStr;
updateInfoView(info);
return;
}
case R.id.btn_delete: {
String selection = getSqlSelectionString();
String[] selectionArg = getSqlSelectArgs();
int count = mContentResolver.delete(DBInfo.CONTENT_URI, selection, selectionArg);
String resultStr = mResources.getString(R.string.fail);
if (count > 0) {
resultStr = mResources.getString(R.string.success);
}
String info = mDeleteStr + ":" + mTvSource + " " + mChannel + " " + resultStr;
updateInfoView(info);
return;
}
default:
break;
}
}
@SuppressLint("SimpleDateFormat")
private void updateInfoView(String info) {
XLog.i(TAG, "updateInbfo()");
SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateStr = dateformat.format(System.currentTimeMillis());
String newInfo = dateStr + " " + info + "\n";
mInfo.setText(mInfo.getText() + newInfo);
mScrollView.fullScroll(ScrollView.FOCUS_DOWN);
}
@Override
protected void onResume() {
super.onResume();
XLog.i(TAG, "onResume() ");
}
@Override
protected void onPause() {
super.onPause();
XLog.i(TAG, "onPause() ");
}
@Override
protected void onDestroy() {
super.onDestroy();
XLog.i(TAG, "onDestroy()");
}
}
附件: Provide的Demo Source code下载