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]
-
- 所有ContentProvider所提供的URI都是以”content://”开头。
-
- 我们创建一个ContentProvider都会为对他指定一个Authority,也对应着URI中的Authority。
-
- path为资源路径。它表示所请求的资源的类型。
创建自定义ContentProvider
我们这里只做简单的步骤介绍,详细的分析与讲解见ContentProvider原理分析
- 使用SQLite技术,创建好数据库和数据表
- 新建类继承ContentProvider
- 重写6个抽象方法
- 创建UriMatcher,定义Uri规则
- 在Manifest中注册provider
- 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运行在另一个进程,
这里要注意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。
更多相关文章
- Android夸进程通信机制八:使用 AIDL进行进程间通信
- android 进程与线程 - 开发文档翻译 - 进程
- Android(安卓)面试必备 - 系统、App、Activity 启动过程
- Android一日游
- 传智播客—Android(二)数据存储和访问 之文件
- Android夸进程通信机制五:使用文件共享进行进程间通信
- Android保存数据几种常用方法解析
- Android平台简介
- Android一日游