Android跨进程数据共享——ContentProvider详解

来源:互联网 发布:js对联代码 编辑:程序博客网 时间:2024/05/17 06:24

一、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}@Overridepublic boolean onCreate() {// TODO Auto-generated method stubsqliteImp=new SqliteImp(getContext(), tableString, null, 1);return true;}@Overridepublic Cursor query(Uri paramUri, String[] paramArrayOfString1,String paramString1, String[] paramArrayOfString2,String paramString2) {// TODO Auto-generated method stubCursor 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;}@Overridepublic String getType(Uri paramUri) {// TODO Auto-generated method stubString 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;}@Overridepublic Uri insert(Uri paramUri, ContentValues paramContentValues) {// TODO Auto-generated method stubUri 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;}@Overridepublic int delete(Uri paramUri, String paramString,String[] paramArrayOfString) {// TODO Auto-generated method stubint 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;}@Overridepublic int update(Uri paramUri, ContentValues paramContentValues,String paramString, String[] paramArrayOfString) {// TODO Auto-generated method stubint 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}@Overridepublic void onCreate(SQLiteDatabase arg0) {// TODO Auto-generated method stubarg0.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);}@Overridepublic 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);    }@Overridepublic void onClick(View arg0) {// TODO Auto-generated method stubswitch (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


2 0
原创粉丝点击