Android跨程序資料共享——ContentProvider詳解
阿新 • • 發佈:2019-01-28
一、ContentProvider介紹
作為android四大元件之一,ContentProvider可能是四大元件中我們用到最少的。
它作為跨程序資料共享來使用,而我們開發app的時候,基本上是獨立的,不會與其他的app發生資料間的通訊。
但如果兩個或者多個app需要共享一個數據源的時候,ContentProvider就顯的非常必要且高安全性,因為我們可以控制資料來源的哪些資料可以被訪問,哪些不能被訪問。
二、ContentProvider的使用
ContentProvider的使用歸結有兩種:使用別人(系統)提供的ContentProvider、使用自定義的ContentProvider。 在使用ContentProvider的過程中有些東西是我們要注意的:注意點:
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