ContentProvider
ContentProvider用于实现数据共享.
ContentProvider是不同应用程序之间进行数据交换的标准API,其以某种Uri的形式对外提供数据,允许其他应用程序访问或修改数据;
其他应用程序使用ContentResolver根据Uri去访问操作指定数据。
注意:ContentProvider为android四大组件之一,需要在AndroidMainfest.xml中做配置处理。
如果把ContentProvider当做一个网站,那么完成的开发一个ContentProvider的操作步骤如下:
1.定义自己的ContentProvider类,该类需要继承android提供的ContentProvider基类;
2.向android系统注册这个“网站”,也就是在AndroidMainfest.xml文件中注册这个ContentProvider——就像注册Activity一样。注册ContentProvider需要为其绑定一个URL.
注意:
向android系统中注册ContentProvider只要在<application.../>元素中添加子元素即可,如下所示:
当我们向android系统注册了ContentProvider之后,其他应用程序就可通过Uri来访问DictProvider所暴露的数据了。
那么DictPovider到底如何暴露其提供的数据?
==》
由于应用程序对数据的操作无非就是CRUD,因此DictProvider除了需要继承ContentProvider之外,还需要提供如下几个方法:
public boolean onCreate() |
该方法在ContentProvider创建后被调用,当其他应用程序第一次访问ContentProvider时,该ContentProvider会被创建出来, 并立即回调该onCreate() |
public Uri insert(Uri uri,ContentValues values) | 根据Uri插入values对应的数据 |
public int delete(Uri uri,String selection,String[] selecttionArgs) | 根据Uri删除select条件所匹配的所有记录 |
publc int update(Uri uri,ContentValues values,String selection,Sting[] selecttionArgs) | 根据Uri修改select条件所匹配的所有记录 |
public Cursor query(Uri uri,Sting[] projection,String selection,Sting[] selectionArgs,String sortOrder) | 根据Uri查询select条件所匹配的所有记录,其中projection就是一个列名列表,表明只选择出指定的列的数据列 |
public String getType(Uri uri) | 该方法用于返回当前Uri所代表的数据的MIME类型。如果该Uri对应数据可能包括多条记录,那么MIME类型字符串应该以vnd.android.cursor.dir/开头;如果该Uri对应的数据只包含一条记录,那么返回MIME类型字符串应该以vnd.android.cursor.item/开头 |
Uri简介
网页URL,如:https://www.baidu.com/s?tn=baidutop...
1.http://——URL协议部分,只要通过HTTP协议来访问网站,该部分为固定信息;
2.www.baidu.com——网站域名部分,只要通过http协议来访问网站,该部分总是固定信息;
3.s?tn=baidutop...——网站资源部分, 当访问者需要访问不同资源时,这个部分是动态改变的。
Android Uri因此类似,如下:
content://org.crazyit.providers.dictprovider/words
其也可分为三个部分:
1.content://,这个不是android规定的,是固定的;
2.org.crazyit.providers.dictprovider,这个部分就是ContentProvider的authority.系统就是由这个部分来找到操作那个ContentProvider.只要访问指定的ContentProvider,这个部分就是固定的;
3.words,资源部分或者说是数据部分,当访问者需要访问不同资源时,这个部分是动态改变的。
注意:
需要指出的是,Android的Uri所能表达的功能更丰富,还可以支持如下Uri:
content://org.crazyit.providers.dictprovider/words/2——此时需要访问的资源为words/2,其意味着访问words数据中ID为2的记录的word字段。
content://org.crazyit.providers.dictprovider/words——访问全部数据;
大部分使用ContentProvider所操作的数据都来自于数据库,但有时候这些数据也可以来自于文件、XML或网络等其他存储方式,此时支持的Uri也可改为如下形式:
content://org.crazyit.providers.dictprovider/word/detail/——代表操作word节点下的detail节点;
为了将一个字符串转换成Uri,Android提供的Uri工具类提供了parse()静态方法,转换方式如下:
Uri uri = Uri.parse("content://org.crazyit.providers.dictprovider/words/2");
使用ContentResolver操作数据
ContentProvider相当于一个“网站”,它的作用是暴露可供操作的数据;
其他程序则通过ContentResolver来操作ContentProvider所暴露的数据;ContentResolver相当于HttpClient.
Content提供了如下方法来获取ContentResolver对象:getContentResolver().
获取ContentResolver对象成功后,可通过如下方法操作数据:
insert(Uri uri,ContentValues values) | 向Uri对应的ContentProvider中插入values对应的数据 |
delete(Uri uri,String where,Sting[] selectionArgs) | 删除uri对应的ContentProvider中where提交匹配的数据 |
update(Uri uri,ContentValues values,String where,String[] selectionArgs) | 更新uri对应的ContentProvider中where提交匹配的数据 |
query(Uri uri,Sting[] projection,String selection,String[] selectionArgs,String sortOrder) | 查询Uri对应的ContentProvider中where提交匹配的数据 |
注意:一般来说,ContentProvider是单实例模式的,当多个应用程序通过ContentResolver来操作ContentProvider提供的数据时,ContentResolver
调用的数据操作将会委托给同一个ContentProvider处理。
操作系统的ContentProvider
使用ContentProvider操作数据的操作步骤:
1.调用Activity的ContentResolver()获取ContentResolver对象
2.根据需要调用ContentResolver的insert()、delete()、update()、query方法操作数据即可
注意:操作系统提供的ContentResolver,需要了解其对应的ContentProvider对应的Uri.
使用ContentProvider管理联系人
Android系统提供了Contacts应用程序来管理联系人,而且Android系统还为联系人管理提供了ContentProvider,这就运行其他应用程序以ContentResolver来管理联系人数据。
Android系统对联系人管理ContentProvider的几个Uri如下:
ContactsContact.Contacts.CONTENT_URI | 管理联系人的Uri |
ContactsContact.CommonDataKinds.Phone.CONTENT_URI | 管理联系人的电话的Uri |
ContactsContact.CommonDataKinds.Email.CONTENT_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 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 | 布局文件main.xml==> <LinearLayout xmlns:android= "http://schemas.android.com/apk/res/android" xmlns:tools= "http://schemas.android.com/tools" android:layout_width= "match_parent" android:layout_height= "match_parent" android:orientation= "vertical" tools:context= ".MainActivity" > <Button android:id= "@+id/btnQuery" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:text= "查询" /> </LinearLayout> result.xml==> <LinearLayout xmlns:android= "http://schemas.android.com/apk/res/android" android:layout_width= "fill_parent" android:layout_height= "fill_parent" android:orientation= "vertical" > <ExpandableListView android:id= "@+id/list" android:layout_width= "fill_parent" android:layout_height= "fill_parent" android:background= "#ffffff" android:cacheColorHint= "#00000000" android:listSelector= "#00000000" > </ExpandableListView> </LinearLayout> AndroidMainfest.xml==> <uses-permission android:name= "android.permission.READ_CONTACTS" /> <uses-permission android:name= "android.permission.WRITE_CONTACTS" /> 代码实现==》 package com.example.myprovidercontent1; import java.util.ArrayList; import android.os.Bundle; import android.provider.ContactsContract; import android.app.Activity; import android.app.AlertDialog; import android.database.Cursor; import android.database.DataSetObserver; import android.util.Log; import android.view.Gravity; import android.view.Menu; import android.view.View; import android.view.ViewGroup; import android.view.View.OnClickListener; import android.widget.AbsListView; import android.widget.AbsListView.LayoutParams; import android.widget.Button; import android.widget.ExpandableListAdapter; import android.widget.ExpandableListView; import android.widget.TextView; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button btnQuery = (Button) this .findViewById(R.id.btnQuery); btnQuery.setOnClickListener( new OnClickListener() { @Override public void onClick(View v) { query(v); } }); } private void query(View v) { // 定义两个List封装系统联系人信息、指定联系人的电话号码、Email等... final ArrayList<String> names = new ArrayList<String>(); final ArrayList<ArrayList<String>> details = new ArrayList<ArrayList<String>>(); // 使用ContentResolver查找联系人数据 Cursor cursor = this .getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null , null , null , null ); // 遍历查询结果,获取系统中所有联系人 while (cursor.moveToNext()) { // 联系人Id、名称 String contactId = cursor.getString(cursor .getColumnIndex(ContactsContract.Contacts._ID)); String name = cursor.getString(cursor .getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)); names.add(name); Log.i( "swg" , "name:" + name); // 联系人电话号码 Cursor phones = this .getContentResolver() .query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null , ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=" + contactId, null , null ); final ArrayList<String> detail = new ArrayList<String>(); // 遍历查询结果,获取该联系人的多个 电话号码 while (phones.moveToNext()) { String phoneNum = phones.getString(phones .getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); Log.i( "swg" , "电话号码:" + phoneNum); detail.add( "电话号码:" + phoneNum); } phones.close(); // 使用ContentResolver查询联系人Email Cursor emails = this .getContentResolver() .query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, null , ContactsContract.CommonDataKinds.Email.CONTACT_ID + "=" + contactId, null , null ); // 遍历查询结果,获取该联系人的多个Email地址 while (emails.moveToNext()) { String emailAdd = emails.getString(emails .getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA)); Log.i( "swg" , "邮件地址:" + emailAdd); detail.add( "邮件地址:" + emailAdd); } emails.close(); details.add(detail); } cursor.close(); // 加载result.xml界面布局文件 View resultDialog = this .getLayoutInflater().inflate(R.layout.result, null ); ExpandableListView list = (ExpandableListView) resultDialog.findViewById(R.id.list); // 创建一个ExpandableListAdapter对象 ExpandableListAdapter adapter = new ExpandableListAdapter() { @Override public void unregisterDataSetObserver(DataSetObserver observer) { } @Override public void registerDataSetObserver(DataSetObserver observer) { } @Override public void onGroupExpanded( int groupPosition) { } @Override public void onGroupCollapsed( int groupPosition) { } @Override public boolean isEmpty() { return false ; } @Override public boolean isChildSelectable( int groupPosition, int childPosition) { return true ; } @Override public boolean hasStableIds() { return true ; } // 该方法决定每个组选项的外观 @Override public View getGroupView( int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { TextView tv = getTextView(); tv.setText(getGroup(groupPosition).toString()); return tv; } @Override public long getGroupId( int groupPosition) { return groupPosition; } @Override public int getGroupCount() { // details return names.size(); } @Override public Object getGroup( int groupPosition) { // details return names. get (groupPosition); } @Override public long getCombinedGroupId( long groupId) { return 0; } @Override public long getCombinedChildId( long groupId, long childId) { return 0; } @Override public int getChildrenCount( int groupPosition) { return details. get (groupPosition).size(); } private TextView getTextView() { AbsListView.LayoutParams lp = new LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, 64); TextView tv = new TextView(MainActivity. this ); tv.setLayoutParams(lp); tv.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.LEFT); tv.setPadding(36, 0, 0, 0); tv.setTextSize(20); return tv; } // 该方法决定每个子项的外观 @Override public View getChildView( int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { TextView tv = getTextView(); tv.setText(getChild(groupPosition, childPosition).toString()); return tv; } @Override public long getChildId( int groupPosition, int childPosition) { return childPosition; } @Override public Object getChild( int groupPosition, int childPosition) { return details. get (groupPosition). get (childPosition); } @Override public boolean areAllItemsEnabled() { return false ; } }; // 为ExpandableListView设置Adapter list.setAdapter(adapter); // 使用对话框来显示查询结果 new AlertDialog.Builder(MainActivity. this ).setView(resultDialog) .setPositiveButton( "确定" , null ).show(); } } |
实例二、添加联系人
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本