您的位置:首页 > 移动开发 > Android开发

Android核心组件之ContentProvider(二)--创建自己的ContentProvider

2014-07-21 11:40 573 查看
转载请注明出处/article/7809129.html

自定义Content Provider初步分析

前几篇博文我们基本了解了Android系统自身所提供的Content Provider的特点,并学习了如何访问其所提供的数据接口。今天我们将来学习如何自定义Content Provider,以及如何访问、操作自定义Content Provider所封装的数据。
首先,我们来确定一下我们在哪些情况下需要自定义Content Provider:
(1)当本应用需要向其它应用提供大量的数据和文件时,需要自定义Content Provider,为其它应用访问数据提供统一的数据接口;
(2)当本应用允许用户拷贝大量数据到其它应用时,需自定义Content Provider;
(3)当需要使用搜索框架来创建一个搜索引擎时,需自定义Content Provider。
需要注意的是,在实际开发中,当对SQLite数据库中的数据操作只在本应用使用时,我们不需要另外自定义Content Provider,直接操作SQLite数据库就行。我们知道,Content Provider它本身并不负责数据的存储,它的作用主要就是为其它应用访问数据提供一个统一的接口,那Content
Provider一般支持哪些数据作为其访问接口呢?一般来讲,支持两种数据存储类型:
(1)文件数据:包括SharedPreference存储的xml格式的数据、文件设备存储的文件数据等;
(2)结构化数据:一般包括存储在数据库、数组和类结构化的数据。

Content Uri

在(Android四大组件只Content Provider--初步学习)这篇博客中我们初步学习一下Content Uri组成方式及作用,今天我们学习自定义Content Provider需要频繁用到Content Uri,因此需要对其进一步深入学习。那么通过前面的学习,我们知道了Content Uri一般是由三部分组成:
(1)content://:该字符串为content uri的特殊标识,此字符串为Content Uri所必须的头部信息
(2)授权(Authority):用于唯一标识这个Content Provider,外部访问者可以根据这个标识找到它。
(3)路径(Path):所需要访问数据的路径,根据业务而定。
在Content Uri组成部分中,Authority字符串我们可以任意命名,但每一个Content Provider的Authority字符串必须具有唯一性,因为该Authority就像数据库中表字段中的主键一样,系统是根据该Authority来定位到具体的Content Provider,从而使外部访问者获取到指定的信息。不过一般来讲,Authority的命名有一定组成规则,为确保Content
Provider的Authority字符串的唯一性,一般按以下命名规则来命名Authority:com.company-name.ContentProvider-name。比如说我们下面将学习的一个例子中,自定义的一个MyContentProvider的内容提供者,company-name为androidleaf。那么在AndroidManifest.xml中注册provider时的代码如下:
<provider
android:name="com.androidleaf.contentprovider.myprovider.MyContentProvider" <!--自定义的Content Provider名称  -->
android:authorities="com.androidleaf.MyContentProvider" <!-- 定义Authority字符串名称 -->
android:exported="true" <!--是否允许其它应用访问 -->
/>
假设上面自定义的MyContentProvider既提供了一系列Persons个人信息,又提供了Company个人所属的公司信息,那么外部访问者需要各自访问两者的信息,就必须根据不同的路径进行访问,那访问Persons信息的Path(路径)可以定义为persons,那访问Persons的Content Uri为
content://com.androidleaf.MyContentProvider/persons,访问Company信息的Path(路径)可以定义为company,那访问Company的Content Uri为 content://com.androidleaf.MyContentProvider/company。那如果要访问Persons中的具体的某一条person数据,则需怎么定义Content Uri呢,其实很简单,只需定义清楚所需要查询的数据的具体路径就行,比如需要查询person信息集中的第3条数据,则Content
Uri应该定义如下:



MIME类型

使用Content Provider可以返回数据文件的MIME类型,有关Android中MIME类型的更多知识,可以学习(Android MIME类型)这篇文章。

跨应用访问自定义Content Provider的数据

好了,学习完自定义Content Provider所需要预备知识后,我们来通过一个实例来具体地学习如何创建自己的Content Provider。在动手实现之前,我们先整理一下实例所需完成的功能及实现思路。首先在SQLite数据库中创建一个person表,并添加数条Person数据,然后自定义一个Content Provider为访问person表中的数据提供一个标准的访问接口,然后创建另一个新的应用,并在新的应用中通过getContentResolve()方法获取person表中的数据,并实现对Person信息的增删改查操作。下面是我们实例程序的列表清单,如下图:



