Android的IPC機制(五)—— ContentProvider的使用
綜述
對於前面一些的ipc過程都是Service與客戶端進行通訊。那麼在不同應用之間ipc可以採用哪些方式呢?首先我們會想到ContentProvider,因為我們平時獲取手機上的聯絡人,圖片等等都是通過ContentProvider得到的。ContentProvider是Android的四大元件之一。翻譯成中文為內容提供者,也就是可以將自己的資料提供給別的應用進行使用。那麼我們現在就來看一下ContentProvider的使用方法。
ContentProvider的用法
ContentProvider的用法其實也很簡單。當我們的兩個應用需要進行資料共享的時候,我們就可以利用ContentProvider為所需要共享的資料定義一個Uri,然後其他應用通過Context獲得ContentResolver並將資料的Uri傳入即可。對於ContentProvider最重要的就是他的資料模型(data model)和Uri。那麼我們現在就先看一下他的資料模型和這個URI是什麼。
資料模型
在ContentProvider中資料的儲存也是為所有的共享資料建立了一個表。比如說我們的一個應用對其他應用提供了員工資訊。那麼下面這張表就體現出了ContentProvider中的資料在表中的儲存情況。
id | woekNum | name | department |
---|---|---|---|
1 | 1001 | 張三 | 銷售部 |
2 | 1002 | 李四 | 人事部 |
3 | 1003 | 王五 | 研發部 |
4 | 1004 | 小明 | 研發部 |
5 | 1005 | 小強 | 銷售部 |
在這個表中,id為主鍵,workNum,name,department分別對應著員工的工號,姓名,部門。每一行表示一條記錄,對應著一個例項,每一列代表著某個資料。不過有一點要注意,ContentProvider中的主鍵不是必需的,並且它不需要使用ID作為一個主鍵的列名。
URI
URI的全稱為Uniform Resource Identifier(統一資源識別符號)。是一個用於標識資源名稱的字串。 這種標識允許我們對任何(包括本地和網際網路)的資源通過特定的協議進行互動。而每個ContentProvider都會對外提供一個公開的URI來標識自己的資料集。當管理多個資料集時,將會為每個資料集分配一個獨立的URI。對於ContentProvider所提供的URI的格式如下。
在這裡說明一下:
1. 所有ContentProvider所提供的URI都是以”content://”開頭。
2. 我們建立一個ContentProvider都會為對他指定一個Authority,也對應著URI中的Authority。
3. path為資源路徑。它表示所請求的資源的型別。
使用案例
那麼現在我們用一個例子來說明一下這個ContentProvider是如何使用的。在這裡我們使用兩個應用。第一個應用通過使用ContentProvider對外提供員工資訊。而第二個應用來獲取這個員工資訊。
那麼現在我們首先看一下第一個應用程式。在這裡我們建立一個SQLite資料庫,將員工資訊存入資料庫中。
我們首先簡單的建立一個Sqlite資料庫。
package com.example.ljd.employee;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class EmployeeDBHelper extends SQLiteOpenHelper {
private final static String DB_NAME = "myDatabase.db";
private final static int DB_VERSION = 1;
public static final String EMPLOYEE_TABLE_NAME = "employee";
private String CREATE_BOOK_TABLE = "CREATE TABLE IF NOT EXISTS "
+ EMPLOYEE_TABLE_NAME + " (id INTEGER PRIMARY KEY,"
+"workNum TEXT,"+ "name TEXT,"+"department TEXT)";
public EmployeeDBHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
然後我們建立一個EmployeeProvider類,這個繼承ContentProvider。我們看一下這個EmployeeProvider。
package com.example.ljd.employee;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.support.annotation.Nullable;
public class EmployeeProvider extends ContentProvider {
private SQLiteDatabase mDb;
private static final String AUTHORITY = "com.example.ljd.employee.EmployeeProvider";
private static final int EMPLOYEE_URI_CODE = 0;
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
sUriMatcher.addURI(AUTHORITY, "employee", EMPLOYEE_URI_CODE);
}
@Override
public boolean onCreate() {
insertDataToDb();
return true;
}
@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
String tableName = getTableName(uri);
if (tableName == null) {
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
Cursor cursor = mDb.query(tableName, projection, selection, selectionArgs, null, null, sortOrder, null);
return cursor;
}
@Nullable
@Override
public String getType(Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(Uri uri, ContentValues values) {
String table = getTableName(uri);
if (table == null) {
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
mDb.insert(table, null, values);
getContext().getContentResolver().notifyChange(uri, null);
return uri;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
String table = getTableName(uri);
if (table == null) {
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
int count = mDb.delete(table, selection, selectionArgs);
if (count > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
String table = getTableName(uri);
if (table == null) {
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
int row = mDb.update(table, values, selection, selectionArgs);
if (row > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return row;
}
private void insertDataToDb() {
mDb = new EmployeeDBHelper(getContext()).getWritableDatabase();
mDb.execSQL("delete from " + EmployeeDBHelper.EMPLOYEE_TABLE_NAME);
mDb.execSQL("insert into employee values(1,'1001','張三','銷售部');");
mDb.execSQL("insert into employee values(2,'1002','李四','人事部');");
mDb.execSQL("insert into employee values(3,'1003','王五','研發部');");
mDb.execSQL("insert into employee values(4,'1004','小明','研發部');");
mDb.execSQL("insert into employee values(5,'1005','小強','銷售部');");
}
private String getTableName(Uri uri) {
String tableName = null;
if (sUriMatcher.match(uri) == EMPLOYEE_URI_CODE){
tableName = EmployeeDBHelper.EMPLOYEE_TABLE_NAME;
}
return tableName;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
在這裡我們實現了ContentProvider的六個抽象方法。在onCreate方法中,用於建立一個ContentProvider,在這裡我們對資料庫進行初始化操作。而getType方法中用來返回Uri請求所對應的MIME型別。而後剩下的四個方法中也就是對應的”CRUD”操作,也就是資料的增刪改查功能。
然後我們還需要在AndroidManifest中註冊這個ContentProvider。
<provider
android:name=".EmployeeProvider"
android:authorities="com.example.ljd.employee.EmployeeProvider"
android:exported="true"
android:permission="com.example.ljd.employee.provider" />
- 1
- 2
- 3
- 4
- 5
authorities: 這個屬性是ContentProvider的唯一標識,通過過這個屬性外部應用才能訪問我們的ContentProvider,也就是說authorities是唯一的。而且這個authorities也是我們Uri裡面的authorities。
export: 設為true的時候表示當前ContentProvider可以被其它應用使用。任何應用可以使用Provider通過URI 來獲得它,也可以通過相應的許可權來使用Provider。設為false的時候表示當前ContentProvider不能被其它應用使用。
permission: 為我們的ContentProvider設定許可權從而避免任何應用都能訪問我們的ContentProvider。
所以現在我們還需要為我們的應用設定許可權。
<permission android:name="com.example.ljd.employee.provider"
android:protectionLevel="normal"/>
- 1
- 2
對於這個應用程式我們只需要在建立一個空白的Activity啟動即可,在這個Activity裡面不需要做任何的操作。然後我們現在看一下第二個應用是如何獲取ContentProvider中資料的。
首先我們需要建立一個Employee實體類。
package com.example.ljd.client;
public class Employee {
private int id;
private String workNum;
private String name;
private String department;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getWorkNum() {
return workNum;
}
public void setWorkNum(String workNum) {
this.workNum = workNum;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
@Override
public String toString(){
return String.format("workNum:%s, name:%s, department:%s",
getWorkNum(),getName(),getDepartment());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
然後我們在看一下是如何ContentProvider中的資料進行操控的。
package com.example.ljd.client;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
public class HandleProvider {
private static Context mContext;
private static HandleProvider mInstance;
private static Uri mEmployeeUri;
private static final String EMPLOYEE_CONTENT_URI = "content://com.example.ljd.employee.EmployeeProvider/employee";
private HandleProvider(Context mContext) {
this.mContext = mContext.getApplicationContext();
mEmployeeUri = Uri.parse(EMPLOYEE_CONTENT_URI);
}
public static HandleProvider getInstance(Context context) {
if (mInstance == null) {
synchronized (HandleProvider.class) {
if (mInstance == null) {
mInstance = new HandleProvider(context);
}
}
}
return mInstance;
}
public void delete() {
ContentResolver contentResolver = mContext.getContentResolver();
String where = "id=?";
String[] where_args = {"7"};
contentResolver.delete(mEmployeeUri, where, where_args);
}
public void update() {
ContentResolver contentResolver = mContext.getContentResolver();
ContentValues values = new ContentValues();
values.put("name", "梁山伯");
String where = "id=?";
String[] where_args = {"1"};
contentResolver.update(mEmployeeUri, values, where, where_args);
}
public void create() {
ContentValues values = new ContentValues();
values.put("id", 7);
values.put("workNum", "1006");
values.put("name", "張三丰");
values.put("department", "研發部");
mContext.getContentResolver().insert(mEmployeeUri, values);
}
public List<String> query() {
List<String> list = new ArrayList<>();
Cursor cursor = mContext.getContentResolver().query(mEmployeeUri, new String[]{"id", "workNum", "name", "department"}, null, null, null);
while (cursor.moveToNext()) {
Employee employee = new Employee();
employee.setId(cursor.getInt(0));
employee.setWorkNum(cursor.getString(1));
employee.setName(cursor.getString(2));
employee.setDepartment(cursor.getString(3));
String str = employee.toString();
list.add(str);
Log.d("mainActivity", "query employee:" + str);
}
cursor.close();
return list;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
我們只需要在Activity使用這些方法即可。
package com.example.ljd.client;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.List;
import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class MainActivity extends AppCompatActivity {
@Bind(R.id.show_linear)
LinearLayout showLinear;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
ButterKnife.unbind(this);
}
@OnClick({R.id.insert_button,R.id.delete_button,R.id.update_button,R.id.query_button,R.id.clear_button})
public void onClickButton(View view){
switch (view.getId()){
case R.id.insert_button:
HandleProvider.getInstance(this).create();
break;
case R.id.delete_button:
HandleProvider.getInstance(this).delete();
break;
case R.id.update_button:
HandleProvider.getInstance(this).update();
break;
case R.id.query_button:
List<String> list = HandleProvider.getInstance(this).query();
for (int i = 0;i<list.size();i++){
TextView textView = new TextView(this);
textView.setText(list.get(i));
showLinear.addView(textView);
}
TextView lineText = new TextView(this);
lineText.setText("-----------------------");
showLinear.addView(lineText);
break;
case R.id.clear_button:
showLinear.removeAllViews();
break;
default:
break;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
由於在ContentProvider中新增了許可權,所以要為我們這個應用新增使用許可權。
<uses-permission android:name="com.example.ljd.employee.provider"/>
- 1
最後在看一下我們的佈局程式碼。
<?xml version="1.0" encoding="utf-8"?>
<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:orientation="vertical"
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="com.example.ljd.client.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/insert_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="@string/insert"/>
<Button
android:id="@+id/delete_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="@string/delete"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/update_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/update"/>
<Button
android:id="@+id/query_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/query"/>
</LinearLayout>
<Button
android:id="@+id/clear_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/clear"/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/show_linear"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
</LinearLayout>
</ScrollView>
</LinearLayout>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
演示
在這裡我們看一下效果演示。
插入:我們將一個名叫張三丰的員工通過ContentProvider插入到資料庫中。
刪除:我們將上面插入的張三丰這個員工刪除。
更新:我們將張三改名為梁山伯。
總結
在這裡我們介紹了ContentProvider的使用方法,而在Android系統中為我們提供ContentProvider元件本身就是用來跨程式訪問資料的。其實在Android應用的四大元件當中可以用於程式間通訊的不止ContentProvider一個。那麼另外一個是誰呢,在下一篇中會進行詳細介紹。
原始碼下載
相關文章
- Android IPC機制(三):淺談Binder的使用Android
- Android IPC 機制分析Android
- 詳解 Android 中的 IPC 機制:基礎篇Android
- 02.Android之IPC機制問題Android
- ContentProvider的基本使用IDE
- Android包管理機制(五)APK是如何被解析的AndroidAPK
- 學習筆記(2)IPC機制筆記
- Android ContentProvider 啟動分析AndroidIDE
- Android ContentProvider 基本原理和使用詳解AndroidIDE
- Android使用ContentProvider初始化SDK庫方案總結AndroidIDE
- contentProvider的用法IDE
- Binder通訊機制與IPC通訊.md
- Android IPC 之AIDLAndroidAI
- 真香!為什麼Android要採用Binder作為IPC機制?全套教學資料Android
- android的TouchEvent派發機制的分析Android
- Android的Handler訊息機制 解析Android
- 圖解Android中的binder機制圖解Android
- 全新的android外掛機制 - DroidPluginAndroidPlugin
- Android 事件分發機制的理解Android事件
- Android的MotionEvent事件分發機制Android事件
- Android系統原始碼分析之-ContentProviderAndroid原始碼IDE
- Android 四大元件之 ContentProviderAndroid元件IDE
- Android四大元件之ContentProviderAndroid元件IDE
- Android必知必會的四大元件 -- ContentProvider篇Android元件IDE
- 藉助 AIDL 理解 Android Binder 機制——AIDL 的使用和原理分析AIAndroid
- Android事件分發機制五:面試官你坐啊Android事件面試
- Android Classloader機制Android
- Android NestedScrolling機制Android
- 【Android基礎】講講Android的事件分發機制Android事件
- Android 的 so 檔案載入機制Android
- Android高階進階之路【五】深入剖析Android系統Binder通訊機制Android
- Android四大元件之ContentProvider篇Android元件IDE
- Android Handler機制理解和AsyncTask使用小記Android
- 【朝花夕拾】Android自定義View篇之(五)Android事件分發及傳遞機制AndroidView事件
- Android中觸控事件的傳遞機制Android事件
- 淺談Android中的事件分發機制Android事件
- Android簽名機制Android
- Android包管理機制Android
- android中反射機制Android反射