android: 實現跨程序數據共享


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

打開 DatabaseTest 項目,首先將 MyDatabaseHelper 中使用 Toast 彈出創建數據庫成功的提示去除掉,因為跨程序訪問時我們不能直接使用 Toast。然后添加 一個 DatabaseProvider 類,代碼如下所示:

 public class DatabaseProvider extends ContentProvider {

 

public static final int BOOK_DIR = 0;

public static final int BOOK_ITEM = 1;

public static final int CATEGORY_DIR = 2;

public static final int CATEGORY_ITEM = 3;

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

private static UriMatcher uriMatcher;

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 中的數據,而且我們還絲毫不用擔心隱私 數據泄漏的問題。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM