Android夸進程通信機制六:使用ContentProvider進行進程間通信


Android夸進程通信機制系列:
Android夸進程通信機制一:多進程簡介
Android夸進程通信機制二:Parcel 與 Parcelable
Android夸進程通信機制三:Messenger與Message
Android夸進程通信機制四:使用 Bundle進行進程間通信
Android夸進程通信機制五:使用文件共享進行進程間通信
Android夸進程通信機制六:使用ContentProvider進行進程間通信
Android夸進程通信機制七:使用 Socket進行進程間通信
Android夸進程通信機制八:使用 AIDL進行進程間通信
Android夸進程通信機制九:AIDL深入了解
...


一、前言

之前介紹了幾種進程間通信的方式,都是在同一個應用之間進行進程間通信的,那么,如果在不同應用之間進行程間的通信的,該用什么方式呢,熟悉Android的童鞋,肯定會想到ContentProvider方式。

二、什么是ContentProvider?

ContentProvider作為Android四大組件之一,重要性肯定是不言而喻的,顧名思義,內(nèi)容提供者,其最重要的作用就在于提供了一種跨進程獲取數(shù)據(jù)的方式,provider組件不僅可以自己的進程使用,還可以提供給其他的進程使用,大大方便了不同進程之間的數(shù)據(jù)交換。

ContentProvider為存儲和獲取數(shù)據(jù)提供統(tǒng)一的接口,它可以在不同的應用程序之間共享數(shù)據(jù),本身就是適合進程間通信的。

ContentProvider底層實現(xiàn)也是Binder,但是使用起來比AIDL要容易許多。系統(tǒng)也預制了很多的ContentProvider,例如通訊錄,音視頻等,這些操作本身就是跨進程進行通信。

ContentProvider的用法

ContentProvider的用法其實也很簡單。當我們的兩個應用需要進行數(shù)據(jù)共享的時候,我們就可以利用ContentProvider為所需要共享的數(shù)據(jù)定義一個Uri,然后其他應用通過Context獲得ContentResolver并將數(shù)據(jù)的Uri傳入即可。對于ContentProvider最重要的就是他的數(shù)據(jù)模型(data model)和Uri。

數(shù)據(jù)模型 在ContentProvider中數(shù)據(jù)的存儲也是為所有的共享數(shù)據(jù)創(chuàng)建了一個表。
URI(Uniform Resource Identifier) 是一個用于標識資源名稱的字符串, 這種標識允許我們對任何(包括本地和互聯(lián)網(wǎng))的資源通過特定的協(xié)議進行交互。而每個ContentProvider都會對外提供一個公開的URI來標識自己的數(shù)據(jù)集。當管理多個數(shù)據(jù)集時,將會為每個數(shù)據(jù)集分配一個獨立的URI。

ContentProvider的URI的格式如下

content://[Authority]/[path]

    1. 所有ContentProvider所提供的URI都是以”content://”開頭。
    1. 我們創(chuàng)建一個ContentProvider都會為對他指定一個Authority,也對應著URI中的Authority。
    1. path為資源路徑。它表示所請求的資源的類型。

創(chuàng)建自定義ContentProvider

我們這里只做簡單的步驟介紹,詳細的分析與講解見ContentProvider原理分析

  1. 使用SQLite技術,創(chuàng)建好數(shù)據(jù)庫和數(shù)據(jù)表
  2. 新建類繼承ContentProvider
  3. 重寫6個抽象方法
  4. 創(chuàng)建UriMatcher,定義Uri規(guī)則
  5. 在Manifest中注冊provider
  6. ContentResolver對ContentProvider中共享的數(shù)據(jù)進行增刪改查操作

三、 使用ContentProvider進行進程間通信

理論講了一大堆,關鍵還是要看實操,下面我們舉例進行實操,加深理解。這里我們主要是實現(xiàn)用ContentProvider來進行進程間通信,而非介紹ContentProvider怎么使用,詳細的使用見ContentProvider原理分析。

1. 建立數(shù)據(jù)庫,方便ContentProvider使用

我們創(chuàng)建數(shù)據(jù)庫,并創(chuàng)建表”IPC_Test.db”,里面有兩個字段分別存儲歌曲的名字和歌手的名字。(DbOpenHelper.java)

/**
 * Copyright (C), 2015-2019, 雨紛紛工作室
 * FileName: ucpSqliteOH
 * Author: yufenfen
 * Date: 2016/4/4 10:53 AM
 * Description: 建立數(shù)據(jù)庫,方便ContentProvider使用
 */

package yb.demo.myProcesses.useContentProvider;

import android.content.Context;
import android.database.DatabaseErrorHandler;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

/**
 * @ClassName: ucpSqliteOH
 * @Description: java類作用描述
 * @Author: yufenfen
 * @Date: 2016/4/4 10:53 AM
 */


public class ucpSqliteOH extends SQLiteOpenHelper {
    private static final String DB_NAME="IPC_Test.db";
    private static final String TABLE_NAME="IPC_Test";

    private static final int DB_VERSION=1;

