Android跨进程数据共享——ContentProvider详解
2015-12-01 09:16
597 查看
一、ContentProvider介绍
作为android四大组件之一,ContentProvider可能是四大组件中我们用到最少的。它作为跨进程数据共享来使用,而我们开发app的时候,基本上是独立的,不会与其他的app发生数据间的通讯。
但如果两个或者多个app需要共享一个数据源的时候,ContentProvider就显的非常必要且高安全性,因为我们可以控制数据源的哪些数据可以被访问,哪些不能被访问。
二、ContentProvider的使用
ContentProvider的使用归结有两种:使用别人(系统)提供的ContentProvider、使用自定义的ContentProvider。在使用ContentProvider的过程中有些东西是我们要注意的:
注意点:
Uri的拼接;MIME的拼接;
ContentProvider如何提供数据源;
Provider的注册、另一个app如何声明Provider的权限并使用;
1、使用系统提供的ContentProvider
这里我们用手机中常见的电话来举例:电话用户名称和电话号码的显示。电话系统的管理是android系统本身自带的,因此,我们现在就是使用别人提供的ContentProvider..
源码如下:
package com.example.contentporvider_csdn; import java.util.ArrayList; import java.util.List; import android.os.Bundle; import android.provider.ContactsContract; import android.app.Activity; import android.content.ContentResolver; import android.database.Cursor; import android.view.Menu; import android.webkit.WebChromeClient.CustomViewCallback; import android.widget.ArrayAdapter; import android.widget.ListView; public class MainActivity extends Activity { ListView listView; List<String> list; ArrayAdapter<String> arrayAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } private void initView() { listView=(ListView)findViewById(R.id.mylistview); list=new ArrayList<String>(); arrayAdapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, list); listView.setAdapter(arrayAdapter); getContentProvider(); } private void getContentProvider() { Cursor cursor=getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null,null); if(cursor!=null) { while (cursor.moveToNext()) { String displayNameString=cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); String numberString=cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); list.add(displayNameString+"\n"+numberString); } cursor.close(); } } }对应的布局如下:
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity" > <ListView android:id="@+id/mylistview" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </RelativeLayout>对比我们以前的代码,我们发现唯一有些特殊的地方就在于:
Cursor cursor=getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null,null);对这个获取数据方式,系统的解释是:
Cursor android.content.ContentResolver.query(Uri
uri,String[] projection,String selection,String[]
selectionArgs,String sortOrder)
详细释义:
getContentResolver():
返回的是一个ContentResolver类,该类主要用于处理ContentProvider的增删改查等。
query(...):
这是与Sqlite类似的一个查询方法,不同的地方主要在于首个参数,Uri,这个会在下面解释。
第二个参数projection,用于说明哪些列可以访问,一般置为null就可以,表示都可以;
第三个参数selecction,用于描述参数条件,一般与第四个参数联合使用。如,"name=?";
第四个参数selectionArgs,用于匹配第三个参数中的占位符。如,new String[]{ "名字"};
第五个参数sortOrder,用于排序。
Uri:
这是用于指向的一个参数,你想访问哪个应用的哪个表,就是通过这个来控制的。
例如,你想访问包名为com.example.test,表名为table的数据。
你可以这么写Uri:content://com.example.test.provider/table。
table后面可以跟id,如果写成Uri:content://com.example.test.provider/table/2则表示访问table表中id为2的单条数据。
Uri中我们有可能用到的通配符有#,*。其中*表示任意长的任意字符,#表示长度不限的数字。因此,#常用于单条数据的访问中。
两种示例写法:
(1)Uri:content://com.example.test.provider/*,表示包名下的所有表;
(2)Uri:content://com.example.test.provider/table/#,表示table表下的所有数据;
以上就是对getContentResolver衍生中的一些方法的解释,另外我们常用的还有insert、delete、update等。这个与以上的解释都是类似的。
对系统和别人提供的provider,还有个地方要注意的,你要加上读取权限:
<uses-permission android:name="android.permission.READ_CONTACTS"/>以上,就是使用provider要注意的地方了。简单的来说,就两句代码:
源码上:
Cursor cursor=getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null,null);权限声明:
<uses-permission android:name="android.permission.READ_CONTACTS"/>
2、使用自定义的ContentProvider
这个的重点主要在于怎么提供ContentProvider。按我们的想法,首先,你要有个ContentProvider类,其次,你要有数据源,再次,你怎么提供数据并保证数据安全?这三个步骤,在我们自己创建的ContentProvider中都能体现出来。
创建自己的ContentProvider类
继承抽象类ContentProvider,并实现方法:package com.example.contentporvider_csdn; import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.net.Uri; public class MyContentPorvider extends ContentProvider{ public static final int DIR=1; public static final int ITEM=2; static String tableString="one"; private static UriMatcher uriMatcher; static { uriMatcher=new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI("com.example.contentporvider_csdn.provider", tableString, DIR); uriMatcher.addURI("com.example.contentporvider_csdn.provider", tableString+"/#", ITEM); } SqliteImp sqliteImp; public MyContentPorvider() { // TODO Auto-generated constructor stub } @Override public boolean onCreate() { // TODO Auto-generated method stub sqliteImp=new SqliteImp(getContext(), tableString, null, 1); return true; } @Override public Cursor query(Uri paramUri, String[] paramArrayOfString1, String paramString1, String[] paramArrayOfString2, String paramString2) { // TODO Auto-generated method stub Cursor cursor=null; switch (uriMatcher.match(paramUri)) { case DIR: cursor=sqliteImp.getWritableDatabase().query(tableString, paramArrayOfString1, paramString1, paramArrayOfString2, null, null, paramString2); break; case ITEM: long id= ContentUris.parseId(paramUri); String whereString="id="+id+" and "+paramString1; cursor=sqliteImp.getWritableDatabase().query(tableString, paramArrayOfString1, whereString, paramArrayOfString2, null, null, paramString2); break; default: break; } return cursor; } @Override public String getType(Uri paramUri) { // TODO Auto-generated method stub String typeString=""; switch (uriMatcher.match(paramUri)) { case DIR: typeString="vnd.android.cursor.dir/vnd.com.example.contentporvider_csdn.provider"; break; case ITEM: typeString="vnd.android.cursor.item/vnd.com.example.contentporvider_csdn.provider"; break; default: break; } return typeString; } @Override public Uri insert(Uri paramUri, ContentValues paramContentValues) { // TODO Auto-generated method stub Uri uri=null; long rowid= sqliteImp.getWritableDatabase().insert(tableString, null, paramContentValues); switch (uriMatcher.match(paramUri)) { case DIR: uri=ContentUris.withAppendedId(paramUri, rowid); break; case ITEM: uri=paramUri; break; default: break; } return uri; } @Override public int delete(Uri paramUri, String paramString, String[] paramArrayOfString) { // TODO Auto-generated method stub int result=0; switch (uriMatcher.match(paramUri)) { case DIR: result= sqliteImp.getWritableDatabase().delete(tableString, paramString, paramArrayOfString); break; case ITEM: long id=ContentUris.parseId(paramUri); paramString="id="+id+" and "+paramString; result= sqliteImp.getWritableDatabase().delete(tableString, paramString, paramArrayOfString); break; default: break; } return result; } @Override public int update(Uri paramUri, ContentValues paramContentValues, String paramString, String[] paramArrayOfString) { // TODO Auto-generated method stub int rowid= 0; switch (uriMatcher.match(paramUri)) { case DIR: rowid= sqliteImp.getWritableDatabase().update(tableString, paramContentValues, paramString, paramArrayOfString); break; case ITEM: long id=ContentUris.parseId(paramUri); paramString="id="+id+" and "+paramString; rowid= sqliteImp.getWritableDatabase().update(tableString, paramContentValues, paramString, paramArrayOfString); break; default: break; } return rowid; } }数据源的提供,我们通过sqlite的数据库来实现。建立一个示意的数据表如下:
package com.example.contentporvider_csdn; import android.content.ContentValues; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteOpenHelper; public class SqliteImp extends SQLiteOpenHelper{ private String createString="create table one (id integer primary key autoincrement," + "name text," + "number text)" + ""; public SqliteImp(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); // TODO Auto-generated constructor stub } @Override public void onCreate(SQLiteDatabase arg0) { // TODO Auto-generated method stub arg0.execSQL(createString); ContentValues contentValues=new ContentValues(); contentValues.put("name", "zhigao"); contentValues.put("number", "15880099999"); arg0.insert("one", null, contentValues); contentValues.clear(); contentValues.put("name", "kongtiao"); contentValues.put("number", "15880077777"); arg0.insert("one", null, contentValues); } @Override public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) { // TODO Auto-generated method stub } }在这里,我们在新建表one的时候,顺便新增了两条数据,用于后续演示。
自定义ContentProvider的注意点
(1)UriMatcher uriMatcher。这是Uri使用中常见的方法,匹配Uri使用。新建实例的方式也比较特别,带一个参数。
uriMatcher=new UriMatcher(UriMatcher.NO_MATCH);
(2)addURI。这是用于将想开放的数据或表,放入到匹配方法中。这是添加规则。
uriMatcher.addURI("com.example.contentporvider_csdn.provider", tableString, DIR); uriMatcher.addURI("com.example.contentporvider_csdn.provider", tableString+"/#", ITEM);(3)uriMatcher.match(paramUri)
这个匹配,就可以得到是DIR,还是ITEM的返回。用于分发处理是否带Id。
(4)数据提供
sqliteImp=new SqliteImp(getContext(), tableString, null, 1);获取我们设置的一些数据。
(5)注册provider。需要将你写的contentProvider注册,使其他的程序能够访问。
<provider android:name="com.example.contentporvider_csdn.MyContentPorvider" android:authorities="com.example.contentporvider_csdn.provider" android:exported="true" ></provider>当其他程序想要使用你自定义的contentporvider时,需要声明权限,如下:
<uses-permission android:name="com.example.contentporvider_csdn.provider"/>
自定义ContentProvider在其他程序中的使用
我们新建一个工程,并打算用这个工程来调用我们自定义的ContentProvider,并实现增删改查功能。package com.example.contentprovidertest_csdn; import java.util.ArrayList; import java.util.List; import android.net.Uri; import android.os.Bundle; import android.R.integer; import android.app.Activity; import android.content.ContentValues; import android.database.Cursor; import android.view.Menu; 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; public class MainActivity extends Activity implements OnClickListener{ ListView listView; List<String> list; ArrayAdapter<String> adapter; Button add,delete,modify,query; Uri uri=Uri.parse("content://com.example.contentporvider_csdn.provider/one"); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } public void initView() { listView=(ListView)findViewById(R.id.mylistview); list=new ArrayList<String>(); adapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, list); listView.setAdapter(adapter); add=(Button)findViewById(R.id.add); delete=(Button)findViewById(R.id.delete); modify=(Button)findViewById(R.id.modify); query=(Button)findViewById(R.id.query); add.setOnClickListener(this); delete.setOnClickListener(this); modify.setOnClickListener(this); query.setOnClickListener(this); } @Override public void onClick(View arg0) { // TODO Auto-generated method stub switch (arg0.getId()) { case R.id.add: ContentValues contentValues=new ContentValues(); contentValues.put("name", "yang_add"); contentValues.put("number", "15880076777"); Uri newuri= getContentResolver().insert(uri, contentValues); Toast.makeText(MainActivity.this, newuri.toString(), 0).show(); break; case R.id.delete: int result= getContentResolver().delete(uri, "name=?", new String[]{"yang_add"}); Toast.makeText(MainActivity.this, result+"", 0).show(); break; case R.id.modify: ContentValues cValues=new ContentValues(); cValues.put("name", "yang_modify"); cValues.put("number", "15880076000"); int result_modify=getContentResolver().update(uri, cValues, "name=?", new String[]{"yang_add"}); Toast.makeText(MainActivity.this, result_modify+"", 0).show(); break; case R.id.query: Cursor cursor= getContentResolver().query(uri, null, null, null, null); if(cursor!=null) { list.clear(); while(cursor.moveToNext()) { String nameString=cursor.getString(cursor.getColumnIndex("name")); String numString=cursor.getString(cursor.getColumnIndex("number")); list.add(nameString+"\n"+numString); } cursor.close(); adapter.notifyDataSetChanged(); } break; default: break; } } }
对应的布局文件:
<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:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:orientation="vertical" tools:context=".MainActivity" > <Button android:id="@+id/add" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="增" /> <Button android:id="@+id/delete" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="删" /> <Button android:id="@+id/modify" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="改" /> <Button android:id="@+id/query" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="查" /> <ListView android:id="@+id/mylistview" android:layout_width="match_parent" android:layout_height="wrap_content" ></ListView> </LinearLayout>
还有一点要注意,权限声明:
<uses-permission android:name="com.example.contentporvider_csdn.provider"/>对于使用ContentProvider,是很简单的一个过程,主要还是在URI这个参数~~。
这样就实现了,自定义ContentProvider,使用自定义ContentProvider。
结束。
源码演示地址:
http://download.csdn.net/detail/yangzhaomuma/9314739
相关文章推荐
- android中volatile和synchronized的应用
- android recovery镜像解包和打包
- Android Studio学习笔记2第一个软件
- 提高数倍工作效率的Android Studio技巧
- 提高数倍工作效率的Android Studio技巧
- Android Studio学习笔记1安装
- Android 关于android:foreground设置无效的问题
- Android 图片信息获取--ExifInterface类
- android studio 各种问题 应该能帮助到你们
- Ultra Pull To Refresh实现知乎下拉刷新风格注意事项
- Android volley全局请求队列和图片加载
- AndroidViewPager监听最后的Item和第一个Item
- Android EditText保留小数点后两位
- android-第一行代码
- Android之Fragment
- android中Parcelable接口的实现
- 关于Android的一些存储
- Android小技巧——利用TimingLogger打印程序的执行时间
- Android 刮刮卡原理
- Android 调出和隐藏软键盘