標籤:
很清晰的思路,轉自Android資料的四種儲存方式
作為一個完成的應用程式,資料存放區操作是必不可少的。因此,Android系統一共提供了四種資料存放區方式。分別是:SharePreference、SQLite、Content Provider和File。由於Android系統中,資料基本都是私人的的,都是存放於“data/data/程式包名”目錄下,所以要實現資料共用,正確方式是使用Content Provider。
SQLite: SQLite是一個輕量級的資料庫,支援基本SQL文法,是常被採用的一種資料存放區方式。Android為此資料庫提供了一個名為SQLiteDatabase的類,封裝了一些操作資料庫的API。
SharedPreference: 除SQLite資料庫外,另一種常用的資料存放區方式,其本質就是一個xml檔案,常用於儲存較簡單的參數設定。
File: 即常說的檔案(I/O)儲存方法,常用語儲存大數量的資料,但是缺點是更新資料將是一件困難的事情。
ContentProvider: Android系統中能實現所有應用程式共用的一種資料存放區方式,由於資料通常在各應用間的是互相私密的,所以此儲存方式較少使用,但是其又是必不可少的一種儲存方式。例如音頻,視頻,圖片和通訊錄,一般都可以採用此種方式進行儲存。每個Content Provider都會對外提供一個公用的URI(封裝成Uri對象),如果應用程式有資料需要共用時,就需要使用Content Provider為這些資料定義一個URI,然後其他的應用程式就通過Content Provider傳入這個URI來對資料進行操作。
SQLite是一種轉為嵌入式裝置設計的輕型資料庫,其只有五種資料類型,分別是:
NULL: 空值
INTEGER: 整數
REAL: 浮點數
TEXT: 字串
BLOB: 大資料
在SQLite中,並沒有專門設計BOOLEAN和DATE類型,因為BOOLEAN型可以用INTEGER的0和1代替true和false,而DATE類型則可以擁有特定格式的TEXT、REAL和INTEGER的值來代替顯示,為了能方便的操作DATE類型,SQLite提供了一組函數,詳見:http://www.sqlite.org/lang_datefunc.html。這樣簡單的資料類型設計更加符合嵌入式裝置的要求。關於SQLite的更多資料,請參看:http://www.sqlite.org/
在Android系統中提供了android.database.sqlite包,用於進行SQLite資料庫的增、刪、改、查工作。其主要方法如下:
beginTransaction(): 開始一個事務。
close(): 關閉串連,釋放資源。
delete(String table, String whereClause, String[] whereArgs): 根據給定條件,刪除合格記錄。
endTransaction(): 結束一個事務。
execSQL(String sql): 執行給定SQL語句。
insert(String table, String nullColumnHack, ContentValues values): 根據給定條件,插入一條記錄。
openOrCreateDatabase(String path, SQLiteDatabase.CursorFactory factory): 根據給定條件串連資料庫,如果此資料庫不存在,則建立。
query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy): 執行查詢。
rawQuery(String sql, String[] selectionArgs): 根據給定SQL,執行查詢。
update(String table, ContentValues values, String whereClause, String[] whereArgs): 根據給定條件,修改合格記錄。
除了上訴主要方法外,Android還提供了諸多實用的方法,總之一句話:其實Android訪問資料庫是一件很方便的事兒。
一、 建立資料庫
通過openOrCreateDatabase(String path, SQLiteDatabase.CursorFactory factory)方法建立資料庫。
1 SQLiteDatabase db =this.openOrCreateDatabase("test_db.db", Context.MODE_PRIVATE, null);
2 SQLiteDatabase db2 = SQLiteDatabase.openOrCreateDatabase("/data/data/com.test/databases/test_db2.db3", null);
如上兩種方式均能建立資料庫,this.openOrCreateDatabase是對SQLiteDatabase.openOrCreateDatabase而來,如代碼所見,原生的SQLiteDatabase.openOrCreateDatabase()方法第一參數要求輸入絕對路勁,而所有的資料庫都是儲存於“data/data/應用報名/databases”目錄下,所以輸入完全的絕對路勁是一件重複且繁雜的工作。採用this.openOrCreateDatabase則省去了此操作。執行操作後的結果如:
另外還可以通過寫一個繼承SQLiteOpenHelper類的方式建立資料庫,此種方式是一種更加進階的建立方式,所以在此不做描述。
二、建立資料表,插入資料。
Android系統並沒有提供特別的建立資料表的方法,資料表通過SQL語句建立,代碼如下:
1 db.execSQL("create table tab(_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)");
表建立好之後,通過insert(String table, String nullColumnHack, ContentValues values)方法插入資料,其中參數含義分別為:
table: 目標表名
nullColumnHack: 指定表中的某列列名。因為在SQLite中,不允許不允許插入所有列均為null的記錄,因此初始值有值為空白時,此列需顯式賦予null
values: ContentValues對象,類似於java中的Map。以索引值對的方式儲存資料。
資料插入代碼如下:
1 ContentValues values =new ContentValues();
2 for(int i=0;i<10;i++){
3 values.put("name", "test"+ i);
4 db.insert("tab", "_id", values);
5 }
執行此操作後,會新增一個名為“tab”的資料表,利用SQLite用戶端(推薦:SQLite Expert Personal 3)可輕鬆查看此表結構和資料。如:
三、修改資料
update(String table, ContentValues values, String whereClause, String[] whereArgs)方法用於修改資料,其四個參數的具體含義如下:
table: 目標表名
values: 要被修改成為的新值
whereClause: where子句,除去where關鍵字剩下的部分,其中可帶?預留位置。如沒有子句,則為null。
whereArgs: 用於替代whereClause參數中?預留位置的參數。如不需傳入參數,則為null。
資料修改代碼如下:
1 ContentValues values =new ContentValues();
2 values.put("name", "name");
3 db.update("tab", values, "_id=1", null);
4 db.update("tab", values, "_id=?", new String[]{"5"});
執行結果如,_id=1和_id=5的資料,name欄位的值被修改為了“name”。
四、查詢資料。
之前一直使用SQLite用戶端查看資料情況了,這裡就使用android提供的query()和rowQuery()方法執行查詢。具體代碼如下:
1 Cursor c = db.query("tab", null, null, null, null, null, null);
2 c.moveToFirst();
3 while(!c.isAfterLast()){
4 int index = c.getColumnIndex("name");
5 Log.d("SQLite", c.getString(index));
6 c.moveToNext();
7 }
8 c = db.rawQuery("select * from tab", null);
9 c.moveToFirst();
10 while(!c.isAfterLast()){
11 int index = c.getColumnIndex("name");
12 Log.d("SQLite", c.getString(index));
13 c.moveToNext();
14 }
查詢結果如:
可以清晰的在查詢結果中,紅線上下的資料是完全一致的,也就是說query和rawQuery方法在的不同僅僅在於所需參數的不同。rawQuery方法需要開發人員手動寫出查詢SQL,而query方法是由目標表名、where子句、order by子句、having子句等諸多子句由系統組成SQL語句。兩方法同返回Cursor對象,所以兩方在使用時孰優孰劣,就看具體情況了。本人更喜歡rawQuery的方式,因為此方式更接近傳統Java開發,也可以由專業DBA來書寫SQL語句,這樣更符合MVC的思想,而且這樣的代碼可讀性更高。(query方法裡面參數實在太多,有點記不住誰是order by子句,誰是having子句了)
Cursor對象可以理解為遊標對象,凡是對資料有所瞭解的人,相信對此對象都不會陌生,在這裡機不再累述。只提醒一點,在第一次讀取Cursor對象中的資料時,一定要先移動遊標,否則此遊標的位置在第一條記錄之前,會引發異常。
五、刪除資料
刪除資料也是一件很簡單的事,只需要調用delete方法,傳入參數即可,delete(String table, String whereClause, String[] whereArgs)的參數三個參數具體含義如下:
table: 目標表名
whereClause: where子句,除去where關鍵字剩下的部分,其中可帶?預留位置。如沒有子句,則為null。
whereArgs: 用於替代whereClause參數中?預留位置的參數。如不需傳入參數,則為null。
具體代碼如下:
db.delete("tab", "_id=? or name=?", new String[]{"8", "name"});
執行結果如下:
其中_id=8和name=‘name’的資料統統被刪除了。
整個資料庫的CRUD操作到此示範完了。最後提醒一點,在操作完資料後,一定要記得調用close()方法關閉串連,釋放資源。這個原因相信大家都是懂的。
SharedPreferences也是一種輕型的資料存放區方式,它的本質是基於XML檔案儲存體key-value索引值對資料,通常用來儲存一些簡單的配置資訊。其儲存位置在/data/data/<包名>/shared_prefs目錄下。SharedPreferences對象本身只能擷取資料而不支援儲存和修改,儲存修改是通過Editor對象實現。實現SharedPreferences儲存的步驟如下:
一、根據Context擷取SharedPreferences對象
二、利用edit()方法擷取Editor對象。
三、通過EditorObject Storage Servicekey-value索引值對資料。
四、通過commit()方法提交資料。
具體實現代碼如下:
1 publicclass MainActivity extends Activity {
2 @Override
3 publicvoid onCreate(Bundle savedInstanceState) {
4 super.onCreate(savedInstanceState);
5 setContentView(R.layout.main);
6
7 //擷取SharedPreferences對象
8 Context ctx = MainActivity.this;
9 SharedPreferences sp = ctx.getSharedPreferences("SP", MODE_PRIVATE);
10 //存入資料
11 Editor editor = sp.edit();
12 editor.putString("STRING_KEY", "string");
13 editor.putInt("INT_KEY", 0);
14 editor.putBoolean("BOOLEAN_KEY", true);
15 editor.commit();
16
17 //返回STRING_KEY的值
18 Log.d("SP", sp.getString("STRING_KEY", "none"));
19 //如果NOT_EXIST不存在,則傳回值為"none"
20 Log.d("SP", sp.getString("NOT_EXIST", "none"));
21 }
22 }
這段代碼執行過後,即在/data/data/com.test/shared_prefs目錄下產生了一個SP.xml檔案,一個應用可以建立多個這樣的xml檔案。:
SP.xml檔案的具體內容如下:
1 <?xml version=‘1.0‘ encoding=‘utf-8‘ standalone=‘yes‘ ?>
2 <map>
3 <string name="STRING_KEY">string</string>
4 <int name="INT_KEY" value="0"/>
5 <boolean name="BOOLEAN_KEY" value="true"/>
6 </map>
在程式碼中,通過getXXX方法,可以方便的獲得對應Key的Value值,如果key值錯誤或者此key無對應value值,SharedPreferences提供了一個賦予預設值的機會,以此保證程式的健壯性。如運行結果中因為並無值為"NOT_EXIST"的Key,所以Log列印出的是其預設值:“none”。在訪問一個不存在key值這個過程中,並無任何異常拋出。
SharedPreferences對象與SQLite資料庫相比,免去了建立資料庫,建立表,寫SQL語句等諸多操作,相對而言更加方便,簡潔。但是SharedPreferences也有其自身缺陷,比如其職能儲存boolean,int,float,long和String五種簡單的資料類型,比如其無法進行條件查詢等。所以不論SharedPreferences的資料存放區操作是如何簡單,它也只能是儲存方式的一種補充,而無法完全替代如SQLite資料庫這樣的其他資料存放區方式。
ContentProvider是Android平台中,在不同應用程式之間實現資料共用的一種機制。一個應用程式如果需要讓別的程式可以操作自己的資料,即可採用這種機制。並且此種方式忽略了底層的資料存放區實現,ContentProvider提供了一種統一的通過Uri實現資料操作的方式。其步驟為:
1. 在當前應用程式中定義一個ContentProvider。
2. 在當前應用程式的AndroidManifest.xml中註冊此ContentProvider
3. 其他應用程式通過ContentResolver和Uri來擷取此ContentProvider的資料。
ContentResolver提供了諸如insert(), delete(), query()和update()之類的方法。用於實現對ContentProvider中資料的存取操作。
Uri是一個通用資源標誌符,將其分為A,B,C,D 4個部分:
A:無法改變的標準首碼,包括;"content://"、"tel://"等。當首碼是"content://"時,說明通過一個Content Provider控制這些資料
B:URI的標識,它通過authorities屬性聲明,用於定義了是哪個ContentProvider提供這些資料。對於第三方應用程式,為了保證URI標識的唯一性,它必須是一個完整的、小寫 類名。例如;"content://com.test.data.myprovider"
C:路徑,可以近似的理解為需要操作的資料庫中表的名字,如:"content://hx.android.text.myprovider/name"中的name
D:如果URI中包含表示需要擷取的記錄的ID;則就返回該id對應的資料,如果沒有ID,就表示返回全部;
下面通過是程式碼範例,示範一下如何在應用之間相互擷取資料。
在應用程式A中,繼承ContProvider類,並重寫其中方法。
1 publicclass MyProvider extends ContentProvider{
2 @Override
3 publicint delete(Uri uri, String selection, String[] selectionArgs) {
4 // TODO Auto-generated method stub
5 return0;
6 }
7
8 @Override
9 public String getType(Uri uri) {
10 // TODO Auto-generated method stub
11 returnnull;
12 }
13
14 @Override
15 public Uri insert(Uri uri, ContentValues values) {
16 returnnull;
17 }
18
19 //在Create中初始化一個資料庫
20 @Override
21 publicboolean onCreate() {
22 SQLiteDatabase db =this.getContext().openOrCreateDatabase("test_db.db3", Context.MODE_PRIVATE, null);
23 db.execSQL("create table tab(_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)");
24 ContentValues values =new ContentValues();
25 values.put("name", "test");
26 db.insert("tab", "_id", values);
27 db.close();
28 returntrue;
29 }
30
31 //實現query方法
32 @Override
33 public Cursor query(Uri uri, String[] projection, String selection,
34 String[] selectionArgs, String sortOrder) {
35 SQLiteDatabase db =this.getContext().openOrCreateDatabase("test_db.db3", Context.MODE_PRIVATE, null);
36 Cursor c = db.query("tab", null, null, null, null, null,null);
37 return c;
38 }
39
40 @Override
41 publicint update(Uri uri, ContentValues values, String selection,
42 String[] selectionArgs) {
43 // TODO Auto-generated method stub
44 return0;
45 }
46 }
在其AndroidManifest.xml中聲明此ContentProvider,其中authorities屬性定義了此ContentProvider的Uri標識。
<provider android:name=".MyProvider" android:authorities="com.test.MyProvider"/>
在應用程式B中,通過ContentResolver擷取程式A的ContentProvider中的資料。
1 publicclass MainActivity extends Activity {
2 @Override
3 publicvoid onCreate(Bundle savedInstanceState) {
4 super.onCreate(savedInstanceState);
5 setContentView(R.layout.main);
6
7 //擷取上下文
8 Context ctx = MainActivity.this;
9 //擷取ContentResolver對象
10 ContentResolver resolver = ctx.getContentResolver();
11 //擷取Uri對象
12 Uri uri = Uri.parse("content://com.test.MyProvider");
13 //擷取資料
14 Cursor c = resolver.query(uri, null, null, null, null);
15 c.moveToFirst();
16 for(int i=0; i<c.getCount(); i++){
17 int index = c.getColumnIndexOrThrow("name");
18 String src = c.getString(index);
19 Log.d("", src);
20 c.moveToNext();
21 }
22 }
23 }
應用程式B的運行結果如下,從此圖可以發現我們在程式B中成功的擷取到了程式A中的資料:
再觀察兩個應用程式的結構,如,其中紅框是應用程式A的程式結構,可以清楚看到其有一個名為“test_db.db3”的資料庫,藍框是應用程式B的程式結構,其並沒有任何資料庫用於儲存資料。由此圖,可以確定應用程式B中查詢出來的資料結果是來自於應用程式A。
以上就是ContentProvider的使用方式,這種儲存方式相比SQLite和SharedPreferences,其複雜性是顯而易見的,但是在處處可見“雲”的今天,程式間的資料互動需求令ContentProvider儲存機制變成必不可少的一部分。
Android資料的四種儲存方式