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對象。

  三、通過Editor對象存儲key-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存儲機制變成必不可少的一部分。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章