1. 程式人生 > >Android跨程序資料共享——ContentProvider詳解

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