Android ContentProvider的使用

ContentProvider

1.适用场景

1) ContentProvider为存储和读取数据提供了统一的接口

2) 使用ContentProvider,应用程序可以实现数据共享

3) android内置的许多数据都是使用ContentProvider形式,供开发者调用的(如视频,音频,图片,通讯录等)

2.相关概念介绍

1)ContentProvider简介
当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据。虽然使用其他方法也可以对外共享数据,但数据访问方式会因数据存储的方式而不同,如:采用文件方式对外共享数据,需要进行文件操作读写数据;采用sharedpreferences共享数据,需要使用sharedpreferencesAPI读写数据。而使用ContentProvider共享数据的好处是统一了数据访问方式。

2)Uri类简介

Uri uri = Uri.parse("content://com.changcheng.provider.contactprovider/contact")

在Content Provider中使用的查询字符串有别于标准的SQL查询。很多诸如select, add, delete, modify等操作我们都使用一种特殊的URI来进行,这种URI由3个部分组成, “content://”, 代表数据的路径,和一个可选的标识数据的ID。以下是一些示例URI:

content://media/internal/images这个URI将返回设备上存储的所有图片
content://contacts/people/这个URI将返回设备上的所有联系人信息
content://contacts/people/45这个URI返回单个结果(联系人信息中ID为45的联系人记录)

  尽管这种查询字符串格式很常见,但是它看起来还是有点令人迷惑。为此,Android提供一系列的帮助类(在android.provider包下),里面包含了很多以类变量形式给出的查询字符串,这种方式更容易让我们理解一点,因此,如上面content://contacts/people/45这个URI就可以写成如下形式:

  Uri person = ContentUris.withAppendedId(People.CONTENT_URI,45);

然后执行数据查询:

Cursor cur = managedQuery(person, null, null, null);

这个查询返回一个包含所有数据字段的游标,我们可以通过迭代这个游标来获取所有的数据:

