您的位置:首页 > 移动开发 > Android开发

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息