该应用负责创建数据库表,初始化表数据以及自定义Content Provider数据接口。



该应用负责访问自定义的Content Provider接口数据,实现跨进程的数据访问,并对person表的数据执行CRUD操作。
文不如表,表不如图,为了加深读者对自定义Content Provider及使用的理解,我用一个图来表现我们实例执行的操作流程及关系,如图下:



接下来我将一步一步编写代码,实现以上图示的访问操作,首先,在SQLite数据库中创建person表,并向person中添加若干条数据。首先创建自己的OpenHelper类,代码列表如下:
/**
* 该类是SQLiteDatabase的帮助类,主要管理数据库的创建和版本的更新
* @author AndroidLeaf
*
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class DatabaseOpenHelper extends SQLiteOpenHelper {

private static DatabaseOpenHelper mDatabaseOpenHelper;
/**
* 数据库版本号
*/
private static final int DATABASE_VERSION = 1;
/**
* 数据库名称
*/
private static final String DATABASE_NAME = "manager.db";

/**
* 定义一个事件监听回调,将创建表和更新数据库表的操作让子类实现
*/
public SQLiteDataTable mDataTable;

public interface SQLiteDataTable{
public void onCreate(SQLiteDatabase mSqLiteDatabase);
public void onUpgrade(SQLiteDatabase mSqLiteDatabase);
}

public void setOnSQLiteDataTable(SQLiteDataTable mDataTable){
this.mDataTable = mDataTable;
}
/**
* 初始化数据库信息
* @param context 应用程序上下文
* @param name 数据库名称
* @param factory cursor工厂对象
* @param version 数据库版本号
* @param errorHandler 数据库错误处理对象
*/
public DatabaseOpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION, new DatabaseErrorHandler() {

@Override
public void onCorruption(SQLiteDatabase dbObj) {
// TODO Auto-generated method stub
}
});
// TODO Auto-generated constructor stub
}

/**
* 使用单例模式,获取数据库唯一实例
* @param mContext 应用程序上下文
* @return mDatabaseOpenHelper 该对象用于获取SQLiteDatabase实例
*/
public synchronized static DatabaseOpenHelper getDatabaseOpenHelper(Context mContext){
if(mDatabaseOpenHelper == null){
mDatabaseOpenHelper = new DatabaseOpenHelper(mContext);
}
return mDatabaseOpenHelper;
}

/**
* 创建数据库时调用,一般执行创建表的操作
*/
@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
//创建一系列的数据库表
mDataTable.onCreate(db);
}

