chapter 12 資料分享 - hscc homehscc.cs.nctu.edu.tw/~lincyu/android/chapter12.pdf · 12.2...

12
Chapter 12 資料分享 作者: 林致孙 上一章中我們學到了資料的儲存方式,也瞭解資料會依應用程式的 Package name 儲存於適當的資料夾內,然而 Android 並沒有一個共同的儲存空間讓所有應用程 式存取,因此若要讓其它的應用程式使用我的資料,就必須使用一些技巧,本章 主要在學習資料分享的方式。 在先前的章節中已經數次提到,一個 Android 應用程式是由四個構成要素所組成 的: Activity, Broadcast Receiver, Service Content Provider 。其中 Content Provider 是用來分享資料用的[1],然而筆者認為一個一般應用程式的開發者會需要寫一 Content Provider 讓其它開發者使用他的資料的情況應屬少數,頂多是開發者 需要在自己的應用程式間共享資料,因此本章只使用一個很簡單的程式範例,來 說明如何建立一個 Content Provider。事實上,一個一般應用程式的開發者比較 常使用的反而是去存取 Android 所提供的 Content Providers,例如存取系統的通 訊錄就是最常見的一個範例,本章也會使用一個簡單的範例來說明如何存取系統 的通訊錄。 12.1 偏好設定與檔案的分享 首先先解說如何讀取別的應用程式的偏好設定,首先讀者可引進光碟中『\範例 程式\Chapter12\Horoscope』這個專案,應用程式的功能是讓使用者輸入生日並得 知星座,程式會將使用者的生日偏好利用 SharedPreferences 儲存起來,這個專案 和『\範例程式\Chapter11\Horoscope』這個專案的差別很小,只是將 getSharedPreferences 的第二個參數從 MODE_PRIVATE 換成 MODE_WORLD_READABLE,這些常數是定義於 Context 類別[2],其中 MODE_WORLD_READABLE 是允許其它應用程式能讀取建立的偏好設定檔。 接下來我們的重心將放在其它應用程式如何讀取這個偏好設定檔,讀者可引進光 碟中『\範例程式\Chapter12\GetPrefBirthday』這個專案,這個專案裡面只有一個 名為 GetPref Activity,這個 Activity 的程式碼也相當簡短,如下所示: 1 public class GetPref extends Activity { 2 @Override 3 public void onCreate(Bundle savedInstanceState) { 4 super.onCreate(savedInstanceState); 5 setContentView(R.layout.main); 6

Upload: others

Post on 06-Jul-2020

6 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Chapter 12 資料分享 - HSCC HOMEhscc.cs.nctu.edu.tw/~lincyu/Android/Chapter12.pdf · 12.2 資料庫內容的分享與Content Provider簡介 在這一節中我們將學習如何分享資料庫裡的資料,我們利用Content

Chapter 12 資料分享

作者: 林致孙

上一章中我們學到了資料的儲存方式,也瞭解資料會依應用程式的 Package name

儲存於適當的資料夾內,然而 Android並沒有一個共同的儲存空間讓所有應用程

式存取,因此若要讓其它的應用程式使用我的資料,就必須使用一些技巧,本章

主要在學習資料分享的方式。

在先前的章節中已經數次提到,一個 Android應用程式是由四個構成要素所組成

的:Activity, Broadcast Receiver, Service與 Content Provider。其中 Content Provider

是用來分享資料用的[1],然而筆者認為一個一般應用程式的開發者會需要寫一

個 Content Provider讓其它開發者使用他的資料的情況應屬少數,頂多是開發者

需要在自己的應用程式間共享資料,因此本章只使用一個很簡單的程式範例,來

說明如何建立一個 Content Provider。事實上,一個一般應用程式的開發者比較

常使用的反而是去存取 Android所提供的 Content Providers,例如存取系統的通

訊錄就是最常見的一個範例,本章也會使用一個簡單的範例來說明如何存取系統

的通訊錄。

12.1 偏好設定與檔案的分享

首先先解說如何讀取別的應用程式的偏好設定,首先讀者可引進光碟中『\範例

程式\Chapter12\Horoscope』這個專案,應用程式的功能是讓使用者輸入生日並得

知星座,程式會將使用者的生日偏好利用 SharedPreferences儲存起來,這個專案

和『\範例程式\Chapter11\Horoscope』這個專案的差別很小,只是將

getSharedPreferences的第二個參數從MODE_PRIVATE換成

MODE_WORLD_READABLE,這些常數是定義於 Context類別[2],其中

MODE_WORLD_READABLE是允許其它應用程式能讀取建立的偏好設定檔。

接下來我們的重心將放在其它應用程式如何讀取這個偏好設定檔,讀者可引進光

碟中『\範例程式\Chapter12\GetPrefBirthday』這個專案,這個專案裡面只有一個

名為 GetPref的 Activity,這個 Activity的程式碼也相當簡短,如下所示:

1 public class GetPref extends Activity {

2 @Override

3 public void onCreate(Bundle savedInstanceState) {

4 super.onCreate(savedInstanceState);

5 setContentView(R.layout.main);

6

Page 2: Chapter 12 資料分享 - HSCC HOMEhscc.cs.nctu.edu.tw/~lincyu/Android/Chapter12.pdf · 12.2 資料庫內容的分享與Content Provider簡介 在這一節中我們將學習如何分享資料庫裡的資料,我們利用Content

7 TextView tv = (TextView)findViewById(R.id.tv_birthday);

8

9 Context otherApp = null;

10

11 try {

12 otherApp = createPackageContext("lincyu.horoscope", 0);

13 } catch (Exception e) {

14 Log.d("LINCYU", e.toString());

15 finish();

16 }

17

18 SharedPreferences pref =

19 otherApp.getSharedPreferences("PREF_BIRTH",

20 MODE_WORLD_READABLE);

21

22 String pref_month = pref.getString("PREF_MONTH", "1");

23 Integer intDay = pref.getInt("PREF_DAY", 1);

24

25 tv.setText(pref_month+"月"+intDay+"日");

26 }

27 }

讀者不難發現這個程式唯一沒學過的就只有第 12行由 Context類別所提供的

createPackageContext方法,第一個參數是『\範例程式\Chapter12\Horoscope』這

個專案的 Package name,第二個參數是設定旗標值,此處填 0即可。

createPackageContext方法會回傳一個 Context物件,程式將之命名為 otherApp,

我們只要呼叫 otherApp的 getSharedPreferences方法就可以取得 Horoscope這個

應用程式的偏好設定值了。

測試這個程式前記得要先安裝並執行 Horoscope這個應用程式(名為『星座計算

(V2)』),可嘗試輸入一個生日,如下圖左側所示,接著再執行 GetPrefBirthday

這個應用程式,如下圖右側所示,可發現程式成功地讀取別的應用程式的偏好設

定。

Page 3: Chapter 12 資料分享 - HSCC HOMEhscc.cs.nctu.edu.tw/~lincyu/Android/Chapter12.pdf · 12.2 資料庫內容的分享與Content Provider簡介 在這一節中我們將學習如何分享資料庫裡的資料,我們利用Content

存取別的應用程式的檔案也是同樣的方法,先利用 createPackageContext取得別

的應用程式的Context物件,再呼叫Context類別的openFileInput或openFileOutput

方法即可。

讀者可參閱『\範例程式\Chapter12\ShareFile』專案與『\範例程式

\Chapter12\ReadSharedFile』專案。ShareFile這個 Activity包含一個 EditText介面

元件,使用者所輸入的文字會被儲存在一個名為 share.txt的檔案裡,在第十一章

我們已學過寫檔的方法,首先必須呼叫 Context類別的 openFileOutput方法取得

一個 FileOutputStream物件,其中 openFileOutput的第二個參數在第十一章是設

定成MODE_PRIVATE,在 ShareFile這個應用程式中,程式是設成

MODE_WORLD_READABLE,讓別的應用程式可以讀取 share.txt。

ReadSharedFile這個 Activity一樣使用 createPackageContext方法取得 ShareFile

的 Context物件後,再呼叫 openFileInput將檔案讀取出來並顯示於 TextView介

面元件上。讀者可先執行 ShareFile,執行範例如下圖左側所示,接著再執行

ReadSharedFile,驗證其結果。

Page 4: Chapter 12 資料分享 - HSCC HOMEhscc.cs.nctu.edu.tw/~lincyu/Android/Chapter12.pdf · 12.2 資料庫內容的分享與Content Provider簡介 在這一節中我們將學習如何分享資料庫裡的資料,我們利用Content

最後要提醒讀者的是,如果想讓別的應用程式也可改寫檔案,mode參數可填入

MODE_WORLD_READABLE+MODE_WORLD_WRITETABLE。

12.2資料庫內容的分享與 Content Provider簡介

在這一節中我們將學習如何分享資料庫裡的資料,我們利用 Content Provider來

做資料庫資料的分享,Content Provider可以讓一個應用程式分享它的資料給其

它應用程式,由於 Content Provider是以資料庫的型式來呈現其資料,因此使用

Content Provider來分享資料庫裡的資料是蠻合適的。

筆者修改了第十一章的 Notepad2這個便條簿程式,這個程式是使用資料庫的型

式儲存便條資料,因此可以拿來示範 Content Provider的使用,請讀者引進『\範

例程式\Chapter12\Notepad2CP』這個專案,其和 Notepad2的差別只有兩處:

新增一個 NoteProvider.java,NoteProvider繼承了 ContentProvider類別,是

一個 Content Provider。

修改 AndroidManifest.xml,幫新增的 Content Provider做註冊。

首先我們先討論 NoteProvider.java,完整的程式碼如下所示:

1 public class NoteProvider extends ContentProvider {

2

3 Context mCtx;

4 SQLiteDatabase db;

5

6 @Override

Page 5: Chapter 12 資料分享 - HSCC HOMEhscc.cs.nctu.edu.tw/~lincyu/Android/Chapter12.pdf · 12.2 資料庫內容的分享與Content Provider簡介 在這一節中我們將學習如何分享資料庫裡的資料,我們利用Content

7 public boolean onCreate() {

8 mCtx = getContext();

9 DatabaseHelper dbHelper;

10 dbHelper = new DatabaseHelper(mCtx);

11 db = dbHelper.getWritableDatabase();

12 return true;

13 }

14

15 @Override

16 public Cursor query(Uri uri, String[] projection, String selection,

17 String[] selectionArgs, String sortOrder) {

18

19 Cursor c = db.rawQuery("select * from " +

20 NoteDB.DATABASE_TABLE + ";", null);

21

22 return c;

23 }

24

25 @Override

26 public int update (Uri uri, ContentValues values, String selection,

27 String[] selectionArgs) {

28 return -1;

29 }

30

31 @Override

32 public Uri insert (Uri uri, ContentValues values) {

33 return null;

34 }

35

36 @Override

37 public int delete (Uri uri, String selection, String[]

38 selectionArgs){

39 return -1;

40 }

41

42 @Override

43 public String getType(Uri uri) {

44 return "";

Page 6: Chapter 12 資料分享 - HSCC HOMEhscc.cs.nctu.edu.tw/~lincyu/Android/Chapter12.pdf · 12.2 資料庫內容的分享與Content Provider簡介 在這一節中我們將學習如何分享資料庫裡的資料,我們利用Content

45 }

46 }

首先在第 1行我們可發現 NoteProvider類別繼承了 ContentProvider類別[3],總

共有六個抽象方法實作:onCreate, query, update, insert, delete, getType。onCreate

方法是在 Provider被啟動時呼叫,程式在 onCreate方法了產生一個

SQLiteDatabase物件,讓我們可以進行資料庫的操作。要提醒讀者注意的是,

DatabaseHelper類別是原本 Notepad2專案就已經寫好的,其會開啟 notes.db這個

資料庫,並建立一個名為 notetable的表格(如果此表格尚未被建立)。

接著我們看覆寫的 query方法,內容相當簡單,呼叫 SQLiteDatabase類別的

rawQuery方法查詢表格,並取得一個 Cursor物件,由於 query方法需要回傳一

個 Cursor物件,程式便將取得的 Cursor物件回傳。

Content Provider設計完後,如同Android應用程式的其它構成要素,我們需要在

應用程式描述檔做註冊的動作,方法是於<application>標籤內利用<provider>標籤

來註冊,內容如下:

<provider android:name=".NoteProvider"

android:authorities="lincyu.noteprovider">

</provider>

其中android:name的值填入Content Provider的類別名稱即可,而android:authorities

是用來辮識Provider的,只要不和系統提供的Providers一樣就可以了。

至此我們已經建立一個Content Provider,利用Notepad2CP所建立的便條可以供其

它應用程式來查詢,接下來我們便要來說明其它應用程式如何查詢Notepad2CP

所產生的便條。

請讀者引進『\範例程式\Chapter12\Notepad2CR』這個專案,這個專案只有一個

Activity,其功能是利用Content Resolver讀取NoteProvider提供的資料,並將第一

筆便條顯示於畫面上。Notepad2CR.java的內容如下所示:

1 public class Notepad2CR extends Activity {

2 @Override

3 public void onCreate(Bundle savedInstanceState) {

4 super.onCreate(savedInstanceState);

5 setContentView(R.layout.main);

6

7 Cursor c = getContentResolver().query(

Page 7: Chapter 12 資料分享 - HSCC HOMEhscc.cs.nctu.edu.tw/~lincyu/Android/Chapter12.pdf · 12.2 資料庫內容的分享與Content Provider簡介 在這一節中我們將學習如何分享資料庫裡的資料,我們利用Content

8 Uri.parse("content://lincyu.noteprovider"),

9 null, null, null, null);

10

11 TextView tv1 = (TextView)findViewById(R.id.tv_title);

12 TextView tv2 = (TextView)findViewById(R.id.tv_body);

13

14 if (c.getCount() != 0) {

15 c.moveToFirst();

16 String title = c.getString(c.getColumnIndex("title"));

17 String body = c.getString(c.getColumnIndex("body"));

18

19 if (title == null || body == null) return;

20

21 tv1.setText(title);

22 tv2.setText(body);

23 }

24 }

25 }

要讀取Content Provider的內容必須利用ContentResolver物件,程式在第7行先呼叫

Context類別的getContentResolver方法取得一個ContentResolver物件,

ContentResolver類別提供了許多跟ContentProvider類別一樣的方法(Methods)[4],

讓我們可以對Content Provider所提供的內容做處理,其中一個方法是query,在

程式中我們只使用到第一個參數,第一個參數是填入一個Uri物件,程式呼叫Uri

類別的parse方法取得一個Uri物件,parse方法的參數填入我們所要存取的Content

Provider。

query方法會回傳一個Cursor物件,若沒有任何的便條,程式便不做任何的處理,

若有便條存在,會將Cursor移到第一筆便條,並讀取title欄位與body欄位的資料,

將之顯示於適當的TextView上。

測試程式時,必須先安裝Notepad2CP(應用程式的名稱為『便條簿』),並可嘗試

新增一個便條,接著執行Notepad2CR,便可驗證程式是否正常運作。

由於筆者認為一個一般應用程式的開發者會需要寫一個 Content Provider讓其它

開發者使用他的資料的情況應屬少數,頂多是開發者需要在自己的應用程式間共

享資料,因此只使用一個很簡單的程式範例,來說明如何建立一個 Content

Provider,若要對 Content Provider有一個完整的瞭解,請讀者自行閱讀 Android

Page 8: Chapter 12 資料分享 - HSCC HOMEhscc.cs.nctu.edu.tw/~lincyu/Android/Chapter12.pdf · 12.2 資料庫內容的分享與Content Provider簡介 在這一節中我們將學習如何分享資料庫裡的資料,我們利用Content

開發者網站上的文件[1]。

12.3 取得系統通訊錄資料

對於一般應用程式的開發者而言,比較會常用到的,是使用系統提供的 Content

Providers,例如讀取通訊錄的內容,或讀取系統的設定值。本節中我們將學習如

何讀取系統的通訊錄內容。

首先要提醒讀者的是,在 Android SDK 2.0後,讀取通訊錄所使用的類別有一些

變化,主要是使用 ContactsContract類別[5],之前的版本是使用 Contacts類別[6]。

筆者的程式是使用 ContactsContract類別,因此在測試程式時,請記得在適當的

模擬器(Platform必須是 2.0以上)上執行。

請讀者引進光碟中『\範例程式\Chapter12\ContactEmail』這個專案,程式的功能

很簡單,讀取通訊錄裡的聯絡人姓名及電子郵件,並將資料利用列表介面元件

(ListView)顯示出來,專案中只有 ContactEmail這一個 Activity,內容如下所示:

1 public class ContactEmail extends ListActivity {

2 @Override

3 public void onCreate(Bundle savedInstanceState) {

4 super.onCreate(savedInstanceState);

5 setContentView(R.layout.main);

6

7 Cursor c = getContentResolver().query(

8 Email.CONTENT_URI, null, null, null, null);

9

10 startManagingCursor(c);

11

12 ListAdapter adapter = new SimpleCursorAdapter(this,

13 R.layout.my_list_item_2, c,

14 new String[] { Data.DISPLAY_NAME, Email.DATA},

15 new int[] { R.id.tv_name, R.id.tv_email });

16 setListAdapter(adapter);

17 }

18 }

首先先看第 7行,要存取系統的 Content Provider,也是先用 getContentResolver

方法取得一個 ContentResolver物件,再呼叫 query方法取得所需的資料,前一節

才提過 query方法需要一個 Uri物件當參數,常數 Email.CONTENT_URI是一個

Page 9: Chapter 12 資料分享 - HSCC HOMEhscc.cs.nctu.edu.tw/~lincyu/Android/Chapter12.pdf · 12.2 資料庫內容的分享與Content Provider簡介 在這一節中我們將學習如何分享資料庫裡的資料,我們利用Content

Uri物件,定義在 ContactsContract.CommonDataKinds.Email類別[7],讀者可將這

個類別視為一個表格,其中表示 Email.DATA欄位代表電子郵件的資料,文件中

有提到,ContactsContract.Data類別的所有欄位也可以使用,其中

Data.DISPLAY_NAME欄位是用來顯示完整姓名的,其它的欄位意義請讀者自行

閱讀相關說明文件。

程式於第 10行呼叫 Activity類別的 startManagingCursor方法來幫忙做 Cursor的

管理,例如在必要時重新做查詢,詳細的說明請參考 Activity類別的說明文件。

在第八章時我們有提過 ListView需要一個接合器(Adapter) 將『資料』和『介面

元件』做接合,之前已經學過如何利用 ArrayAdapter產生 ListAdapter物件,這

一章我們利用 SimpleCursorAdapter類別來產生 ListAdapter物件[9],

SimpleCursorAdapter可將 Cursor所指的表格中的某一欄的『資料』對應到『介

面元件』上,其建構子需要五個參數:

Context context:第一個參數是一個 Context物件,填入 ContactEmail的物件

實體即可。

int layout:第二個參數是一個版面資源檔,是列表元件裡每一個『項目』所

呈現的版面,讀者可以打開/res/layout/my_list_item_2.xml,裡面使用了一個

LinearLayout包住了兩個 TextView,且這兩個 TextView會以垂直方式排列,

執行結果如下圖所示,這個執行範例包含了兩個項目。

Cursor c:第三個參數需要一個 Cursor物件,填入 query方法所回傳的 Cursor

物件即可。

String[] from:接合器是接合『資料』與『介面元件』,這個參數是用來指定

『資料』的來源,我們從 Cursor所指的表格中取出兩欄:

Data.DISPLAY_NAME與 Email.DATA,分別代表使用者姓名與電子郵件資

Page 10: Chapter 12 資料分享 - HSCC HOMEhscc.cs.nctu.edu.tw/~lincyu/Android/Chapter12.pdf · 12.2 資料庫內容的分享與Content Provider簡介 在這一節中我們將學習如何分享資料庫裡的資料,我們利用Content

料。

int[] to:這個參數是將『資料』對應到適當的地方,我們將

Data.DISPLAY_NAME的資料放入 tv_name這個 TextView,將 Email.DATA

的資料放入 tv_email這個 TextView。

至此讀者應已能瞭解所有程式細節,我們可以做一些測試,首先先利用模擬器提

供的 Contacts應用程式新增幾筆聯絡人資料,如下圖所示,接著再執行

ContactsEmail即可驗證是否有正確地讀取通訊錄資料。

除了通訊錄,Android還提供了其它許多的 Content Providers,讀者可參閱

android.provider套件的說明文件得知 Android提供了哪些 Content

Providers[10] 。

12.4 摘要

本章介紹了 Android上存取其它應用程式的資料的方法,若想存取別的應用程式

的偏好設定與檔案,可呼叫 createPackageContext方法取得別的應用程式的

Context物件,再呼叫 getSharedPreferences方法或 openFileInput相關方法即可。

當然分享偏好設定與檔案的應用程式也必須對檔案的模式(mode)做適當的設

定。

若想分享資料庫裡的資料給其它應用程式,可建立一個 Content Provider,若想

使用其它應用程式所提供的 Content Provider則必須使用 Content Resolver,

Page 11: Chapter 12 資料分享 - HSCC HOMEhscc.cs.nctu.edu.tw/~lincyu/Android/Chapter12.pdf · 12.2 資料庫內容的分享與Content Provider簡介 在這一節中我們將學習如何分享資料庫裡的資料,我們利用Content

Android提供了許多的 Content Providers供程式開發者使用。

12.5 作業

1. 寫一個新的應用程式,讓這個應用程式能讀取第十一章的 Notepad1這個便條

簿的 NoteList.txt。當然 Notepad1專案內的程式也必須做適當的修改。提示:

使用 createPackageContext方法。

2. 寫一個需要使用到資料庫的應用程式,並替這個應用程式建立一個 Content

Provider。

3. 修改『\範例程式\Chapter12\ContactEmail』這個專案。將電子郵件換成電話號

碼。提示:參閱 ContactsContract.CommonDataKinds.Phone類別[11]。

12.6 參考資料

[1] Content Providers | Android Developers,

http://developer.android.com/guide/topics/providers/content-providers.html

[2] Context | Android Developers,

http://developer.android.com/reference/android/content/Context.html

[3] ContentProvider | Android Developers,

http://developer.android.com/reference/android/content/ContentProvider.html

[4] ContentResolver | Android Developers,

http://developer.android.com/reference/android/content/ContentResolver.html

[5] ContactsContract | Android Developers,

http://developer.android.com/reference/android/provider/ContactsContract.html

[6] Contacts | Android Developers,

http://developer.android.com/reference/android/provider/Contacts.html

[7] ContactsContract.CommonDataKinds.Email | Android Developers,

http://developer.android.com/reference/android/provider/ContactsContract.CommonD

ataKinds.Email.html

Page 12: Chapter 12 資料分享 - HSCC HOMEhscc.cs.nctu.edu.tw/~lincyu/Android/Chapter12.pdf · 12.2 資料庫內容的分享與Content Provider簡介 在這一節中我們將學習如何分享資料庫裡的資料,我們利用Content

[8] ContactsContract.Data | Android Developers,

http://developer.android.com/reference/android/provider/ContactsContract.Data.html

[9] SimpleCursorAdapter | Android Developers,

http://developer.android.com/reference/android/widget/SimpleCursorAdapter.html

[10] android.provder | Android Developers,

http://developer.android.com/reference/android/provider/package-summary.html

[11] ContactsContract.CommonDataKinds.Phone | Android Developers,

http://developer.android.com/reference/android/provider/ContactsContract.CommonD

ataKinds.Phone.html