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]
- 所有ContentProvider所提供的URI都是以”content://”開頭。
- 我們創(chuàng)建一個ContentProvider都會為對他指定一個Authority,也對應著URI中的Authority。
- path為資源路徑。它表示所請求的資源的類型。
創(chuàng)建自定義ContentProvider
我們這里只做簡單的步驟介紹,詳細的分析與講解見ContentProvider原理分析
- 使用SQLite技術,創(chuàng)建好數(shù)據(jù)庫和數(shù)據(jù)表
- 新建類繼承ContentProvider
- 重寫6個抽象方法
- 創(chuàng)建UriMatcher,定義Uri規(guī)則
- 在Manifest中注冊provider
- 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。