Android Content Provider

Content Provider
(1)android平台提供了Content Provider使一个应用程序的指定数据集提供给其他应用程序。其他应用可以通过ContentResolver类从该内容提供者中获取或存入数据。
(2)只有需要在多个应用程序间共享数据是才需要内容提供者。例如,通讯录数据被多个应用程序使用,且必须存储在一个内容提供者中。它的好处是统一数据访问方式。
(3)ContentProvider实现数据共享。ContentProvider用于保存和获取数据,并使其对所有应用程序可见。这是不同应用程序间共享数据的唯一方式,因为android没有提供所有应用共同访问的公共存储区。
(4)开发人员不会直接使用ContentProvider类的对象,大多数是通过ContentResolver对象实现对ContentProvider的操作。
(5)ContentProvider使用URI来唯一标识其数据集,这里的URI以content://作为前缀,表示该数据由ContentProvider来管理。
1 Content Provider简述
在这里插入图片描述

ContentProvider虽说我们平时用的并不多,但是作为安卓四大组件之一,其地位不容忽视。ContentProvider的作用是为不同的应用之间数据共享,提供统一的接口,我们知道安卓系统中应用内部的数据是对外隔离的,要想让其它应用能使用自己的数据(例如通讯录)这个时候就用到了ContentProvider。
ContentProvider是Android系统中为开发者专门提供的不同应用间进行数据共享的组件,其提供了一套标准的接口用来获取以及操作数据,准许开发者把自己的应用数据根据需求开放给其他应用进行增删改查,而无须担心直接开放数据库权限而带来的安全问题。系统预置了许多ContentProvider用于获取用户数据,例如消息、联系人、日程表等
具体形式如下图所示:
在这里插入图片描述

2 Content Provider使用方式
2.1 ContentResolver
在ContentProvider的使用过程中,需要借用ContentResolver来控制ContentProvider所暴露处理的接口,作为代理来间接操作ContentProvider以获取数据。
在 Context.java 的源码中如下抽象方法
/** Return a ContentResolver instance for your application’s package. */
public abstract ContentResolver getContentResolver();

所以可以在所有继承Context的类中通过 getContentResovler() 方法获取ContentResolver
ContentResolver contentResolver = getContentResovler();

/**

  • @author: 下码看花

  • date: 2019/8/8

  • description: ContentProvider例子
    /public class MyContentProvider extends ContentProvider { /*
    * 执行初始化工作
    * @return
    /
    @Override
    public boolean onCreate() { return false;
    }
    /
    *
    * 查询数据
    * @param uri
    * @param projection
    * @param selection
    * @param selectionArgs
    * @param sortOrder
    * @return
    /
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { return null;
    }
    /
    *
    * 通过Uri返回对应的MIME类型
    * @param uri
    * @return
    /
    @Override
    public String getType(@NonNull Uri uri) { return null;
    }
    /
    *
    * 插入新数据
    * @param uri
    * @param values
    * @return
    */
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { return null;
    }

      /**     
      * 删除已有数据    
      * @param uri     
      * @param selection     
      * @param selectionArgs     
      * @return     
      */   
      @Override    
      public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[]                 selectionArgs) {                return 0;    
      }        /**     
      * 更新数据     
      * @param uri     
      * @param values     
      * @param selection     
      * @param selectionArgs     
      * @return     
      */    
      @Override    
      public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String                 selection, @Nullable String[] selectionArgs) {                return 0;    
      }
    

}

之后,需要在AdnroidManifest.xml中对ContentProvider进行注册

