当前位置:   article > 正文

Android四大组件——ContentProvider开发

contentprovider

一、简单介绍

1、适用场景

1) ContentProvider 为存储和读取数据提供了统一的接口
2) 使用 ContentProvider,应用程序可以实现数据共享

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

2、相关概念

ContentProvider简介

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

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);

二、ContentProvider使用实例

1、创建数据库管理类,并创建所需的表和数据

  1. public class MyDatabaseHelper extends SQLiteOpenHelper {
  2. private static final String DATABASE_NAME = "Users.db";
  3. private static final int DATABASE_VERSION= 1;
  4. private static final String TABLE_NAME= "User";
  5. public MyDatabaseHelper(Context context) {
  6. super(context, DATABASE_NAME, null, DATABASE_VERSION);
  7. }
  8. @Override
  9. public void onCreate(SQLiteDatabase db) {
  10. //创建用于存储数据的表
  11. db.execSQL("Create table " + TABLE_NAME + "( _id INTEGER PRIMARY KEY AUTOINCREMENT, USER_NAME TEXT);");
  12. }
  13. @Override
  14. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  15. db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
  16. onCreate(db);
  17. }
  18. }

2、创建一个类,定义uri并存储用户名称并显示所有的用户名称(使用 SQLLite数据库存储这些数据)

  1. public class MyUsers {
  2. public static final String AUTHORITY = "com.cx.datastored.MyContentProvider";
  3. //BaseColumn类中已经包含了 _id字段
  4. public static final class User implements BaseColumns {
  5. //定义uri
  6. public static final Uri CONTENT_URI = Uri.parse("content://com.cx.datastored.MyContentProvider");
  7. // 表数据列
  8. public static final String USER_NAME = "USER_NAME";
  9. }
  10. }

 3、重写ContentProvider,完成自己的MyContentProvider,这里只做了插入和查询功能

  1. public class MyContentProvider extends ContentProvider {
  2. private SQLiteDatabase sqlDB;
  3. private MyDatabaseHelper dbHelper;
  4. private static final String TABLE_NAME= "User";
  5. @Override
  6. public int delete(Uri arg0, String arg1, String[] arg2) {
  7. //根据Uri删除arg1指定的条件所匹配的全部记录
  8. return 0;
  9. }
  10. @Override
  11. public String getType(Uri arg0) {
  12. //返回当前Uri的MIME类型,如果该URI对应的数据可能包含多条记录,那么MIME类型字符串就是以vnd.android.dir开头
  13. //如果该URI对应的数据只有一条,该MIME类型字符串就是以vnd..android.cursor.item开头
  14. return null;
  15. }
  16. @Override
  17. public Uri insert(Uri arg0, ContentValues arg1) {
  18. //根据Uri插入arg1对应的数据
  19. sqlDB = dbHelper.getWritableDatabase();
  20. long rowId = sqlDB.insert(TABLE_NAME, "", arg1);
  21. if (rowId > 0) {
  22. Uri rowUri = ContentUris.appendId(MyUsers.User.CONTENT_URI.buildUpon(), rowId).build();
  23. getContext().getContentResolver().notifyChange(rowUri, null);
  24. return rowUri;
  25. }
  26. throw new SQLException("Failed to insert row into " + arg0);
  27. }
  28. @Override
  29. public boolean onCreate() {
  30. // 在ContentProvider创建后被调用
  31. dbHelper = new MyDatabaseHelper(getContext());
  32. return (dbHelper == null) ? false : true;
  33. }
  34. @Override
  35. public Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3,
  36. String arg4) {
  37. //根据Uri查询出arg2指定的条件所匹配的全部记录,并且可以指定查询那些列,以什么方式(arg4)排序
  38. SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
  39. SQLiteDatabase db = dbHelper.getReadableDatabase();
  40. qb.setTables(TABLE_NAME);
  41. Cursor c = qb.query(db, arg1, arg2, null, null, null, arg4);
  42. c.setNotificationUri(getContext().getContentResolver(), arg0);
  43. return c;
  44. }
  45. @Override
  46. public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
  47. //根据Uri修改arg2指定的条件所匹配的全部记录
  48. return 0;
  49. }
  50. }

