【Android开发艺术】ContentProvider进程间通信
前言:
已经开始考虑转Java后端,但是觉得Android毕竟是一门手艺,干脆把书读读然后消化掉。好像一个人多项技能,后端刚学,肯定没法以此作为高薪的突破口。
如果遇到机会,还可以用Android为主,后端为辅,打个漂亮的加分赛。毕竟既懂前端又懂后端还是听起来很厉害的。
也能达到一些触类旁通的效果。
正文:
ContentProvider底层也是Binder。所以用来进行IPC进程间通信非常适合。平时日常编码中很少用到ContentProvider,可能与我是写普通应用的缘故。
源码提取地点
温故:
一、 服务端App:
写一个简单的ContentProvider测试demo,用来提供数据。(虽然本例,现在什么也没提供)
public class BookProvider extends ContentProvider {
private static final String TAG = "BookProvider";
public BookProvider() {
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
Log.e(TAG,"delete,current Thread:Thread.currentThread());
return 0;
}
@Override
public String getType(Uri uri) {
Log.e(TAG,"getType,current Thread:Thread.currentThread());
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
Log.e(TAG,"insert,current Thread:Thread.currentThread());
return null;
}
@Override
public boolean onCreate() {
Log.e(TAG,"onCreate,current Thread:Thread.currentThread());
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
Log.e(TAG,"query,current Thread:Thread.currentThread());
return null;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
Log.e(TAG,"update,current Thread:Thread.currentThread());
return 0;
}
}
注释:写一个BookProvider继承自ContentProvider。其中方法实现暂时都通过打日志实现。关键在于如何使外部访问到BookProvider这个服务端进程,在于manifest的使用:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="haibo.com.contentprovider1">
<permission
android:name="haibo.com.PROVIDER"
android:label="provider permission"
android:protectionLevel="normal" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name=".provider.BookProvider"
android:authorities="haibo.com.contentprovider1.book.provider"
android:enabled="true"
android:exported="true"
android:permission="haibo.com.PROVIDER"></provider>
</application>
</manifest>
第28行,exported属性为true,表明可以被其他应用使用当前组件。(四大组件都有该属性)。重点:先注册(第29行)后发布(第6行)权限。
第29行,设置权限为一个自定义字符串(该权限可更细化为读权限和写权限writePermission和readPermission)
PS:随便什么字符串都可以作为访问当前组件的权限名。
第6行,发布该权限对应使用。
android:name="haibo.com.PROVIDER"
第26行,authorities必须唯一,建议加上包名前缀。
二、客户端App:
代码非常简单如下:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Uri uri = Uri.parse("content://haibo.com.contentprovider1.book.provider");
getContentResolver().query(uri,null,null,null,null);
getContentResolver().query(uri,null,null,null,null);
getContentResolver().query(uri,null,null,null,null);
}
}
关键在三个地方:
1、通过打印日志发现:BookProvider只有onCreate在主线程中,其他增删改查操作都在Binder线程(子线程)中。
2、第6行:通过uri访问contentprovider,这个contentprovider即我们服务端app注册manifest中使用的authorities。
3、客户端使用权限,需要在manifest中使用
<uses-permission android:name="haibo.com.PROVIDER"/>
通过以上代码,即可实现客户端app访问服务端app的contentprovider数据。
知新:
既然通过上述方式可以实现客户端app访问服务端app,那么我们可以通过服务端App的可以通过数据库实现增删改查。
一、服务端App
1、写一个数据库操作类,关于数据库的基础知识可以查看我这篇文章
public class DbOpenHelper extends SQLiteOpenHelper {
public static final String BOOK = "book";
public static final String USER = "user";
private static final String CREATE_BOOK = "create table book ("
+"id integer primary key autoincrement,"+"author text,"
+"price real,"+"pages integer,"+"name text)";//primary key 主键,autoincrement 自增长
private static final String CREATE_USER = "create table user ("
+"id integer primary key autoincrement,"+"username text,"
+"password text)";//新增一个表
private Context context;
public DbOpenHelper(Context mContext) {
super(mContext,"book_provider.db",null,1);
this.context = mContext;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK);
db.execSQL(CREATE_USER);//新增一条执行语句
Toast.makeText(context,"Create succeeded",Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
2、BookProvider修改如下:
public class BookProvider extends ContentProvider {
private static final String TAG = "BookProvider";
private static final String AUTHORITY = "haibo.com.contentprovider1.book.provider";
public static final int BOOK_URI_CODE = 0;
public static final int USER_URI_CODE = 1;
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
sUriMatcher.addURI(AUTHORITY,"book",BOOK_URI_CODE);
sUriMatcher.addURI(AUTHORITY,"user",USER_URI_CODE);
}
private Context mContext;
private SQLiteDatabase mDb;
public BookProvider() {
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
Log.e(TAG,"delete,current Thread:"+Thread.currentThread());
String table = getTableName(uri);
int count = mDb.delete(table,selection,selectionArgs);
return count;
}
@Override
public String getType(Uri uri) {
Log.e(TAG,"getType,current Thread:"+Thread.currentThread());
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
Log.e(TAG,"insert,current Thread:"+Thread.currentThread());
String table = getTableName(uri);
mDb.insert(table,null,values);
return uri;
}
@Override
public boolean onCreate() {
Log.e(TAG,"onCreate,current Thread:"+Thread.currentThread());
mContext = getContext();
mDb = new DbOpenHelper(mContext).getWritableDatabase();
mDb.execSQL("delete from book");
mDb.execSQL("delete from user");
mDb.execSQL("insert into book(name,pages,price,author) values ('安卓开发艺术',20,100,'任玉刚')");
mDb.execSQL("insert into book(name,pages,price,author) values ('effect java',10,40,'匿名')");
mDb.execSQL("insert into book(name,pages,price,author) values ('第一行代码',30,50,'郭霖')");
mDb.execSQL("insert into book(name,pages,price,author) values ('第一行代码',30,50,'郭霖')");
mDb.execSQL("insert into user(username,password) values ('bobo','000000')");
mDb.execSQL("insert into user(username,password) values ('hehe','111111')");
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
Log.e(TAG,"query,current Thread:"+Thread.currentThread());
String table = getTableName(uri);
return mDb.query(table,projection,selection,selectionArgs,null,null,sortOrder,null);
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
Log.e(TAG,"update,current Thread:"+Thread.currentThread());
String table = getTableName(uri);
return mDb.update(table,values,selection,selectionArgs);
}
private String getTableName(Uri uri){
String tableName = null;
switch (sUriMatcher.match(uri)){
case BOOK_URI_CODE:
tableName = DbOpenHelper.BOOK;
break;
case USER_URI_CODE:
tableName = DbOpenHelper.USER;
break;
}
return tableName;
}
}
关键点:
2.1、第10、11行:使用UriMatcher将AUTHORITY(即服务端manifest注册contentprovider的authorities唯一字符串)
+表名(即book或者user,数据库表的名称) 关联到一个int的值(即BOOK_URI_CODE,USER_URI_CODE用来枚举判断)上。
sUriMatcher.addURI(AUTHORITY,"book",BOOK_URI_CODE);
sUriMatcher.addURI(AUTHORITY,"user",USER_URI_CODE);
2.2、Uri可以通过上面获得的int的值,判断表名是什么。增删改查都是通过表名查询表中数据。(如String table = getTableName(uri);)
private String getTableName(Uri uri){
String tableName = null;
switch (sUriMatcher.match(uri)){
case BOOK_URI_CODE:
tableName = DbOpenHelper.BOOK;
break;
case USER_URI_CODE:
tableName = DbOpenHelper.USER;
break;
}
return tableName;
}
2.3、第46到54行:在onCreate中初始化数据。(第1行新建数据库,第2、3行清空表,后面插入初始化数据)
mDb = new DbOpenHelper(mContext).getWritableDatabase();
mDb.execSQL("delete from book");
mDb.execSQL("delete from user");
mDb.execSQL("insert into book(name,pages,price,author) values ('安卓开发艺术',20,100,'任玉刚')");
mDb.execSQL("insert into book(name,pages,price,author) values ('effect java',10,40,'匿名')");
mDb.execSQL("insert into book(name,pages,price,author) values ('第一行代码',30,50,'郭霖')");
mDb.execSQL("insert into book(name,pages,price,author) values ('第一行代码',30,50,'郭霖')");
mDb.execSQL("insert into user(username,password) values ('bobo','000000')");
mDb.execSQL("insert into user(username,password) values ('hehe','111111')");
二、客户端App:
代码修改如下:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Uri uri = Uri.parse("content://haibo.com.contentprovider1.book.provider/book");
selectCursor(uri);
Log.e("Main","--------执行前--------");
//此处添加增删改查方法代码
Log.e("Main","--------执行后--------");
selectCursor(uri);
}
private void selectCursor(Uri uri){
Cursor cursor = getContentResolver().query(uri,new String[]{"name","author"},null,null,null);
while (cursor.moveToNext()){
Book book = new Book();
book.setName(cursor.getString(0));
book.setAuthor(cursor.getString(1));
Log.e("Main",book.toString());
}
cursor.close();
}
private void addBook(Uri uri){
ContentValues contentValues = new ContentValues();
contentValues.put("name","bobo的book");
contentValues.put("pages",20);
contentValues.put("price",1000);
contentValues.put("author","bobo");
getContentResolver().insert(uri,contentValues);
}
private void delete(Uri uri){
int count = getContentResolver().delete(uri,"name=?",new String[]{"bobo的book"});
Log.e("Main","成功删除了"+count+"行数据");
}
private void update(Uri uri){
ContentValues contentValues = new ContentValues();
contentValues.put("name","bobo的book");
contentValues.put("pages",20);
contentValues.put("price",1000);
getContentResolver().update(uri,contentValues,"author=?",new String[]{"任玉刚"});
}
}
直接执行代码查询数据库日志如下:
02-01 18:53:15.783 21252-21252/haibo.com.providerclient E/Main: Book{name='安卓开发艺术', author='任玉刚'}
02-01 18:53:15.783 21252-21252/haibo.com.providerclient E/Main: Book{name='effect java', author='匿名'}
02-01 18:53:15.783 21252-21252/haibo.com.providerclient E/Main: Book{name='第一行代码', author='郭霖'}
02-01 18:53:15.783 21252-21252/haibo.com.providerclient E/Main: Book{name='第一行代码', author='郭霖'}
02-01 18:53:15.783 21252-21252/haibo.com.providerclient E/Main: --------执行前--------
02-01 18:53:15.783 21252-21252/haibo.com.providerclient E/Main: --------执行后--------
02-01 18:53:15.783 21252-21252/haibo.com.providerclient E/Main: Book{name='安卓开发艺术', author='任玉刚'}
02-01 18:53:15.783 21252-21252/haibo.com.providerclient E/Main: Book{name='effect java', author='匿名'}
02-01 18:53:15.783 21252-21252/haibo.com.providerclient E/Main: Book{name='第一行代码', author='郭霖'}
02-01 18:53:15.783 21252-21252/haibo.com.providerclient E/Main: Book{name='第一行代码', author='郭霖'}
若在第10行,替换为addBook(uri);再执行代码,日志如下:
02-01 18:56:25.133 24274-24274/? E/Main: Book{name='安卓开发艺术', author='任玉刚'}
02-01 18:56:25.133 24274-24274/? E/Main: Book{name='effect java', author='匿名'}
02-01 18:56:25.133 24274-24274/? E/Main: Book{name='第一行代码', author='郭霖'}
02-01 18:56:25.133 24274-24274/? E/Main: Book{name='第一行代码', author='郭霖'}
02-01 18:56:25.133 24274-24274/? E/Main: --------执行前--------
02-01 18:56:25.143 24274-24274/? E/Main: --------执行后--------
02-01 18:56:25.143 24274-24274/? E/Main: Book{name='安卓开发艺术', author='任玉刚'}
02-01 18:56:25.143 24274-24274/? E/Main: Book{name='effect java', author='匿名'}
02-01 18:56:25.143 24274-24274/? E/Main: Book{name='第一行代码', author='郭霖'}
02-01 18:56:25.143 24274-24274/? E/Main: Book{name='第一行代码', author='郭霖'}
02-01 18:56:25.143 24274-24274/? E/Main: Book{name='bobo的book', author='bobo'}
若在第10行,替换为delete(uri);;再执行代码,日志如下:
02-01 18:57:36.873 25507-25514/? E/jdwp: Failed sending reply to debugger: Broken pipe
02-01 18:57:36.963 25507-25507/? E/Main: Book{name='安卓开发艺术', author='任玉刚'}
02-01 18:57:36.963 25507-25507/? E/Main: Book{name='effect java', author='匿名'}
02-01 18:57:36.963 25507-25507/? E/Main: Book{name='第一行代码', author='郭霖'}
02-01 18:57:36.963 25507-25507/? E/Main: Book{name='第一行代码', author='郭霖'}
02-01 18:57:36.963 25507-25507/? E/Main: Book{name='bobo的book', author='bobo'}
02-01 18:57:36.963 25507-25507/? E/Main: --------执行前--------
02-01 18:57:36.983 25507-25507/? E/Main: 成功删除了1行数据
02-01 18:57:36.983 25507-25507/? E/Main: --------执行后--------
02-01 18:57:36.993 25507-25507/? E/Main: Book{name='安卓开发艺术', author='任玉刚'}
02-01 18:57:36.993 25507-25507/? E/Main: Book{name='effect java', author='匿名'}
02-01 18:57:36.993 25507-25507/? E/Main: Book{name='第一行代码', author='郭霖'}
02-01 18:57:36.993 25507-25507/? E/Main: Book{name='第一行代码', author='郭霖'}
若在第10行,替换为update(uri);再执行代码,日志如下:
02-01 18:58:31.733 26341-26341/haibo.com.providerclient E/Main: Book{name='安卓开发艺术', author='任玉刚'}
02-01 18:58:31.733 26341-26341/haibo.com.providerclient E/Main: Book{name='effect java', author='匿名'}
02-01 18:58:31.733 26341-26341/haibo.com.providerclient E/Main: Book{name='第一行代码', author='郭霖'}
02-01 18:58:31.733 26341-26341/haibo.com.providerclient E/Main: Book{name='第一行代码', author='郭霖'}
02-01 18:58:31.733 26341-26341/haibo.com.providerclient E/Main: --------执行前--------
02-01 18:58:31.743 26341-26341/haibo.com.providerclient E/Main: --------执行后--------
02-01 18:58:31.753 26341-26341/haibo.com.providerclient E/Main: Book{name='bobo的book', author='任玉刚'}
02-01 18:58:31.753 26341-26341/haibo.com.providerclient E/Main: Book{name='effect java', author='匿名'}
02-01 18:58:31.753 26341-26341/haibo.com.providerclient E/Main: Book{name='第一行代码', author='郭霖'}
02-01 18:58:31.753 26341-26341/haibo.com.providerclient E/Main: Book{name='第一行代码', author='郭霖'}
本文,完。