name:自定义的ContentProvider的全称类名。
authorities:自定义ContentProvider的唯一标识,外部应用通过该属性值来访问我们的ContentProvider。因此该属性值必须是唯一的,建议在命名时以包名为前缀。
exported:表明是否允许其他应用调用ContentProvider,true表示支持,false表示不支持。默认值根据开发者的属性设置而会有所不同,如果包含 Intent-Filter 则默认值为true,否则为false。
2.2 ContentProvider
ContentProvider作为Android四大组件之一,并没有Activity那样复杂的生命周期,只有简单地onCreate过程。
创建一个自定义ContentProvider的方式是继承ContentProvider类并实现其六个抽象方法:
3 ContentProvider数据共享
用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序的数据,同时保证被访数据的安全性,使用ContentProvider是Android实现跨程序共享数据的标准方式。不同于文件存储和SharedPreferences,ContentProvider可以选择只对哪一部分数据进行共享。
##ContentResolver用法##
对于每一个应用程序来说,如果想要访问ContentProvider中共享的数据,就一定要借助ContentResolver类,可以通过Content中的getContentResolver()方法获取到改类的实例。ContentResolver提供了一系列的方法用于对数据进行CRUD操作。

  • insert()插入
  • update()更新
  • delete()删除
  • query()查询
    不同于SqLiteDatabase,ContentResolver中的增删改查不接收表名参数,而是使用一个Uri参数代替,这个参数被称为内容URI。内容URI给ContentProvider中的数据建立了唯一标识符,主要由两部分组成:authority和path。
    authority:用于对不同的应用程序做区分的,一般会采用程序包名进行命名。比如某个程序的包名是com.example.app,那么该程序的authority可以命名为:com.example.app.provider。
    path:则是用于对同一应用程序中不同的表做区分的,通常添加到authority后面,比如某个程序的数据库有2张表,table1和table2,path分别命名为/table1和/table2,然后把authority和path进行组合,内容URI就变成了com.example.app.provider/table1和com.example.app.provider/table2,

最后在头部加上协议声明就可以了。
content://com.example.app.provider/table1
content://com.example.app.provider/table2

在得到了内容URI字符串后,需要将他解析成Uri对象才可以作为参数传入。
Uri uri=Uri.parse(“content://com.example.app.provider/table1”);

##查询##
现在就可以使用这个Uri对象来查询table1表中的数据了。
Cursor cursor=getContentResolver().query(
uri,
projection,
selection,
selectionArgs,
sortOrder);

这些参数和SQLiteDatabase中query()方法里的参数很像。
query()参数
对应SQL部分
描述
uri
from table_name
指定查询某个应用程序下的某一张表
projection
select column1,column2
指定查询的列名
selection
where column=value
指定where的约束条件
selectionArgs

为where中的占位符提供具体的值
sortOrder
order by column1,column2
指定查询结果的排序方式

查询完成后返回的是一个Cursor对象。读取的思路是通过移动游标的位置来遍历Cursor的所有行,然后取出相应列的数据
if(cursor!=null){
while(cursor.moveToNext()){
String column1=cursor.getString(cursor.getColumnIndex(“column1”));
int column2=cursor.getInt(cursor.getColumnIndex(“column2”));
}
cursor.close();
}

##添加##
ContentValues values=new ContentValues();
values.put(“column1”,“text”);
values.put(“column2”,1);
getContentResolver().insert(uri,values);

##更新##
把column1的值清空

ContentValues values=new ContentValues();
values.put(“column1”," ");
getContentResolver().update(uri,values,“column1=? and column2=?”,new String[]{“text”,1});

##删除##
getContentResolver().delete(uri,“column2=?”,new String[]{“1”});

##读取联系人##
以读取联系人为例子,实战使用一下。
在这里插入图片描述
在这里插入图片描述

##activity_main.xml

<?xml version="1.0" encoding="utf-8"?>

<ListView
    android:id="@+id/lv_list"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</ListView>

##MainActivity##
里面使用了运行时权限的知识,如果不熟悉的话,点击我!
public class MainActivity extends AppCompatActivity {
ArrayAdapter adapter;
List lists = new ArrayList<>();

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ListView lv_list = (ListView) findViewById(R.id.lv_list);
    //创建设置适配器
    adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, lists);
    lv_list.setAdapter(adapter);
    //申请读取联系人权限
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS}, 1);
    } else {
        readContacts();
    }
}