/**
* 当数据库需要修改的时系统会自动调用此方法。一般我们在这个方法里边删除数据库表,
* 并建立新的数据库表,当然是否还需要其它操作,完全取决于应用的需求
*/
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
mDataTable.onUpgrade(db);
onCreate(db);
}
}
然后创建逻辑层接口IDatabaseManager,定义了一些主要的CRUD方法,具体代码如下:
public interface IDatabaseManager {
public long insert(ContentValues mContentValues);
public Cursor query(String sql);
public Cursor query(int id);
public int update(ContentValues mContentValues);
public int delete(int id);
public void execSQL(String sql);
public void closeDB();
}
最后创建逻辑层实现类DatabaseManager,具体代码如下:
public class DataBaseManager implements IDatabaseManager{

private static final String TAG = "sqlite_log";
private static final String PERSON_TABLE = MyContentProviderMetaData.ProviderMetaData.Persons.PERSON_TABLE_NAME;
private static final String PERSON_ID = MyContentProviderMetaData.ProviderMetaData.Persons._ID;

private SQLiteDatabase mSqLiteDatabase;
DatabaseOpenHelper mDatabaseOpenHelper;

public DataBaseManager(Context mContext){
mDatabaseOpenHelper = DatabaseOpenHelper.getDatabaseOpenHelper(mContext);
//获取SQLiteDatabase对象,创建或打开数据库
mSqLiteDatabase = mDatabaseOpenHelper.getWritableDatabase();
}

/**
* 插入单条数据操作
* @param mContentValues 插入的数据集合
* @return long 插入数据的行号
*/
@Override
public long insert(ContentValues mContentValues) {
// TODO Auto-generated method stub
mSqLiteDatabase.beginTransaction();
try {
long rowId = mSqLiteDatabase.insertOrThrow(PERSON_TABLE,PERSON_ID, mContentValues);
mSqLiteDatabase.setTransactionSuccessful();
return rowId;
} catch (Exception e) {
// TODO: handle exception
Log.e(TAG, "The insert operation failed");
}finally{
mSqLiteDatabase.endTransaction();
}
return -1;
}

/**
* 更新单条数据操作
* @param mContentValues 需要更新的数据集合
* @return int 更新数据的数量
*/
@Override
public int update(ContentValues mContentValues) {
// TODO Auto-generated method stub
mSqLiteDatabase.beginTransaction();
try {
int rows = mSqLiteDatabase.update(PERSON_TABLE, mContentValues, PERSON_ID +"= ?",
new String[]{String.valueOf(mContentValues.get(PERSON_ID))});
mSqLiteDatabase.setTransactionSuccessful();
return rows;
} catch (Exception e) {
// TODO: handle exception
Log.e(TAG, "The update operation failed");
}finally{
mSqLiteDatabase.endTransaction();
}
return 0;
}

/**
* 更新多条数据操作
* @param mContentValues 需要更新的数据集合
* @param whereClause
* @param whereArgs
* @return int 更新的数据数量
*/
public int update(ContentValues mContentValues,String whereClause,String[] whereArgs) {
// TODO Auto-generated method stub
mSqLiteDatabase.beginTransaction();
try {
int rows = mSqLiteDatabase.update(PERSON_TABLE, mContentValues, whereClause, whereArgs);
mSqLiteDatabase.setTransactionSuccessful();
return rows;
} catch (Exception e) {
// TODO: handle exception
Log.e(TAG, "The update operation failed");
}finally{
mSqLiteDatabase.endTransaction();
}
return 0;
}

/**
* 删除单条数据操作
* @param mContentValues 需要更删除的数据选项ID
* @return int 删除的数据数量
*/
@Override
public int delete(int id) {
// TODO Auto-generated method stub
mSqLiteDatabase.beginTransaction();
try {
int rows = mSqLiteDatabase.delete(PERSON_TABLE, PERSON_ID +"= ?", new String[]{String.valueOf(id)});
mSqLiteDatabase.setTransactionSuccessful();
return rows;
} catch (Exception e) {
// TODO: handle exception
Log.e(TAG, "The delete operation failed");
}finally{
mSqLiteDatabase.endTransaction();
}
return 0;
}
/**
* 删除多条数据操作
* @param whereClause
* @param whereArgs
* @return int 删除的数据数量
*/
public int delete(String whereClause,String[] whereArgs) {
// TODO Auto-generated method stub
mSqLiteDatabase.beginTransaction();
try {
int rows = mSqLiteDatabase.delete(PERSON_TABLE, whereClause, whereArgs);
mSqLiteDatabase.setTransactionSuccessful();
return rows;
} catch (Exception e) {
// TODO: handle exception
Log.e(TAG, "The delete operation failed");
}finally{
mSqLiteDatabase.endTransaction();
}
return 0;
}

/**
* 使用标准SQL语句查询数据列表
* @param <T>
* @param sql 标准SQL语句
* @return mList 查询后的数据列表
*/
@SuppressWarnings("unchecked")
@Override
public Cursor query(String sql) {
return mSqLiteDatabase.rawQuery(sql, null);
}

/**
* 根据ID查询数据
* @param <T>
* @param id 需要查询的数据ID
* @return T 查询后获取到的对象
*/
@Override
public Cursor query(int id) {
// TODO Auto-generated method stub
return mSqLiteDatabase.query(PERSON_TABLE, null, PERSON_ID + "=?", new String[]{String.valueOf(id)}, null, null, null);
}

/**
* 执行一些较为复杂的CRUD操作
*/
@Override
public void execSQL(String sql) {
// TODO Auto-generated method stub

}

/**
* 执行对数据库中数据的操作后关闭数据库
*/
@Override
public void closeDB() {
// TODO Auto-generated method stub
mSqLiteDatabase.close();
}
}
至此,SQLite数据库中person表的创建和数据初始化基本已经完成,对SQLite数据库操作还不熟悉的读者,可以先学习(Android中SQLite数据库介绍和使用)这篇文章。接下来,将介绍本章的重点知识--自定义Content Provider。首先,我们先梳理一下自定义Content Provider的编程步骤:
(1)为访问不同的数据集定义不同的CONTENT_URI,并使用UriMatcher类来实现Uri的匹配器,用于匹配外部访问者传递过来的Uri,从而根据不同的Uri执行目标操作;
(2)创建一个继承Content Provider抽象类的子类(示例中为MyContentProvider),同时分别重写onCreate()、query()、getType()、delete()、update()和insert()抽象方法;