package com.wissen.testApp;public class ContentProviderDemo extends Activity {    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);       displayRecords();    }    private void displayRecords() {        //该数组中包含了所有要返回的字段     String columns[] = new String[] { People.NAME, People.NUMBER };       Uri mContacts = People.CONTENT_URI;       Cursor cur = managedQuery(          mContacts,          columns,  // 要返回的数据字段         null,          // WHERE子句         null,         // WHERE 子句的参数         null         // Order-by子句     );       if (cur.moveToFirst()) {           String name = null;           String phoneNo = null;           do {              // 获取字段的值            name = cur.getString(cur.getColumnIndex(People.NAME));             phoneNo = cur.getString(cur.getColumnIndex(People.NUMBER));             Toast.makeText(this, name + ” ” + phoneNo, Toast.LENGTH_LONG).show();          } while (cur.moveToNext());       }    }}

上例示范了一个如何依次读取联系人信息表中的指定数据列name和number。

修改记录:
我们可以使用ContentResolver.update()方法来修改数据,我们来写一个修改数据的方法:

private void updateRecord(int recNo, String name) {    Uri uri = ContentUris.withAppendedId(People.CONTENT_URI, recNo);    ContentValues values = new ContentValues();    values.put(People.NAME, name);    getContentResolver().update(uri, values, null, null);}

现在你可以调用上面的方法来更新指定记录:

updateRecord(10, ”XYZ”);//更改第10条记录的name字段值为“XYZ”

添加记录:
要增加记录,我们可以调用ContentResolver.insert()方法,该方法接受一个要增加的记录的目标URI,以及一个包含了新记录值的Map对象,调用后的返回值是新记录的URI,包含记录号。
上面的例子中我们都是基于联系人信息簿这个标准的Content Provider,现在我们继续来创建一个insertRecord() 方法以对联系人信息簿中进行数据的添加:

private void insertRecords(String name, String phoneNo) {    ContentValues values = new ContentValues();    values.put(People.NAME, name);    Uri uri = getContentResolver().insert(People.CONTENT_URI, values);    Log.d(”ANDROID”, uri.toString());    Uri numberUri = Uri.withAppendedPath(uri, People.Phones.CONTENT_DIRECTORY);    values.clear();    values.put(Contacts.Phones.TYPE, People.Phones.TYPE_MOBILE);    values.put(People.NUMBER, phoneNo);    getContentResolver().insert(numberUri, values);}

这样我们就可以调用insertRecords(name, phoneNo)的方式来向联系人信息簿中添加联系人姓名和电话号码。


删除记录:
Content Provider中的getContextResolver.delete()方法可以用来删除记录,下面的记录用来删除设备上所有的联系人信息:

private void deleteRecords() {    Uri uri = People.CONTENT_URI;    getContentResolver().delete(uri, null, null);}

你也可以指定WHERE条件语句来删除特定的记录:

getContentResolver().delete(uri, “NAME=” + “‘XYZ XYZ’”, null);

这将会删除name为‘XYZ XYZ’的记录。

3. 创建ContentProvider

要创建我们自己的Content Provider的话,我们需要遵循以下几步:
a. 创建一个继承了ContentProvider父类的类

b. 定义一个名为CONTENT_URI,并且是public static final的Uri类型的类变量,你必须为其指定一个唯一的字符串值,最好的方案是以类的全名称, 如:
public static final Uri CONTENT_URI = Uri.parse( “content://com.google.android.MyContentProvider”);

c. 定义你要返回给客户端的数据列名。如果你正在使用Android数据库,必须为其定义一个叫_id的列,它用来表示每条记录的唯一性。

d. 创建你的数据存储系统。大多数Content Provider使用Android文件系统或SQLite数据库来保持数据,但是你也可以以任何你想要的方式来存储。

e. 如果你要存储字节型数据,比如位图文件等,数据列其实是一个表示实际保存文件的URI字符串,通过它来读取对应的文件数据。处理这种数据类型的Content Provider需要实现一个名为_data的字段,_data字段列出了该文件在Android文件系统上的精确路径。这个字段不仅是供客户端使用,而且也可以供ContentResolver使用。客户端可以调用ContentResolver.openOutputStream()方法来处理该URI指向的文件资源;如果是ContentResolver本身的话,由于其持有的权限比客户端要高,所以它能直接访问该数据文件。

f. 声明public static String型的变量,用于指定要从游标处返回的数据列。

g. 查询返回一个Cursor类型的对象。所有执行写操作的方法如insert(), update() 以及delete()都将被监听。我们可以通过使用ContentResover().notifyChange()方法来通知监听器关于数据更新的信息。

h. 在AndroidMenifest.xml中使用<provider>标签来设置Content Provider。

i. 如果你要处理的数据类型是一种比较新的类型,你就必须先定义一个新的MIME类型,以供ContentProvider.geType(url)来返回。MIME类型有两种形式:一种是为指定的单个记录的,还有一种是为多条记录的。这里给出一种常用的格式:

  vnd.android.cursor.item/vnd.yourcompanyname.contenttype (单个记录的MIME类型)
  比如, 一个请求列车信息的URI如content://com.example.transportationprovider/trains/122 可能就会返回typevnd.android.cursor.item/vnd.example.rail这样一个MIME类型。

  vnd.android.cursor.dir/vnd.yourcompanyname.contenttype (多个记录的MIME类型)
  比如, 一个请求所有列车信息的URI如content://com.example.transportationprovider/trains 可能就会返回vnd.android.cursor.dir/vnd.example.rail这样一个MIME 类型。

下列代码将创建一个Content Provider,它仅仅是存储用户名称并显示所有的用户名称(使用 SQLLite数据库存储这些数据):

package com.example.androidcontentprovider;import android.net.Uri;import android.provider.BaseColumns;/** * 定义了Content Provider的CONTENT_URI,以及数据列 * @author miaowei * */public class MyUsers {/** * Content Provider的CONTENT_URI */public static final String AUTHORITY = "com.example.androidcontentprovider.MyContentProvider";/** * 数据列 * @author miaowei * */public static final class User implements BaseColumns{public static final Uri CONTENT_URL = Uri.parse("content://"+AUTHORITY);//表数据列public static final String USER_NAME = "USER_NAME";}}

上面的类中定义了Content Provider的CONTENT_URI,以及数据列。下面我们将定义基于上面的类来定义实际的Content Provider类:

package com.example.androidcontentprovider;import java.sql.SQLException;import android.content.ContentProvider;import android.content.ContentUris;import android.content.ContentValues;import android.content.Context;import android.database.Cursor;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteOpenHelper;import android.database.sqlite.SQLiteQueryBuilder;import android.net.Uri;/** * 定义实际的Content Provider类 * 用于从Sqlite数据库中添加和读取记录 * @author miaowei * */public class MyContentProvider extends ContentProvider {private SQLiteDatabase sqlDB;private DatabaseHelper dbHelper;private static final String TABLE_NAME = "user";private static final String TAG = "MyContentProvider";@Overridepublic boolean onCreate() {dbHelper = new DatabaseHelper(getContext());return (dbHelper == null) ? false : true;}@Overridepublic String getType(Uri uri) {return null;}/** * 从sqlite中读取记录 */@Overridepublic Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {SQLiteQueryBuilder qb = new SQLiteQueryBuilder();SQLiteDatabase db = dbHelper.getReadableDatabase();qb.setTables(TABLE_NAME);Cursor cursor = qb.query(db , projection, selection,null, null,null, sortOrder);cursor.setNotificationUri(getContext().getContentResolver(),uri);return cursor;}/** * Sqlite数据库中添加记录 */@Overridepublic Uri insert(Uri uri, ContentValues values) {sqlDB = dbHelper.getWritableDatabase();long rowId = sqlDB.insert(TABLE_NAME, "", values);if (rowId > 0) {Uri  rowUri = ContentUris.appendId(MyUsers.User.CONTENT_URL.buildUpon(), rowId).build();getContext().getContentResolver().notifyChange(rowUri, null);return rowUri;}try {throw new SQLException("Failed to insert row into "+uri);} catch (SQLException e) {e.printStackTrace();}return null;}@Overridepublic int delete(Uri uri, String selection, String[] selectionArgs) {return 0;}@Overridepublic int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) {return 0;}/** * 创建数据库和数据表 * @author miaowei * */private static class DatabaseHelper extends SQLiteOpenHelper{private static final String DATABASE_NAME = "user.db";private static final int DATABASE_VERSION = 1;public DatabaseHelper(Context context) {super(context, DATABASE_NAME, null, DATABASE_VERSION);}@Overridepublic void onCreate(SQLiteDatabase db) {//创建用于存储数据的表        db.execSQL("Create table " + TABLE_NAME + "( _id INTEGER PRIMARY KEY AUTOINCREMENT, USER_NAME TEXT);");}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {db.execSQL("drop table if exists "+TABLE_NAME);onCreate(db);}}}

一个名为MyContentProvider的Content Provider创建完成了,它用于从Sqlite数据库中添加和读取记录。

Content Provider的入口需要在AndroidManifest.xml中配置:

<application        android:allowBackup="true"        android:icon="@drawable/ic_launcher"        android:label="@string/app_name"        android:theme="@style/AppTheme" >        <activity            android:name=".MainActivity"            android:label="@string/app_name" >            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>              <provider android:name="MyContentProvider"                  android:authorities="com.example.androidcontentprovider.MyContentProvider"></provider>            </application>

之后,让我们来使用这个定义好的Content Provider:

1)为应用程序添加ContentProvider的访问权限。

2)通过getContentResolver()方法得到ContentResolver对象。

3)调用ContentResolver类的query()方法查询数据,该方法会返回一个Cursor对象。

4)对得到的Cursor对象进行分析,得到需要的数据。

5)调用Cursor类的close()方法将Cursor对象关闭。

package com.example.androidcontentprovider;import java.util.ArrayList;import com.example.androidcontentprovider.R.id;import android.app.Activity;import android.content.ContentUris;import android.content.ContentValues;import android.database.Cursor;import android.net.Uri;import android.os.Bundle;import android.os.Handler;import android.view.View;import android.view.View.OnClickListener;import android.widget.ArrayAdapter;import android.widget.Button;import android.widget.ListView;import android.widget.Toast;/** *Android ContentProvider简单使用 * @author miaowei * */public class MainActivity extends Activity {private Button btn_addButton;private Button btn_queryButton;private ListView listView;private ArrayAdapter adapter;private ArrayList arrayList;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);arrayList = new ArrayList();adapter = new ArrayAdapter(this,android.R.layout.simple_list_item_1,android.R.id.text1, arrayList);btn_addButton = (Button)findViewById(R.id.btn_add);btn_queryButton = (Button)findViewById(R.id.btn_query);listView = (ListView)findViewById(R.id.listview);btn_addButton.setOnClickListener(onClickListener);btn_queryButton.setOnClickListener(onClickListener);listView.setAdapter(adapter);}private OnClickListener onClickListener = new OnClickListener() {@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.btn_add:insertRecord("MyUser");break;case R.id.btn_query:arrayList.clear();displayRecords();break;default:break;}}};/** * 添加数据记录 * @param userName */private void insertRecord(String userName){ContentValues values = new ContentValues();values.put(MyUsers.User.USER_NAME, userName);getContentResolver().insert(MyUsers.User.CONTENT_URL, values);}/** * 查询数据记录 */private void displayRecords(){String colums[] = new String[]{MyUsers.User._ID,MyUsers.User.USER_NAME};Uri myUri = MyUsers.User.CONTENT_URL;Cursor cursor = managedQuery(myUri, colums, null, null, null);if (cursor.moveToFirst()) {String idString = null;String userNameString = null;do {idString = cursor.getString(cursor.getColumnIndex(MyUsers.User._ID));userNameString = cursor.getString(cursor.getColumnIndex(MyUsers.User.USER_NAME));arrayList.add(idString+" "+userNameString);Toast.makeText(getApplicationContext(), idString+" "+userNameString, Toast.LENGTH_SHORT).show();mHandler.sendEmptyMessage(1);} while (cursor.moveToNext());}}private Handler mHandler = new Handler(){public void handleMessage(android.os.Message msg) {switch (msg.what) {case 1:adapter.notifyDataSetChanged();break;default:break;}};};}

源码见附件

更多相关文章

  1. Android中有关数据库SQLite的介绍
  2. 〖Android〗简单隐藏Android虚拟键盘的方法
  3. Android使用HttpURLConnection和HttpClient请求服务器数据
  4. Android应用程序的数据存放目录 路径
  5. android 引入jni 的so库的方法
  6. 一步一步学习androidNDK编程(java给c传递数据)
  7. Android屏幕保持常亮的三种方法

随机推荐

  1. Android(安卓)TextView 一些字体颜色、大
  2. Android录音,拍照,摄像
  3. 在ubuntu9.10下build android(转)
  4. Android(安卓)给圆形头像加边框
  5. android greenDao 编译问题总结
  6. Android可控图片旋转
  7. Android(安卓)性能优化(六)——资源文件的
  8. Android(安卓)配置问题
  9. Android(安卓)判断网络连接情况
  10. Android(安卓)开发获取手机运行内存工具