private void readContacts() {
    Cursor cursor = null;
    try {
        cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
        if (cursor != null) {
            while (cursor.moveToNext()) {
                //获取联系人姓名
                String displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
                //获取联系人手机号
                String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                lists.add(displayName + "\n" + number);
            }
            adapter.notifyDataSetChanged();
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (cursor != null) {
            cursor.close();
        }
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    switch (requestCode) {
        case 1:
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                readContacts();
            } else {
                Toast.makeText(this, "取消授权", Toast.LENGTH_SHORT).show();
            }
            break;
        default:
            break;
    }
}

}

读取联系人的权限也要去AndroidManifest.xml中声明

4 Uri
观察MyContentProvider中的几个方法,可以发现除了 onCreate() 方法外,其它五个抽象方法都包含了一个Uri(统一资源标识符)参数,通过这个对象可以来匹配对应的请求。那么从ContentProvider的数据操作方法可以看出都依赖于Uri,而对于Uri有其固定的数据格式,例如:
字段
含义

前缀
默认的固定开头格式
content://
授权
唯一标识,外部调用者根据这个标识来指定要操作的ContentProvider
com.xmkh.contentproviderdemo.MyContentProvider
路径
数据类别以及数据项,用来标识要操作的具体对象
/article

比如,ContentProvider中操作的数据可以都是从SQLite数据库中获取的,而数据库中可能存在许多张表,这时候就需要用到Uri来表明是要操作哪个数据库、操作数据库的哪张表了
/**

  • @author: 下码看花
  • date: 2019/8/8
  • description: URI组装代码示例
    */public class TestContract { public static final String CONTENT_AUTHORITY = “com.xmkh.contentproviderdemo.MyContentProvider”; public static final Uri BASE_CONTENT_URI = Uri.parse(“content://” + CONTENT_AUTHORITY); public static final String PATH_ARTICLE = “article”; public static final class TestEntry implements BaseColumns { public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon().appendPath(PATH_ARTICLE).build(); protected static Uri buildUri(long id) { return ContentUris.withAppendedId(CONTENT_URI, id);
    } protected static final String TABLE_NAME = “article”; public static final String COLUMN_NAME = “name”;
    }
    }

从上面代码我们可以看到,我们创建了一个 content://com.xmkh.contentproviderdemo.MyContentProvider/article的uri,并且开了一个静态方法,用以在有新数据产生时根据id生成新的Uri。
那么如何确定一个Uri是要执行哪项操作呢?这里需要用到UriMatcher来帮助ContentProvider匹配Uri,它仅包含了两个方法:
addURI(String authority, String path, int code):用于向UriMatcher对象注册Uri。authtity是在AndroidManifest.xml中注册的ContentProvider的authority属性值;path表示一个路径,可以设置为通配符,#表示任意数字,表示任意字符;两者组合成一个Uri,而code则代表该Uri对应的标识码
match(Uri uri):匹配传入的Uri。返回addURI()方法中传递的code参数,如果找不到匹配的标识码则返回-1
4.1 实战
写一个demo
首先,自定义一个ContentProvider,然后向其写入和读取数据,使用SQLite作为ContentProvider的数据存储地址和数据来源,因此需要先建立一个SQLiteOpenHelper,创建一个名为"article.db"的数据库,包含“article”和“author”两张表:
/
*

  • @author: 下码看花
  • date: 2019/8/8
  • description: 演示demo数据库
    /public class DbOpenHelper extends SQLiteOpenHelper {
    /
    *
    • 数据库名
      /
      private static final String DATA_BASE_NAME = “article.db”; /
      *
    • 数据库版本号
      /
      private static final int DATE_BASE_VERSION = 1; /
      *
    • 表名-文章
      /
      public static final String ARTICLE_TABLE_NAME = “article”; /
      *
    • 表名-作者
      /
      public static final String AUTHOR_TABLE_NAME = “author”; /
      *
    • 创建表-文章(两列:主键自增长、文章名称)
      /
      private final String CREATE_ARTICLE_TABLE = "create table " + ARTICLE_TABLE_NAME
      + “(_id integer primary key autoincrement, articleName text)”; /
      *
    • 创建表-作者(三列:主键自增长、作者名、性别)
      */
      private final String CREATE_AUTHOR_TABLE = "create table " + AUTHOR_TABLE_NAME
      + “(_id integer primary key autoincrement, authorName text, sex text)”; public DbOpenHelper(Context context) { super(context, DATA_BASE_NAME, null, DATE_BASE_VERSION);
      } @Override
      public void onCreate(SQLiteDatabase db) {
      db.execSQL(CREATE_ARTICLE_TABLE);
      db.execSQL(CREATE_AUTHOR_TABLE);
      } @Override
      public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
      }
      }