(3)分别实现自定义Content Provider中的5个方法,在这里主要实现对Uri的匹配,以及对SQLite数据库中表的数据的CRUD操作;
(4)在外部访问者中(可以是在本进程,也可以在不同的进程中)调用getContentResolver()方法通过传递不同的CONTENT_URI参数,执行不同的操作,系统会自动访问目标Content Provider,如果访问是在不同的进程中进行,那系统会自动执行跨进程操作。
(5)获取访问目标Content Provider返回的结果。
接下来我们一一按步骤进行自定义Content Provider操作,首先开始第一步,我们定义一个MyContentProviderMetaData类,用于创建不同的CONTENT_URI,具体的代码如下:
public class MyContentProviderMetaData {
/**
* 定义基本授权Authority
*/
public static final String AUTHORITY = "com.androidleaf.MyContentProvider";
public static final String URI_AUTHORITY = "content://"+ AUTHORITY +"/";
public static final int COLLECTION_INDICATOR = 1;
public static final int SINGLE_INDICATOR = 2;

public static final class ProviderMetaData{

public static final class Persons implements BaseColumns{

/**
* 表的名称
*/
public static final String PERSON_TABLE_NAME = "person";
public static final String PERSON_FIELD_PERSONNAME = "name";
public static final String PERSON_FIELD_EMAIL = "email";
public static final String PERSON_FIELD_HEIGHT = "height";

public static UriMatcher mUriMatcher = null;

/**
* 创建Person相关的CONTENT_URI,PERSONS_URI用于匹配查询多条Person数据集,
* PERSON_URI用于匹配查询单条Person的数据
*/
public static final Uri PERSONS_URI = getContentUri("persons");
public static final Uri PERSON_URI = getContentUri("persons/#");

static{
/**
* 实例化UriMatcher对象,注册需要匹配的URI
*/
mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
mUriMatcher.addURI(AUTHORITY, "persons", COLLECTION_INDICATOR);
mUriMatcher.addURI(AUTHORITY, "persons/#", SINGLE_INDICATOR);
}

public static Uri getContentUri(String volumeName){
return Uri.parse(URI_AUTHORITY + volumeName);
}
}
}
}
在此段代码中,我们创建了Person相关的CONTENT_URI,其中path为"persons"的PERSONS_URI是用于匹配查询多条Person数据集,而path为“persons/#”的PERSON_URI是用于匹配查询单条Person的数据,在开发中,“#”字符我们可以使用ContentUris.withAppendedId(Uri
uri,int id)方法来替换我们需要所需查询的具体单条Person数据的_id。而程序中UriMatcher对象主要作用则是使用本身已经注册的Uri匹配从外部访问者传递过来的Uri,每一个传递过来的Uri和注册的时的Uri一一对应,若匹配成功,则执行目标操作,反之则返回一个NO_MATCH常量。在为UriMatcher注册Uri过程中,我们可以使用addURI(String authority,String path,int code)方法来很方便地注册所需要匹配的Uri。
接下来我们来实现第2、3步骤的操作,就是创建Content Provider的子类,并实现5个抽象方法,具体的代码列表如下:

