android: 實現跨程序數據共享

簡單起見,咱們仍是在上一章中 DatabaseTest 項目的基礎上繼續開發,經過內容提供器 來給它加入外部訪問接口。android

打開 DatabaseTest 項目,首先將 MyDatabaseHelper 中使用 Toast 彈出建立數據庫成功的提示去除掉,由於跨程序訪問時咱們不能直接使用 Toast。而後添加 一個 DatabaseProvider 類,代碼以下所示:數據庫

 public class DatabaseProvider extends ContentProvider {app

 

public static final int BOOK_DIR = 0; ide

public static final int BOOK_ITEM = 1; 佈局

public static final int CATEGORY_DIR = 2; 3d

public static final int CATEGORY_ITEM = 3;日誌

public static final String AUTHORITY = "com.example.databasetest.provider";orm

private static UriMatcher uriMatcher;xml

private MyDatabaseHelper dbHelper; 對象

 

static {

uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

uriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);

uriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);

uriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);

uriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);

}

 

@Override

public boolean onCreate() {

dbHelper = new MyDatabaseHelper(getContext(), "BookStore.db", null, 2);

return true;

}

 

@Override

public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {

// 查詢數據

SQLiteDatabase db = dbHelper.getReadableDatabase();

Cursor cursor = null;

switch (uriMatcher.match(uri)) {

case BOOK_DIR:

cursor = db.query("Book", projection, selection, selectionArgs, null, null, sortOrder);

break;

case BOOK_ITEM:

String bookId = uri.getPathSegments().get(1);

cursor = db.query("Book", projection, "id = ?", new String[]{ bookId }, null, null, sortOrder);

break;

case CATEGORY_DIR:

cursor = db.query("Category", projection, selection, selectionArgs, null, null, sortOrder);

break;

case CATEGORY_ITEM:

String categoryId = uri.getPathSegments().get(1);

cursor = db.query("Category", projection, "id = ?", new String[]{ categoryId }, null, null, sortOrder);

break;

default:

break;

}

return cursor;

}

 

@Override

public Uri insert(Uri uri, ContentValues values) {

 

// 添加數據

SQLiteDatabase db = dbHelper.getWritableDatabase(); Uri uriReturn = null;

switch (uriMatcher.match(uri)) {

case BOOK_DIR:

case BOOK_ITEM:

long newBookId = db.insert("Book", null, values);

uriReturn = Uri.parse("content://" + AUTHORITY + "/book/" +newBookId);

break;

case CATEGORY_DIR:

case CATEGORY_ITEM:

long newCategoryId = db.insert("Category", null, values);

uriReturn = Uri.parse("content://" + AUTHORITY + "/category/" +newCategoryId);

break;

default:

break;

}

return uriReturn;

}

 

@Override

public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {

// 更新數據

SQLiteDatabase db = dbHelper.getWritableDatabase();

int updatedRows = 0;

switch (uriMatcher.match(uri)) {

case BOOK_DIR:

updatedRows = db.update("Book", values, selection, selectionArgs);

break;

case BOOK_ITEM:

String bookId = uri.getPathSegments().get(1);

updatedRows = db.update("Book", values, "id = ?", new String[]{ bookId });

break;

case CATEGORY_DIR:

updatedRows = db.update("Category", values, selection, selectionArgs);

break;

case CATEGORY_ITEM:

String categoryId = uri.getPathSegments().get(1);

updatedRows = db.update("Category", values, "id = ?", new String[]{ categoryId });

break;

default:

break;

}

return updatedRows;

}

 

 

@Override

public int delete(Uri uri, String selection, String[] selectionArgs) {

// 刪除數據

SQLiteDatabase db = dbHelper.getWritableDatabase();

int deletedRows = 0;

switch (uriMatcher.match(uri)) {

case BOOK_DIR:

deletedRows = db.delete("Book", selection, selectionArgs);

break;

case BOOK_ITEM:

String bookId = uri.getPathSegments().get(1);

deletedRows = db.delete("Book", "id = ?", new String[] { bookId });

break;

case CATEGORY_DIR:

deletedRows = db.delete("Category", selection, selectionArgs);

break;

case CATEGORY_ITEM:

String categoryId = uri.getPathSegments().get(1);

deletedRows = db.delete("Category", "id = ?", new String[]{ categoryId });

break;

default:

break;

}

return deletedRows;

}

 

@Override

public String getType(Uri uri) {

switch (uriMatcher.match(uri)) {

case BOOK_DIR:

return "vnd.android.cursor.dir/vnd.com.example.databasetest. provider.book";

case BOOK_ITEM:

return "vnd.android.cursor.item/vnd.com.example.databasetest. provider.book";

case CATEGORY_DIR:

return "vnd.android.cursor.dir/vnd.com.example.databasetest. provider.category";

case CATEGORY_ITEM:

return "vnd.android.cursor.item/vnd.com.example.databasetest. provider.category";

}

return null;

}

}