自定义ContentProvider 当中,getTableName(Uri uri)方法用于判定Uri指向的数据库表名 然后在initProviderData()方法中向数据库插入一些原始数据
为了方便大家理解,我们将上述出现的代码进行修改,展示给大家:
/**

  • @author: 下码看花
  • date: 2019/8/8
  • description: ContentProvider例子
    /public class MyContentProvider extends ContentProvider {
    private Context mContext; private SQLiteDatabase sqLiteDatabase; public static final String AUTHORITY = “com.xmkh.MyContentProvider”; public static final int ARTICLE_URI_CODE = 0; public static final int AUTHOR_URI_CODE = 1; private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); static {
    uriMatcher.addURI(AUTHORITY, DbOpenHelper.ARTICLE_TABLE_NAME, ARTICLE_URI_CODE);
    uriMatcher.addURI(AUTHORITY, DbOpenHelper.AUTHOR_TABLE_NAME, AUTHOR_URI_CODE);
    } private String getTableName(Uri uri) {
    String tableName = null; switch (uriMatcher.match(uri)) { case ARTICLE_URI_CODE:
    tableName = DbOpenHelper.ARTICLE_TABLE_NAME; break; case AUTHOR_URI_CODE:
    tableName = DbOpenHelper.AUTHOR_TABLE_NAME; break;
    } return tableName;
    } public MyContentProvider() {
    } /
    *
    • 执行初始化工作
    • @return
      /
      @Override
      public boolean onCreate() {
      mContext = getContext();
      initArticleProviderData(); return false;
      } //初始化原始数据
      private void initArticleProviderData() {
      sqLiteDatabase = new DbOpenHelper(mContext).getWritableDatabase();
      sqLiteDatabase.beginTransaction();
      ContentValues contentValues = new ContentValues();
      contentValues.put(“articleName”, “Android四大组件之Activity”);
      sqLiteDatabase.insert(DbOpenHelper.ARTICLE_TABLE_NAME, null, contentValues);
      contentValues.put(“articleName”, “Android四大组件之BroadcastReceiver”);
      sqLiteDatabase.insert(DbOpenHelper.ARTICLE_TABLE_NAME, null, contentValues);
      contentValues.put(“articleName”, “Android四大组件之Service”);
      sqLiteDatabase.insert(DbOpenHelper.ARTICLE_TABLE_NAME, null, contentValues);
      contentValues.clear();
      contentValues.put(“authorName”, “ptt”);
      contentValues.put(“sex”, “女”);
      sqLiteDatabase.insert(DbOpenHelper.AUTHOR_TABLE_NAME, null, contentValues);
      contentValues.put(“authorName”, “HiYoung”);
      contentValues.put(“sex”, “男”);
      sqLiteDatabase.insert(DbOpenHelper.AUTHOR_TABLE_NAME, null, contentValues);
      contentValues.put(“authorName”, “gy”);
      contentValues.put(“sex”, “男”);
      sqLiteDatabase.insert(DbOpenHelper.AUTHOR_TABLE_NAME, null, contentValues);
      sqLiteDatabase.setTransactionSuccessful();
      sqLiteDatabase.endTransaction();
      } /
      *
    • 查询数据
    • @param uri
    • @param projection
    • @param selection
    • @param selectionArgs
    • @param sortOrder
    • @return
      /
      @Override
      public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
      String tableName = getTableName(uri); if (tableName == null) { throw new IllegalArgumentException(“Unsupported URI:” + uri);
      } return sqLiteDatabase.query(tableName, projection, selection, selectionArgs, null, null, sortOrder, null);
      } /
      *
    • 通过Uri返回对应的MIME类型
    • @param uri
    • @return
      /
      @Override
      public String getType(@NonNull Uri uri) { return null;
      } /
      *
    • 插入新数据
    • @param uri
    • @param values
    • @return
      /
      @Override
      public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
      String tableName = getTableName(uri); if (tableName == null) { throw new IllegalArgumentException(“Unsupported URI:” + uri);
      }
      sqLiteDatabase.insert(tableName, null, values);
      mContext.getContentResolver().notifyChange(uri, null); return uri;
      } /
      *
    • 删除已有数据
    • @param uri
    • @param selection
    • @param selectionArgs
    • @return
      /
      @Override
      public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
      String tableName = getTableName(uri); if (tableName == null) { throw new IllegalArgumentException(“Unsupported URI:” + uri);
      } int count = sqLiteDatabase.delete(tableName, selection, selectionArgs); if (count > 0) {
      mContext.getContentResolver().notifyChange(uri, null);
      } return count;
      } /
      *
    • 更新数据
    • @param uri
    • @param values
    • @param selection
    • @param selectionArgs
    • @return
      */
      @Override
      public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
      String tableName = getTableName(uri); if (tableName == null) { throw new IllegalArgumentException(“Unsupported URI:” + uri);
      } int row = sqLiteDatabase.update(tableName, values, selection, selectionArgs); if (row > 0) {
      mContext.getContentResolver().notifyChange(uri, null);
      } return row;
      }
      }

