天天看點

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

本文,完。