代碼雖然很長,不過不用擔憂,這些內容都很是容易理解,由於使用到的所有都是上一 小節中咱們學到的知識。首先在類的一開始,一樣是定義了四個常量,分別用於表示訪問 Book 表中的全部數據、訪問 Book 表中的單條數據、訪問 Category 表中的全部數據和訪問 Category 表中的單條數據。而後在靜態代碼塊裏對 UriMatcher 進行了初始化操做,將指望匹 配的幾種 URI 格式添加了進去。

接下來就是每一個抽象方法的具體實現了,先來看下 onCreate()方法,這個方法的代碼很 短,就是建立了一個 MyDatabaseHelper 的實例,而後返回 true 表示內容提供器初始化成功, 這時數據庫就已經完成了建立或升級操做。

接着看一下 query()方法,在這個方法中先獲取到了 SQLiteDatabase 的實例,而後根據 傳入的 Uri 參數判斷出用戶想要訪問哪張表,再調用 SQLiteDatabase 的 query()進行查詢,並 將 Cursor 對象返回就行了。注意當訪問單條數據的時候有一個細節,這裏調用了 Uri 對象的 getPathSegments()方法,它會將內容 URI 權限以後的部分以「/」符號進行分割,並把分割後 的結果放入到一個字符串列表中,那這個列表的第 0 個位置存放的就是路徑,第 1 個位置存 放的就是 id 了。獲得了 id 以後,再經過 selection 和 selectionArgs 參數進行約束,就實現了 查詢單條數據的功能。

再日後就是 insert()方法,一樣它也是先獲取到了 SQLiteDatabase 的實例,而後根據傳入 的 Uri 參數判斷出用戶想要往哪張表裏添加數據,再調用 SQLiteDatabase 的 insert()方法進行添加就能夠了。注意 insert()方法要求返回一個可以表示這條新增數據的 URI,因此咱們還須要調用 Uri.parse()方法來將一個內容 URI 解析成 Uri 對象,固然這個內容 URI 是以新增數據 的 id 結尾的。

接下來就是 update()方法了,相信這個方法中的代碼已經徹底難不倒你了。也是先獲取 SQLiteDatabase 的實例,而後根據傳入的 Uri 參數判斷出用戶想要更新哪張表裏的數據,再 調用 SQLiteDatabase 的 update()方法進行更新就行了,受影響的行數將做爲返回值返回。

下面是 delete()方法,是否是感受越到後面越輕鬆了?由於你已經漸入佳境,真正地找 到竅門了。這裏仍然是先獲取到 SQLiteDatabase 的實例,而後根據傳入的 Uri 參數判斷出用 戶想要刪除哪張表裏的數據,再調用 SQLiteDatabase 的 delete()方法進行刪除就行了,被刪 除的行數將做爲返回值返回。

最後是 getType()方法,這個方法中的代碼徹底是按照上一節中介紹的格式規則編寫的, 相信已經沒有什麼解釋的必要了。

這樣咱們就將內容提供器中的代碼所有編寫完了,不過離實現跨程序數據共享的功能還 差了一小步,由於還須要將內容提供器在 AndroidManifest.xml 文件中註冊才能夠,以下所示:

 

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.databasetest"

android:versionCode="1" android:versionName="1.0" >

……

 

<application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" >

……

 

<provider android:name="com.example.databasetest.DatabaseProvider" android:authorities="com.example.databasetest.provider" >

</provider>

</application>

</manifest>

能夠看到,這裏咱們使用了<provider>標籤來對 DatabaseProvider 這個內容提供器進行注 冊,在 android:name 屬性中指定了該類的全名,又在 android:authorities 屬性中指定了該內容 提供器的權限。

如今 DatabaseTest 這個項目就已經擁有了跨程序共享數據的功能了,咱們趕快來嘗試一下。首先須要將 DatabaseTest 程序從模擬器中刪除掉,以防止上一章中產生的遺留數據對我

們形成干擾。而後運行一下項目,將 DatabaseTest 程序從新安裝在模擬器上了。接着關閉掉 DatabaseTest 這個項目,並建立一個新項目 ProviderTest ,咱們就將經過這個程序去訪問 DatabaseTest 中的數據。

仍是先來編寫一下佈局文件吧,修改 activity_main.xml 中的代碼,以下所示:

 

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"

android:orientation="vertical" >

 

 

<Button android:id="@+id/add_data" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Add To Book" />

 

<Button android:id="@+id/query_data" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Query From Book" />

 

<Button android:id="@+id/update_data" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Update Book" />

 

<Button android:id="@+id/delete_data" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Delete From Book" />

 

</LinearLayout>

佈局文件很簡單,裏面放置了四個按鈕,分別用於添加、查詢、修改和刪除數據的。然 後修改 MainActivity 中的代碼,以下所示:

 