ContentProvider创建好切记一定要在 AndroidManifest.xml中注册!(前文已经提到了如何注册,我就不再复述啦~)然后分别操作article和author两张表,向其插入一条数据后Log输出所有的数据
/**

  • @author: 下码看花
  • date: 2019/8/8
  • description: ContentProvider实战
    */public class MainActivity extends AppCompatActivity {
    private final String TAG = “MainActivity”; @Override
    protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Uri articleUri = Uri.parse(“content://com.xmkh.MyContentProvider/article”);
    ContentValues contentValues = new ContentValues();
    contentValues.put(“articleName”, “Android四大组件之ContentProvider”);
    getContentResolver().insert(articleUri, contentValues);
    Cursor articleCursor = getContentResolver().query(articleUri, new String[]{“_id”, “articleName”}, null, null, null); if (articleCursor != null) { while (articleCursor.moveToNext()) {
    Log.e(TAG, “ID:” + articleCursor.getInt(articleCursor.getColumnIndex(“_id”))
    + " ArticleName:" + articleCursor.getString(articleCursor.getColumnIndex(“articleName”)));
    }
    articleCursor.close();
    }
    Uri authorUri = Uri.parse(“content://com.xmkh.MyContentProvider/author”);
    contentValues.clear();
    contentValues.put(“authorName”, “Austen”);
    contentValues.put(“sex”, “男”);
    getContentResolver().insert(authorUri, contentValues);
    Cursor authorCursor = getContentResolver().query(authorUri, new String[]{“_id”, “authorName”, “sex”}, null, null, null); if (authorCursor != null) { while (authorCursor.moveToNext()) {
    Log.e(TAG, “ID:” + authorCursor.getInt(authorCursor.getColumnIndex(“_id”))
    + " AuthorName:" + authorCursor.getString(authorCursor.getColumnIndex(“authorName”))
    + " Sex:" + authorCursor.getString(authorCursor.getColumnIndex(“sex”)));
    }
    authorCursor.close();
    }
    }
    }

得到的输出结果是:
在这里插入图片描述

4.2 结语
ContentProvider作为Android的四大组件之一,虽说我们平时用的并不多,但是作为安卓四大组件之一,其地位不容忽视。 小伙伴们赶紧上手实操,把它灵活的运用到项目中,让我们每天一起快乐的进步吧~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北境王

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值