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四大组件之一,重要性肯定是不言而喻的,顾名思义,内容提供者,其最重要的作用就在于提供了一种跨进程获取数据的方式,provider组件不仅可以自己的进程使用,还可以提供给其他的进程使用,大大方便了不同进程之间的数据交换。

ContentProvider为存储和获取数据提供统一的接口,它可以在不同的应用程序之间共享数据,本身就是适合进程间通信的。

ContentProvider底层实现也是Binder,但是使用起来比AIDL要容易许多。系统也预制了很多的ContentProvider,例如通讯录,音视频等,这些操作本身就是跨进程进行通信。

ContentProvider的用法

ContentProvider的用法其实也很简单。当我们的两个应用需要进行数据共享的时候,我们就可以利用ContentProvider为所需要共享的数据定义一个Uri,然后其他应用通过Context获得ContentResolver并将数据的Uri传入即可。对于ContentProvider最重要的就是他的数据模型(data model)和Uri。

数据模型 在ContentProvider中数据的存储也是为所有的共享数据创建了一个表。
URI(Uniform Resource Identifier) 是一个用于标识资源名称的字符串, 这种标识允许我们对任何(包括本地和互联网)的资源通过特定的协议进行交互。而每个ContentProvider都会对外提供一个公开的URI来标识自己的数据集。当管理多个数据集时,将会为每个数据集分配一个独立的URI。

ContentProvider的URI的格式如下

content://[Authority]/[path]

    1. 所有ContentProvider所提供的URI都是以”content://”开头。
    1. 我们创建一个ContentProvider都会为对他指定一个Authority,也对应着URI中的Authority。
    1. path为资源路径。它表示所请求的资源的类型。

创建自定义ContentProvider

我们这里只做简单的步骤介绍,详细的分析与讲解见ContentProvider原理分析

  1. 使用SQLite技术,创建好数据库和数据表
  2. 新建类继承ContentProvider
  3. 重写6个抽象方法
  4. 创建UriMatcher,定义Uri规则
  5. 在Manifest中注册provider
  6. ContentResolver对ContentProvider中共享的数据进行增删改查操作

三、 使用ContentProvider进行进程间通信

理论讲了一大堆,关键还是要看实操,下面我们举例进行实操,加深理解。这里我们主要是实现用ContentProvider来进行进程间通信,而非介绍ContentProvider怎么使用,详细的使用见ContentProvider原理分析

1. 建立数据库,方便ContentProvider使用

我们创建数据库,并创建表”IPC_Test.db”,里面有两个字段分别存储歌曲的名字和歌手的名字。(DbOpenHelper.java)

/**
 * Copyright (C), 2015-2019, 雨纷纷工作室
 * FileName: ucpSqliteOH
 * Author: yufenfen
 * Date: 2016/4/4 10:53 AM
 * Description: 建立数据库,方便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对数据库进行操作

在initProvoder方法中,我们开启线程来对数据库进行操作,删除表的所有数据,再添加数据,并实现了曾删改查方法。

/**
 * @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. 进程间调用MusicProvider的方法

在MusicActivity中插入一条数据到另一个进程的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: 请求插入数据
I/MusicActivity: 记录 歌曲:七里香 ,歌手:周杰伦
    记录 歌曲:烟花易冷 ,歌手:周杰伦

四、 结语

ContentProvider是Android中提供的专门用于不同应用间进行数据共享的方式,从这一点来看,它天生就适合进程间通信。
和Messenger一样,ContentProvider的底层实现同样也是Binder,由此可见,Binder在Android系统中是何等的重要,后面会重点研究Binder。

虽然ContentProvider的底层实现是Binder,但是它的使用过程要比AIDL简单许多,这是因为系统已经为我们做了封装,使得我们无须关心底层细节即可轻松实现IPC。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,513评论 4 369
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,312评论 1 305
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,124评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,529评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,937评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,913评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,084评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,816评论 0 205
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,593评论 1 249
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,788评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,267评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,601评论 3 261
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,265评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,158评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,953评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,066评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,852评论 2 277

推荐阅读更多精彩内容