4、 在AndroidMenifest.xml中使用<provider>标签来设置ContentProvider

  1. <provider
  2. android:exported="true"
  3. android:name="com.cx.datastored.MyContentProvider"
  4. android:authorities="com.cx.datastored.MyContentProvider" >
  5. </provider>

         注:android:exported="true"如果不设置,在本应用中访问数据时正常,但在其他应用中访问时就会出现异常。

5、在本应用中测试

  1. public class MainActivity extends Activity {
  2. @SuppressLint("SdCardPath")
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.activity_main);
  7. insertRecord("user");
  8. displayRecords();
  9. }
  10. private void insertRecord(String userName) {
  11. ContentValues values = new ContentValues();
  12. values.put(MyUsers.User.USER_NAME, userName);
  13. getContentResolver().insert(MyUsers.User.CONTENT_URI, values);
  14. }
  15. private void displayRecords() {
  16. ContentResolver cr = getContentResolver();
  17. String columns[] = new String[] { MyUsers.User._ID, MyUsers.User.USER_NAME };
  18. Uri myUri = MyUsers.User.CONTENT_URI;
  19. Cursor cur = cr.query(myUri, columns, null, null, null);
  20. if (cur.moveToFirst()) {
  21. String id = null;
  22. String userName = null;
  23. do {
  24. id = cur.getString(cur.getColumnIndex(MyUsers.User._ID));
  25. userName = cur.getString(cur.getColumnIndex(MyUsers.User.USER_NAME));
  26. Toast.makeText(this, id + " " + userName, Toast.LENGTH_LONG).show();
  27. } while (cur.moveToNext());
  28. }
  29. }
  30. }

 6、在其他应用中测试,这里需要新建一个工程。

1)新建工程中创建一个与上面2相同的类

  1. public class MyUsers {
  2. public static final String AUTHORITY = "com.cx.datastored.MyContentProvider";
  3. //BaseColumn类中已经包含了 _id字段
  4. public static final class User implements BaseColumns {
  5. //定义uri
  6. public static final Uri CONTENT_URI = Uri.parse("content://com.cx.datastored.MyContentProvider");
  7. // 表数据列
  8. public static final String USER_NAME = "USER_NAME";
  9. }
  10. }

2)测试代码也与5相同,只是他们是不同的应用

  1. public class MainActivity extends Activity {
  2. @SuppressLint("SdCardPath")
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.activity_main);
  7. insertRecord("user");
  8. displayRecords();
  9. }
  10. private void insertRecord(String userName) {
  11. ContentValues values = new ContentValues();
  12. values.put(MyUsers.User.USER_NAME, userName);
  13. getContentResolver().insert(MyUsers.User.CONTENT_URI, values);
  14. }
  15. private void displayRecords() {
  16. ContentResolver cr = getContentResolver();
  17. String columns[] = new String[] { MyUsers.User._ID, MyUsers.User.USER_NAME };
  18. Uri myUri = MyUsers.User.CONTENT_URI;
  19. Cursor cur = cr.query(myUri, columns, null, null, null);
  20. if (cur.moveToFirst()) {
  21. String id = null;
  22. String userName = null;
  23. do {
  24. id = cur.getString(cur.getColumnIndex(MyUsers.User._ID));
  25. userName = cur.getString(cur.getColumnIndex(MyUsers.User.USER_NAME));
  26. Toast.makeText(this, id + " " + userName, Toast.LENGTH_LONG).show();
  27. } while (cur.moveToNext());
  28. }
  29. }
  30. }

源码下载

测试应用源码

        注意:只有在先运行了上面的应用后,再运行测试应用源码才会看到效果。

        由于ContentProvider的主要用途是不同应用间的数据共享,所以在开发时很少会用到。例如,一个公司的一系列产品中的的某些数据需要相互使用。

三、获取通讯录实例

