这篇“Android怎么使用ContentProvider实现跨进程通讯”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Android怎么使用ContentProvider实现跨进程通讯”文章吧。
1 前言
ContentProvider 即内容提供器,是 Android 四大组件之一,为 App 存取数据提供统一的对外接口,让不同的应用之间可以共享数据。
如图,Server 端通过 ContentProvider 对外提供操作本地数据(DataBase、File 等)的接口,Client 端通过 ContentResolver 与 ContentProvider 通讯,从而实现跨进程操作 Server 端数据,Observer 端通过 ContentObserver 监听 Server 端的数据是否发生变化,并触发相关操作。
(1) ContentProvider 接口
ContentProvider 是一个抽象类,用户需要实现如下抽象方法。
// 创建数据库并获得数据库连接public abstract boolean onCreate()// 查询数据public abstract Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)// 插入数据public abstract Uri insert(Uri uri, ContentValues values)// 删除数据public abstract int delete(Uri uri, String selection, String[] selectionArgs)// 更新数据public abstract int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)// 获取数据类型(MIME类型,如:"text/html"、"image/png"、"message/rfc882"、"vnd.android-dir/mms-sms")public abstract String getType(Uri uri)
参数说明:
uri:数据表路径
projection:需要查询的字段名称
selection:查询条件
selectionArgs:查询条件中的参数列表
sortOrder:排序
(2)ContentResolver 接口
// 查询数据public final Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)// 插入数据public final Uri insert(Uri url, ContentValues values)// 删除数据public final int delete(Uri url, String selection, String[] selectionArgs)// 更新数据public final int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
可以看到,ContentProvider 和 ContentResolver 都有增删改查操作,并且参数列表完全一致,以实现对 Server 端数据的操作。
(3)ContentObserver 接口
ContentObserver 是一个抽象类,用户需要重写其构造方法和 onChange() 方法。
//用户需要重写构造方法,并注入 handlerpublic ContentObserver(Handler handler)//监听的内容发生变化时调用public void onChange(boolean selfChange)//注册 ContentObservermContext.getContentResolver().registerContentObserver(uri, false, observer);//注销 ContentObservermContext.getContentResolver().unregisterContentObserver(observer);
2 URI 简介
URI 即统一资源标识符(Uniform Resource Identifier),能够唯一标识资源,如同资源的身份ID。数据库的 URI 在 ContentProvider 中定义,ContentResolver 通过资源 URI 找到对应的 ContentProvider,从而操作数据库。URI 类图如下。
URI 由3部分组成:scheme、authority、path,其中 authority 又由 host、port 组成,URI 一般格式如下:
scheme://authority/path?queryscheme://host:port/path?query
URI 类的常用接口如下:
//根据 uriString 生成 StringUri 对象(Uri的子类,重写了Uri的抽象方法)public static Uri parse(String uriString)//根据 fiel 生成 HierarchicalUri 对象(Uri的子类,重写了Uri的抽象方法)public static Uri fromFile(File file)//根据 scheme、ssp、fragment 生成 OpaqueUri 对象(Uri的子类,重写了Uri的抽象方法)public static Uri fromParts(String scheme, String ssp, String fragment)public abstract String getScheme()public abstract String getHost()public abstract int getPort()public abstract String getPath()public abstract String getQuery()
常见的 URI 实例如下:
//网络资源https://blog.csdn.net/m0_37602827//本地资源file:///sdcard/Pictures/WeiXin/a.mp4//ContentProvidercontent://com.zhyan8.content.MyProvider/query//打电话tel:10086//发短信smsto:10086//发邮件mailto:xxxx@qq.com//定位geo:52.76,-79.0342
3 项目结构
本文将以一个案例讲解使用 ContentProvider 实现跨进程通讯,项目结构如下,一共包含 3 个 Module。
content_s:服务端,数据持有者,对外提供 ContentProvider
content_c:客户端,数据操作者,通过 ContentResolver 操作服务端数据
content_o:数据监听者,通过 ContentObserver 监听服务端数据的变化
4 服务端(Content_S)
(1)数据库操作类
DBHelper.java
package com.zhyan8.content_s;import android.content.ContentValues;import android.content.Context;import android.database.Cursor;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteOpenHelper;public class DBHelper extends SQLiteOpenHelper { private static final String DATABASE = "test.db"; private static final String TABLE = "user"; public DBHelper(Context context) { super(context, DATABASE, null, 1); } @Override public void onCreate(SQLiteDatabase db) { String create_table = "create table " + TABLE + "(id int primary key, name varchar not null)"; db.execSQL(create_table); db.execSQL("insert into " + TABLE + " values(1001, '张三'),(1002, '李四')"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {} public Cursor query() { SQLiteDatabase db = getReadableDatabase(); String sql = "select * from " + TABLE; Cursor cursor = db.rawQuery(sql, null); return cursor; } public long insert(ContentValues cv) { SQLiteDatabase db = getWritableDatabase(); long result = db.insert(TABLE, null, cv); db.close(); return result; }}
生成的数据库如下:
(2)自定义 ContentProvider
MyProvider.java
package com.zhyan8.content_s;import android.content.ContentProvider;import android.content.ContentValues;import android.content.UriMatcher;import android.database.Cursor;import android.net.Uri;import android.util.Log;public class MyProvider extends ContentProvider { private DBHelper mUserDBHelper; private static UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); //用于匹配URI,并返回对应的操作编码 private static final String AUTHORITES = "com.zhyan8.content.MyProvider"; private static final int QUERY = 1; //查询操作编码 private static final int INSERT = 2; //插入操作编码 static { //添加有效的 URI 及其编码 sUriMatcher.addURI(AUTHORITES, "/query", QUERY); sUriMatcher.addURI(AUTHORITES, "/insert", INSERT); } @Override public boolean onCreate() { mUserDBHelper = new DBHelper(getContext()); return false; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { Log.e("xxx-Provider", "query: "); int code = sUriMatcher.match(uri); if (code == QUERY) { return mUserDBHelper.query(); } return null; } @Override public Uri insert(Uri uri, ContentValues values) { Log.e("xxx-Provider", "insert: "); int code = sUriMatcher.match(uri); if (code == INSERT) { mUserDBHelper.insert(values); } getContext().getContentResolver().notifyChange(uri, null); //通知外界,数据发生变化 return null; } @Override public String getType(Uri uri) { //获取资源 MIME return null; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { return 0; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; }}
(3)配置
在 AndroidManifest.xml 的 application 节点下配置 ContentProvider,如下。
<provider android:name=".MyProvider" android:authorities="com.zhyan8.content.MyProvider" android:exported="true" />
exported 属性用于定义是否允许外界访问。
(4)主类
MainActivity.java
package com.zhyan8.content_s;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }}
5 客户端(Content_C)
(1)数据类
User.java
package com.zhyan8.content_c;public class User { private Integer id; private String name; public User(){}; public User(Integer id, String name) { this.id = id; this.name = name; } public void setId(Integer id) { this.id = id; } public void setName(String name) { this.name = name; } public Integer getId() { return id; } public String getName() { return name; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; }}
(2)自定义 ContentResolver
MyResolver.java
package com.zhyan8.content_c;import android.content.ContentResolver;import android.content.ContentValues;import android.content.Context;import android.database.ContentObserver;import android.database.Cursor;import android.net.Uri;import java.util.ArrayList;public class MyResolver { private ContentResolver mContentResolver; private static final String AUTHORITES = "com.zhyan8.content.MyProvider"; public MyResolver(Context context) { mContentResolver = context.getContentResolver(); } public ArrayList<User> query() { ArrayList<User> list = new ArrayList<>(); Uri uri = Uri.parse("content://" + AUTHORITES + "/query"); Cursor cursor = mContentResolver.query(uri, null, null, null, null); while (cursor.moveToNext()) { User user = new User(); user.setId(cursor.getInt(0)); user.setName(cursor.getString(1)); list.add(user); } cursor.close(); return list; } public void insert(User user) { Uri uri = Uri.parse("content://" + AUTHORITES + "/insert"); ContentValues cv = new ContentValues(); cv.put("id", user.getId()); cv.put("name", user.getName()); mContentResolver.insert(uri, cv); }}
(3)主类
MainActivity.java
package com.zhyan8.content_c;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.util.Log;import android.view.View;import java.util.ArrayList;public class MainActivity extends AppCompatActivity { private MyResolver myResolver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myResolver = new MyResolver(this); } public void clickQuery(View v) { ArrayList<User> users = myResolver.query(); Log.e("xxx", "clickQuery: " + users.toString()); } public void clickInsert(View v) { User user = new User(1003, "王五"); myResolver.insert(user); }}
(4)布局
activity_main.xml
<?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" tools:context=".MainActivity" android:orientation="vertical"> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="查询" android:textSize="40sp" android:layout_marginTop="50dp" android:onClick="clickQuery"/> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="添加" android:textSize="40sp" android:layout_marginTop="50dp" android:onClick="clickInsert"/></LinearLayout>
界面如下:
5 监听者(Content_O)
(1)自定义 ContentObserver
MyObserver.java
package com.zhyan8.content_o;import android.database.ContentObserver;import android.os.Handler;public class MyObserver extends ContentObserver { private Uri mUri = Uri.parse("content://com.zhyan8.content.MyProvider/insert"); private ContentResolver mResolver; Handler mHandler; public MyObserver(Context context, Handler handler) { super(handler); mResolver = context.getContentResolver(); mHandler = handler; } @Override public void onChange(boolean selfChange) { mHandler.sendEmptyMessage(0x111); } public void registerObserver() { mResolver.registerContentObserver(mUri, false, this); } public void unregisterObserver() { mResolver.unregisterContentObserver(this); }}
(2)主类
MainActivity.java
package com.zhyan8.content_o;import android.net.Uri;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.support.v7.app.AppCompatActivity;import android.util.Log;public class MainActivity extends AppCompatActivity { private MyObserver mObserver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mObserver = new MyObserver(mHandler); mObserver.registerObserver(); } private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what==0x111) { Log.e("xxx", "监听的数据改变了"); } } }; @Override public void onDestroy() { mObserver.unregisterObserver(); super.onDestroy(); }}
6 效果展示
首先保证 Content_S、Content_C 和 Content_O 3个应用都打开了,再在 Content_C 端依次点击【查询】→【插入】→【查询】,打印日志如下:
2020-11-22 22:47:41.397 5227-5597/com.zhyan8.content_s E/xxx-Provider: query:
2020-11-22 22:47:41.401 6261-6261/com.zhyan8.content_c E/xxx: clickQuery: [User{id=1001, name='张三'}, User{id=1002, name='李四'}]
2020-11-22 22:47:49.848 5227-5493/com.zhyan8.content_s E/xxx-Provider: insert:
2020-11-22 22:47:49.855 7879-7879/com.zhyan8.content_o E/xxx: 监听的数据改变了
2020-11-22 22:47:52.055 5227-5597/com.zhyan8.content_s E/xxx-Provider: query:
2020-11-22 22:47:52.063 6261-6261/com.zhyan8.content_c E/xxx: clickQuery: [User{id=1001, name='张三'}, User{id=1002, name='李四'}, User{id=1003, name='王五'}]
以上就是关于“Android怎么使用ContentProvider实现跨进程通讯”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注编程网行业资讯频道。