/**
* 自定义Content Provider,实现对SQLite数据库数据操作的封装
* @author AndroidLeaf
*/
public class MyContentProvider extends ContentProvider {

//获取UriMatcher对象
UriMatcher mUriMatcher = MyContentProviderMetaData.ProviderMetaData.Persons.mUriMatcher;
DataBaseManager mDataBaseManager;

@Override
public boolean onCreate() {
// TODO Auto-generated method stub
mDataBaseManager = new DataBaseManager(getContext());
return mDataBaseManager == null?false:true;
}

@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// TODO Auto-generated method stub
Cursor mCursor = null;
switch (mUriMatcher.match(uri)) {
case MyContentProviderMetaData.SINGLE_INDICATOR:
//得到最后"/"字符串的数值ID
String rowId = uri.getLastPathSegment();
mCursor = mDataBaseManager.query(Integer.parseInt(rowId));
break;
case MyContentProviderMetaData.COLLECTION_INDICATOR:
//查询所有数据
mCursor = mDataBaseManager.query("select * from person");
break;
default:
throw new IllegalArgumentException("Unkonw Uri "+ uri);
}
return mCursor;
}

@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
return null;
}

@Override
public Uri insert(Uri uri, ContentValues values) {
// TODO Auto-generated method stub
if(mUriMatcher.match(uri) != MyContentProviderMetaData.COLLECTION_INDICATOR){
throw new IllegalArgumentException("Unkonw Uri "+ uri);
}
long rowId = mDataBaseManager.insert(values);
if( rowId!= -1){
Uri retUri = ContentUris.withAppendedId(MyContentProviderMetaData.ProviderMetaData.Persons.PERSONS_URI, rowId);
notifySetChanged(uri);
return retUri;
}
return null;
}

@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// TODO Auto-generated method stub
int count = 0;
//UriMatcher.match(uri)方法用于匹配uri,匹配成功后返回注册时设置的code
switch (mUriMatcher.match(uri)) {
case MyContentProviderMetaData.SINGLE_INDICATOR:
//得到最后"/"字符串的数值ID
String rowId = uri.getLastPathSegment();
count = mDataBaseManager.delete(Integer.parseInt(rowId));
break;
case MyContentProviderMetaData.COLLECTION_INDICATOR:
count = mDataBaseManager.delete(selection, selectionArgs);
break;
default:
throw new IllegalArgumentException("Unkonw Uri "+ uri);
}

if(count > 0){
notifySetChanged(uri);
}
return count;
}

@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO Auto-generated method stub
int count = 0;
switch (mUriMatcher.match(uri)) {
case MyContentProviderMetaData.SINGLE_INDICATOR:
//得到最后"/"字符串的数值ID
String rowId = uri.getLastPathSegment();
values.put(MyContentProviderMetaData.ProviderMetaData.Persons._ID, rowId);
count = mDataBaseManager.update(values);
break;
case MyContentProviderMetaData.COLLECTION_INDICATOR:
count = mDataBaseManager.update(values, selection, selectionArgs);
default:
throw new IllegalArgumentException("Unkonw Uri "+ uri);
}

if(count > 0){
notifySetChanged(uri);
}
return count;
}

public void notifySetChanged(Uri uri){
//更新数据时,通知其它的ContentObserver
this.getContext().getContentResolver().notifyChange(uri, null);
}
}
完成第2、3步骤后,我们基本上已经完成了使用自定义的Content Provider对SQLite数据库中的数据的封装,并提供了一个统一的访问接口,以供外部访问者使用。那么接下来我们就需要执行第4、5
步骤的操作了,我们需要做的是另外创建一个新的应用(在实例中名为05_AndroidContentProvider_Access_Blog),通过调用getContentResolver()方法来访问刚刚我们自定义的MyContentProvider类的方法(当然,我们需要传递相关的Uri参数),完成跨进程的操作,从而获取SQLite数据库中的Person信息集合,并执行对Person信息的CRUD操作。我们来看一下具体的实例代码,首先看一下MainActivity类中的代码列表,如下:
public class MainActivity extends ListActivity {

PersonListAdapter mAdapter = null;
public static final int INSERT_ACTION = 0;
public static final int UPDATE_ACTION = 1;
public static final int DELETE_ACTION = 2;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

init();
}

public void init(){

ArrayList<Person> mArrayList =  getAllPersons();
if(mArrayList != null && mArrayList.size() > 0){
mAdapter = new PersonListAdapter(getApplicationContext(),mArrayList);

//适配器绑定数据
setListAdapter(mAdapter);
//将ListView列表添加到上下文Menu中
this.registerForContextMenu(getListView());
}
}

