天天看点

【Android开发艺术】ContentProvider进程间通信前言:正文:温故:知新:

【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='郭霖'}

本文,完。