    private String CREATE_GAME_TABLE="create table if not exists " + TABLE_NAME +"(_id integer primary key," + "song TEXT, "+"singer TEXT)";

    public ucpSqliteOH( Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_GAME_TABLE);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

2. 使用ContentProvider對數(shù)據(jù)庫進行操作

在initProvoder方法中,我們開啟線程來對數(shù)據(jù)庫進行操作,刪除表的所有數(shù)據(jù),再添加數(shù)據(jù),并實現(xiàn)了曾刪改查方法。

/**
 * @ClassName: MusicProvider
 * @Description: java類作用描述
 * @Author: yufenfen
 * @Date: 2016/4/4 11:11 AM
 */
public class MusicProvider extends ContentProvider {
    //這里的AUTHORITY就是我們在AndroidManifest.xml中配置的authorities
    public static final String AUTHORITY = "yb.demo.myProcesses.useContentProvider.MusicProvider";
    
    public static final String MUSIC_PROVIDER_PATH = "music";
    public static final Uri GAME_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + MUSIC_PROVIDER_PATH);
    private static final UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    private SQLiteDatabase mDb;
    private Context mContext;
    private String table;

    static {
        mUriMatcher.addURI(AUTHORITY, MUSIC_PROVIDER_PATH, 0);
    }

    @Override
    public boolean onCreate() {
        table = UcpSqliteOH.TABLE_NAME;
        mContext = getContext();
        initProvoder();

        return false;
    }

    private void initProvoder() {
        mDb = new UcpSqliteOH(mContext).getWritableDatabase();
        new Thread(new Runnable() {
            @Override
            public void run() {
                mDb.execSQL("delete from " + UcpSqliteOH.TABLE_NAME);
                mDb.execSQL("insert into "+ UcpSqliteOH.TABLE_NAME +" values(1,'七里香','周杰倫');");
            }
        }).start();
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        Cursor mCursor = mDb.query(table, projection, selection, selectionArgs, null, sortOrder, null);
        return mCursor;
    }

    @Override
    public String getType(Uri uri) {
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        mDb.insert(table, null, values);

        mContext.getContentResolver().notifyChange(uri, null);

        return null;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        mDb.delete(table, selection, selectionArgs);

        mContext.getContentResolver().notifyChange(uri, null);

        return 0;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        mDb.update(table, values, selection, selectionArgs);

        mContext.getContentResolver().notifyChange(uri, null);

        return 0;
    }
}

3. 在manifest中,聲明ContentProvider

在manifest文件中,我們要讓ContentProvider運行在另一個進程,

        <provider android:name="yb.demo.myProcesses.useContentProvider.MusicProvider"
            android:authorities="yb.demo.myProcesses.useContentProvider.MusicProvider"
            android:process="yb.demo.myProcesses.musicService" />

這里要注意authorities屬性的值要與MusicProvider的AUTHORITY的值保持一致

4. 進程間調(diào)用MusicProvider的方法

在MusicActivity中插入一條數(shù)據(jù)到另一個進程的MusicProvider。

/**
 * 作者:created by yufenfen on 2019/3/28:18:16
 * 郵箱: ybyj1314@126.com
 */
public class MusicActivity extends Activity {
    ...
    ...
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.myprocesses_activity_layout);

        testContentProvider();
    }
    /*******************************************************/
    /************************   Bundle   *******************/
    /*******************************************************/

    /**
     *
     */
    private void testContentProvider(){
        Uri uri = MusicProvider.GAME_CONTENT_URI;
        ContentValues mContentValues = new ContentValues();
        mContentValues.put("song", "煙花易冷");
        mContentValues.put("singer", "周杰倫");
        getContentResolver().insert(uri, mContentValues);

        Cursor musicCursor = getContentResolver().query(uri, new String[]{"song", "singer"}, null, null, null);
        while (musicCursor.moveToNext()) {
            String song = musicCursor.getString(0);
            String singer = musicCursor.getString(1);

            Log.i(TAG,  "記錄 歌曲:" + song + " ,歌手:" + singer);
        }
    }
    ...
    ...
    ...
}

OK,進過意思幾步,我們的程序就可以跑起來了,如無意外,你將會在Run欄里看到以下信息

I/MusicActivity: 請求插入數(shù)據(jù)
I/MusicActivity: 記錄 歌曲:七里香 ,歌手:周杰倫
    記錄 歌曲:煙花易冷 ,歌手:周杰倫

四、 結語

ContentProvider是Android中提供的專門用于不同應用間進行數(shù)據(jù)共享的方式,從這一點來看,它天生就適合進程間通信。
和Messenger一樣,ContentProvider的底層實現(xiàn)同樣也是Binder,由此可見,Binder在Android系統(tǒng)中是何等的重要,后面會重點研究Binder。

雖然ContentProvider的底層實現(xiàn)是Binder,但是它的使用過程要比AIDL簡單許多,這是因為系統(tǒng)已經(jīng)為我們做了封裝,使得我們無須關心底層細節(jié)即可輕松實現(xiàn)IPC。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容