public ArrayList<Person> getAllPersons(){
ArrayList<Person> mList = new ArrayList<Person>();
Cursor mCursor = getContentResolver().query(MyContentProviderMetaData.ProviderMetaData.Persons.PERSONS_URI, null, null, null, null);
if(mCursor != null && mCursor.getCount() > 0){
mCursor.moveToFirst();
while(!mCursor.isAfterLast()){
int id = mCursor.getInt(mCursor.getColumnIndex(MyContentProviderMetaData.ProviderMetaData.Persons._ID));
String name = mCursor.getString(mCursor.getColumnIndex(MyContentProviderMetaData.ProviderMetaData.Persons.PERSON_FIELD_PERSONNAME));
String email = mCursor.getString(mCursor.getColumnIndex(MyContentProviderMetaData.ProviderMetaData.Persons.PERSON_FIELD_EMAIL));
float height = mCursor.getFloat(mCursor.getColumnIndex(MyContentProviderMetaData.ProviderMetaData.Persons.PERSON_FIELD_HEIGHT));
mList.add(new Person(id, name, email, height));
mCursor.moveToNext();
}
return mList;
}else{
((TextView)findViewById(R.id.empty_textview)).setText("获取的数据为空!!!");
return null;
}
}

/**
* 删除数据
* @param id 列表ID
*/
public void deletePerson(final int id){
AlertDialog.Builder mBuilder = new AlertDialog.Builder(MainActivity.this)
.setTitle("是否删除?")
.setPositiveButton("确定", new OnClickListener() {

@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
Uri mUri = ContentUris.withAppendedId(MyContentProviderMetaData.ProviderMetaData.Persons.PERSON_URI, id);
getContentResolver().delete(mUri, null, null);
//重新设置数据
mAdapter.setData(getAllPersons());
//更新数据列表
mAdapter.notifyDataSetChanged();
}
})
.setNegativeButton("取消", null);
mBuilder.show();
}

/**
* 更新数据
* @param id 列表ID
*/
public void updatePerson(int id,Person mPerson){
createDialog("更新", UPDATE_ACTION, id,mPerson);
}

/**
* 插入数据
*/
public void insertPerson(){
createDialog("添加", INSERT_ACTION, -1,null);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {

// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
insertPerson();
return super.onOptionsItemSelected(item);
}

@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
// TODO Auto-generated method stub
menu.setHeaderTitle("文件操作");
menu.add(0, 1, Menu.NONE, "修改");
menu.add(0, 2, Menu.NONE, "删除");
}

@Override
public boolean onContextItemSelected(MenuItem item) {
// TODO Auto-generated method stub
// 得到当前被选中的item信息
AdapterContextMenuInfo menuInfo = (AdapterContextMenuInfo) item
.getMenuInfo();
int id = -1;
Person mPerson = null;
if(mAdapter != null){
mPerson = (Person)mAdapter.getItem(menuInfo.position);
id = mPerson.getId();
}

switch (item.getItemId()) {
case UPDATE_ACTION:
updatePerson(id,mPerson);
break;
case DELETE_ACTION:
deletePerson(id);
break;
default:
break;
}
return true;
}

