使用ContentProvider进行应用程序间的数据交互
什么是ContentProvider:
ContentProvider用来管理数据的访问规则。它允许你的应用程序向外界暴露需要被访问的数据。
是Android的四大组件之一。
ContentProviders支持四种基本的操作,即我们平时所见到的CRUD操作(增删改查)。Android系统
本身已经提供了一些内容提供者,它们允许我们查询联系人,媒体库,和短息消息等。
基于Content Uri的查询:
没有Uri,ContentProvider 类基本无法工作,就像我们上网没有网址。当我们要上网,就要在地址栏输入网址。
因此,要了解ContentProvider,我们必须先了解 Content Uri
下面是Uri的基本格式:
1 | content://authority/optionalPath/optionalId |
content://是内容提供者的标准前缀,而且它必须是content://。authority由我们自己定义,它必须具有唯一性,
因此我们一般使用应用程序包名命名authority。optionalPath和optionalId是可选的。
下面我们来看一下WordPress的文章地址格式:
http://www.whathecode.com/archives/221
http:// 是http协议的标准写法,它是规定的,没有为什么。
www.whathecode.com可以理解为authority,因为域名是唯一的,它可以分辨我们要访问哪一个网站。
arichives可以理解为文章的分类。
最后的221就是文章的id,假如我们要查看第一遍文章,只要将221改成1就可以。
这样理解Uri是不是简单了很多。
建立自己的ContentProvider
建立ContentProvider只需几部:
1. 设计数据库的储存方式,因为ContentProvider提供的是数据,没有数据,ContentProvider就没有了用处。
2. 定义自己的类,继承ContentProvider类,并实现基本的方法。
3. 设计authority字符串。(要被人访问你的网站,你就需要一个网址,当然ip地址也可以)
4. 在AndroidManifest中注册Provider
我们先来研究一下代码:
作为示例下面的代码只实现了query方法,而且只添加了一个Uri,
一般应用程序都不只一个Uri
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | package com.whathecode.provider; import android.content.ContentProvider; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteOpenHelper; import android.net.Uri; import android.util.Log; import android.widget.Toast; public class MemberProvider extends ContentProvider { private static final UriMatcher sMatcher; private static final String MEMBER_NAME = "name" ; private static final int QUERY_BY_NAME = 0 ; private static final String DB_NAME = "member.db" ; private static final String TAG = "MemberProvider" ; static { sMatcher = new UriMatcher(UriMatcher.NO_MATCH); /** * 添加需要匹配的Uri, 当这个Uri被匹配的时候返回第三个参数。 * 得到这个参数之后就可以在query,insert,update,delete * 方法中做出相应的动作。 */ sMatcher.addURI(Member.AUTHORITY, MEMBER_NAME, QUERY_BY_NAME); } private static class DataBaseHelper extends SQLiteOpenHelper { public DataBaseHelper(Context context, String name, CursorFactory factory, int version) { super (context, name, factory, version); } @Override public void onCreate(SQLiteDatabase db) { //建立数据库表结构 db.execSQL( "create table member(_id integer primary key," + "name varchar(20)," + "work varchar(20)," + "age integer)" ); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // TODO Auto-generated method stub } } private DataBaseHelper mHelper; @Override public boolean onCreate() { mHelper = new DataBaseHelper(getContext(), DB_NAME, null , 2 ); return true ; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { /** * 前面我们在静态代码块中已添加了Uri库 * 当每个Uri作为参数传进来的时候我们都先进行匹配 * */ switch (sMatcher.match(uri)) { case QUERY_BY_NAME: //第一次执行时生成数据库member.db SQLiteDatabase readableDatabase = mHelper.getReadableDatabase(); /** * 当query方法执行的时候向外发送广播 */ Intent intent = new Intent(); intent.setAction( "com.contentprovider.execquery" ); intent.putExtra( "MemberProvider" , "query方法执行了" ); getContext().sendBroadcast(intent); break ; default : /** * 当Uri匹配失败的时候说明Uri参数错误,无法继续进行操作, * 因此程序抛出一个错误 */ throw new IllegalArgumentException( "Unknown URI " + uri); } return null ; } @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 return null ; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { // TODO Auto-generated method stub return 0 ; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { // TODO Auto-generated method stub return 0 ; } } |
这样,一个虽简单但可行的内容提供者就创建好了,现在我们只需在AndroidManifest文件中注册它。
1 2 3 | < provider android:name="com.whathecode.provider.MemberProvider" android:authorities="com.whathecode.contentproviderdemo.member"> </ provider > |
接下来,运行这个程序,将我们刚才定义的ContentProvider部署到模拟器。
然后,我们建立另外一个程序测试这个内容提供者是否真的工作正常。
下面是另外一个程序的代码:
我们需要另外一个类进行访问ContentProvider提供的数据。
这个类就是ContentResolver,它提供了和ContentProvider一样的CRUD方法。
方便我们查询数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | package com.whathecode.contentprovidertest; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; import android.os.Bundle; import android.view.View; import android.widget.Toast; public class MainActivity extends Activity { private BroadcastReceiver receiver; @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); //创建一个广播接受者用于接收MemberProvider发出的广播 receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { /** * 当广播被接收到的时候Toast提示用户接收到的信息 */ Toast.makeText(getBaseContext(), "收到的信息" + intent.getStringExtra( "MemberProvider" ), Toast.LENGTH_SHORT).show(); } }; } @Override protected void onResume() { super .onResume(); //注册广播接受者 registerReceiver(receiver, new IntentFilter( "com.contentprovider.execquery" )); } public void onClick(View view) { /** * 获取ContentResolver实例访问ContentProvider */ ContentResolver resolver = getContentResolver(); Uri uri = Uri .parse( "content://com.whathecode.contentproviderdemo.member/name" ); resolver.query(uri, null , null , null , null ); } @Override protected void onPause() { super .onPause(); //取消注册广播接受者 unregisterReceiver(receiver); } } |
运行效果:
如果Toast运行证明MemberProvider中的query方法被成功执行
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】博客园2025新款「AI繁忙」系列T恤上架,前往周边小店选购
【推荐】凌霞软件回馈社区,携手博客园推出1Panel与Halo联合会员
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Android编译时动态插入代码原理与实践
· 解锁.NET 9性能优化黑科技:从内存管理到Web性能的最全指南
· 通过一个DEMO理解MCP(模型上下文协议)的生命周期
· MySQL下200GB大表备份,利用传输表空间解决停服发版表备份问题
· 记一次 .NET某固高运动卡测试 卡慢分析
· STM32真的是很落后吗?
· .NET周刊【4月第1期 2025-04-06】
· Android编译时动态插入代码原理与实践
· 国产的 Java Solon v3.2.0 发布(央企信创的优选)
· Spring Security认证与授权