public class MainActivity extends Activity {

 

 

private String newId;

 

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

Button addData = (Button) findViewById(R.id.add_data);

addData.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

// 添加數據

Uri uri = Uri.parse("content://com.example.databasetest. provider/book");

ContentValues values = new ContentValues();

values.put("name", "A Clash of Kings");

values.put("author", "George Martin");

values.put("pages", 1040);

values.put("price", 22.85);

Uri newUri = getContentResolver().insert(uri, values);

newId = newUri.getPathSegments().get(1);

}

});

Button queryData = (Button) findViewById(R.id.query_data);

queryData.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

// 查詢數據

Uri uri = Uri.parse("content://com.example.databasetest. provider/book");

Cursor cursor = getContentResolver().query(uri, null, null,null, null); 

 

if (cursor != null) {

while (cursor.moveToNext()) {

String name = cursor.getString(cursor.getColumnIndex("name"));

String author = cursor.getString(cursor. getColumnIndex("author"));

int pages = cursor.getInt(cursor.getColumnIndex("pages")); 

double price = cursor.getDouble(cursor. getColumnIndex("price"));

 

Log.d("MainActivity", "book name is " + name);

Log.d("MainActivity", "book author is " + author);

Log.d("MainActivity", "book pages is " + pages);

Log.d("MainActivity", "book price is " + price);

 }

}

}

cursor.close();

}

 

Button updateData = (Button) findViewById(R.id.update_data);

updateData.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

// 更新數據

Uri uri = Uri.parse("content://com.example.databasetest. provider/book/" + newId);

ContentValues values = new ContentValues();

values.put("name", "A Storm of Swords");

values.put("pages", 1216);

values.put("price", 24.05);

getContentResolver().update(uri, values, null, null);

}

});

 

Button deleteData = (Button) findViewById(R.id.delete_data);

deleteData.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

// 刪除數據

Uri uri = Uri.parse("content://com.example.databasetest. provider/book/" + newId);

getContentResolver().delete(uri, null, null);

}

});

}

 

}

 

 

能夠看到,咱們分別在這四個按鈕的點擊事件裏面處理了增刪改查的邏輯。添加數據的時候,首先調用了 Uri.parse()方法將一個內容 URI 解析成 Uri 對象,而後把要添加的數據都 存放到 ContentValues 對象中,接着調用 ContentResolver 的 insert()方法執行添加操做就能夠 了。注意 insert()方法會返回一個 Uri 對象,這個對象中包含了新增數據的 id,咱們經過 getPathSegments()方法將這個 id 取出,稍後會用到它。

查詢數據的時候,一樣是調用了 Uri.parse()方法將一個內容 URI 解析成 Uri 對象,而後 調用 ContentResolver 的 query()方法去查詢數據,查詢的結果固然仍是存放在 Cursor 對象中 的。以後對 Cursor 進行遍歷,從中取出查詢結果,並一一打印出來。

更新數據的時候,也是先將內容 URI 解析成 Uri 對象,而後把想要更新的數據存放到 ContentValues 對象中,再調用 ContentResolver 的 update()方法執行更新操做就能夠了。注意 這裏咱們爲了避免想讓 Book 表中其餘的行受到影響,在調用 Uri.parse()方法時,給內容 URI 的尾部增長了一個 id,而這個 id 正是添加數據時所返回的。這就表示咱們只但願更新剛剛 添加的那條數據,Book 表中的其餘行都不會受影響。

刪除數據的時候,也是使用一樣的方法解析了一個以 id 結尾的內容 URI,而後調用 ContentResolver 的 delete()方法執行刪除操做就能夠了。因爲咱們在內容 URI 裏指定了一個 id,所以只會刪掉擁有相應 id 的那行數據,Book 表中的其餘數據都不會受影響。

如今運行一下 ProviderTest 項目,會顯示如圖 7.4 所示的界面。

 

圖   7.4

 

點擊一下 Add To Book 按鈕,此時數據就應該已經添加到 DatabaseTest 程序的數據庫中了,咱們能夠經過點擊 Query From Book 按鈕來檢查一下,打印日誌如圖 7.5 所示。

 

圖   7.5

 

而後點擊一下 Update Book 按鈕來更新數據,再點擊一下 Query From Book 按鈕進行檢 查,結果如圖 7.6 所示。

 

圖   7.6

 

最後點擊 Delete From Book 按鈕刪除數據,此時再點擊 Query From Book 按鈕就查詢不 到數據了。

由此能夠看出,咱們的跨程序共享數據功能已經成功實現了!如今不只是 ProviderTest 程序,任何一個程序均可以輕鬆訪問 DatabaseTest 中的數據,並且咱們還絲絕不用擔憂隱私 數據泄漏的問題。

相關文章
相關標籤/搜索