1、实现代码

  1. public class MainActivity extends Activity {
  2. @SuppressLint("SdCardPath")
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.activity_main);
  7. ContentResolver cr = getContentResolver();
  8. //向联系人中插入一条数据
  9. ContentValues values = new ContentValues();
  10. Uri uri = cr.insert(RawContacts.CONTENT_URI, values);
  11. //解析uri
  12. Long raw_contacts_id = ContentUris.parseId(uri);
  13. values.clear();
  14. //插入人名,指定联系人和插入行
  15. values.put(StructuredName.RAW_CONTACT_ID, raw_contacts_id);
  16. //追加插入联系人信息
  17. values.put(StructuredName.DISPLAY_NAME, "张三");
  18. values.put(StructuredName.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
  19. uri = cr.insert(Data.CONTENT_URI, values);
  20. //插入电话信息
  21. values.clear();
  22. values.put(Phone.RAW_CONTACT_ID, raw_contacts_id);
  23. values.put(Phone.NUMBER, "123434556456");
  24. values.put(Phone.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
  25. uri = cr.insert(Data.CONTENT_URI, values);
  26. //查询通讯录数据
  27. Cursor c = cr.query(ContactsContract.Contacts.CONTENT_URI, new String[]{Contacts._ID, Contacts.DISPLAY_NAME}, null, null, null);
  28. if(c != null){
  29. while (c.moveToNext()) {
  30. //如果不知道对应字段名称可以写成Contacts._ID和Contacts.DISPLAY_NAME
  31. int id = c.getInt(c.getColumnIndex("_id"));
  32. Log.e(">>>>>>>>>>>>", "_id" + id);
  33. Log.e(">>>>>>>>>>>>", "name" + c.getString(c.getColumnIndex("display_name")));
  34. //查询联系人电话号码,必须使用ID获取。通过ID查询号码和类型
  35. Cursor c1 = cr.query(Phone.CONTENT_URI, new String[]{Phone.NUMBER, Phone.TYPE}, Phone.CONTACT_ID + "=" + id, null, null);
  36. if(c1 != null){
  37. while (c1.moveToNext()) {
  38. int type = c1.getInt(c1.getColumnIndex(Phone.TYPE));
  39. if(type == Phone.TYPE_HOME){
  40. Log.e(">>>>>>>>>>>>", "家庭电话:" + c1.getString(c1.getColumnIndex(Phone.NUMBER)));
  41. }else if (type == Phone.TYPE_MOBILE){
  42. Log.e(">>>>>>>>>>>>", "手机:" + c1.getString(c1.getColumnIndex(Phone.NUMBER)));
  43. }
  44. }
  45. c1.close();
  46. }
  47. //根据联系人id查询出联系人的邮箱地址
  48. Cursor c2 = cr.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, new String[]{Email.DATA, Email.TYPE}, Email.CONTACT_ID + "=" + id, null, null);
  49. if(c2 != null){
  50. while (c2.moveToNext()) {
  51. int type = c2.getInt(c2.getColumnIndex(Email.TYPE));
  52. if(type == Email.TYPE_WORK){
  53. Log.e(">>>>>>>>>>>>", "工作邮箱:" + c2.getString(c2.getColumnIndex(Email.DATA)));
  54. }
  55. }
  56. c2.close();
  57. }
  58. }
  59. }
  60. c.close();
  61. }
  62. }

 2、添加权限

  1. <!-- 查询联系人 -->
  2. <uses-permission android:name="android.permission.READ_CONTACTS" />
  3. <!-- 添加联系人 -->
  4. <uses-permission android:name="android.permission.WRITE_CONTACTS" />

下载源码s

四、FW中常见使用

        对于 Framework 开发来说,经常需要向 APP 暴露一些系统的状态,例如电源、蓝牙、wifi状态等。这时可以可以通过 get 方法以及回调函数获取,但这样如果是第三方应用需要通过提供jar包,调取里面的方法,jar包更新和调用都不是太方便。所以我们可以使用 ContentProvider 进行数据存储和读取。

1、存储用户id

Settings.System.putInt(getContentResolver(), "user_name_id", 1);

 2、读取用户id

int userId = Settings.System.getInt(getContentResolver(), "user_name_id", -1);

 3、实时监听

  1. //监听内容变化
  2. getContentResolver().registerContentObserver(Settings.System.getUriFor("user_name_id"), true,
  3. new ContentObserver(new Handler()) {
  4. @Override
  5. public void onChange(boolean selfChange) {
  6. super.onChange(selfChange);
  7. //监听到内容有变化
  8. int userId = Settings.System.getInt(getContentResolver(), "user_name_id", -1);
  9. }
  10. });

更多内容>>

参考文章:Android之ContentProvider总结

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/993195
推荐阅读
相关标签
  

闽ICP备14008679号