Android的IPC機制(五)—— ContentProvider的使用

yangxi_001發表於2017-12-19

綜述

  對於前面一些的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一個。那麼另外一個是誰呢,在下一篇中會進行詳細介紹。

原始碼下載

相關文章