/**
* 创建Dialog实例
* @param title 设置Dialog标题
* @param action 设置操作类型 ,UPDATE_ACTION为更新操作,INSERT_ACTION为插入操作
* @param id 被选中的选项ID
*/
public void createDialog(String title,final int action,final int id,final Person mPerson){

final View mView = LayoutInflater.from(MainActivity.this).inflate(R.layout.layout_operation, null);
String titlName = getResources().getString(R.string.operation, new Object[]{title});

final EditText mEditText_name = (EditText)mView.findViewById(R.id.name);
final EditText mEditText_email = (EditText)mView.findViewById(R.id.email);
final EditText mEditText_height = (EditText)mView.findViewById(R.id.height);

//初始化数据
if(action == UPDATE_ACTION){
mEditText_name.setText(mPerson.getName());
mEditText_email.setText(mPerson.getEmail());
mEditText_height.setText(String.valueOf(mPerson.getHeight()));
}

//创建Dialog实例对象
AlertDialog.Builder mBuilder = new AlertDialog.Builder(MainActivity.this)
.setTitle(titlName)
.setView(mView)
.setPositiveButton("确定", new OnClickListener() {

@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
String name = mEditText_name.getText().toString().trim();
String email = mEditText_email.getText().toString().trim();
float height = Float.parseFloat(mEditText_height.getText().toString().trim());

if(!TextUtils.isEmpty(name) && !TextUtils.isEmpty(email) &&
!TextUtils.isEmpty(mEditText_height.getText().toString().trim())){
ContentValues mValues = new ContentValues();
mValues.put(MyContentProviderMetaData.ProviderMetaData.Persons.PERSON_FIELD_PERSONNAME, name);
mValues.put(MyContentProviderMetaData.ProviderMetaData.Persons.PERSON_FIELD_EMAIL, email);
mValues.put(MyContentProviderMetaData.ProviderMetaData.Persons.PERSON_FIELD_HEIGHT, height);
switch (action) {
case INSERT_ACTION:
MainActivity.this.getContentResolver().insert(MyContentProviderMetaData.ProviderMetaData.Persons.PERSONS_URI, mValues);
break;
case UPDATE_ACTION:
Uri mUri = ContentUris.withAppendedId(MyContentProviderMetaData.ProviderMetaData.Persons.PERSON_URI, id);
MainActivity.this.getContentResolver().update(mUri, mValues, null, null);
break;
default:
break;
}
//重新设置数据
mAdapter.setData(getAllPersons());
//更新数据列表
mAdapter.notifyDataSetChanged();
}
}
})
.setNegativeButton("取消", null);
mBuilder.show();
}

}

然后Person的entity类,代码如下:
public class Person {
private int id;
private String name;
private String email;
private float height;

public Person(){}

public Person(int id,String name,String email,float height){
this.id = id;
this.name = name;
this.email = email;
this.height = height;
}

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 getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public float getHeight() {
return height;
}
public void setHeight(float height) {
this.height = height;
}
}

最后是PersonListAdapter适配器类,具体代码如下:

public class PersonListAdapter extends BaseAdapter {

private Context mContext;
private ArrayList<Person> mList;

public PersonListAdapter(Context mContext,ArrayList<Person> mList){
this.mContext = mContext;
this.mList = mList;
}

public void setData(ArrayList<Person> mList){
this.mList = mList;
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return mList.size();
}

@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return mList.get(position);
}

@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
ViewHolder mViewHolder = null;
if(convertView == null){
convertView = LayoutInflater.from(mContext).inflate(R.layout.layout_item, null);
mViewHolder = new ViewHolder();
mViewHolder.mTextView_id = (TextView)convertView.findViewById(R.id.person_id);
mViewHolder.mTextView_name = (TextView)convertView.findViewById(R.id.person_name);
mViewHolder.mTextView_height = (TextView)convertView.findViewById(R.id.person_height);
mViewHolder.mTextView_email = (TextView)convertView.findViewById(R.id.person_email);
convertView.setTag(mViewHolder);
}else{
mViewHolder = (ViewHolder)convertView.getTag();
}
mViewHolder.mTextView_id.setText(mList.get(position).getId()+"");
mViewHolder.mTextView_name.setText(mList.get(position).getName());
mViewHolder.mTextView_height.setText(mList.get(position).getHeight()+"");
mViewHolder.mTextView_email.setText(mList.get(position).getEmail());
return convertView;
}

class ViewHolder{
TextView mTextView_id;
TextView mTextView_name;
TextView mTextView_height;
TextView mTextView_email;
}

}
至此,我们便全部编写完成了实例代码,我来先后在模拟器上运行一下两个应用实例,运行效果如下图:



依据以上图示可知,我们成功获取了SQLite数据库中的Person数据,并且可以执行对person表中数据的CRUD操作。
总结:自定义Content Provider在实际的开发中其实用到的不算很多,因为我们开发的大多数APP的数据都是私有化的,很少会暴露数据接口给外部的应用,但Content Provider作为Android重要组成部分,其承担的角色的重要性不言而喻。学习自定义Content
Provider有助于我们对Android系统的内部的机制和实现更进一步的了解。我们总结一下我们今天学习的主要知识点:(1)Content Uri的组成方式及使用;(3)UriMatcer类的作用及使用;(3)自定义Content Provider的步骤及实现方式。

源代码下载,请戳这里!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: