【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='郭霖'}
本文,完。