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

一、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


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值