前言 啦啦啦各位小伙伴們許久不見了~學期末和過年期間自己忙著做其他事沒能及時更新Android開發系列課程的博客,實在是罪過罪過~ 好啦~廢話不多說,進入我們今天的主題。今天我們將和大家學習其他的數據存儲的方法,一起來學習SQLite資料庫和ContentProvider的使用,複習Android界 ...
前言
啦啦啦各位小伙伴們許久不見了~學期末和過年期間自己忙著做其他事沒能及時更新Android開發系列課程的博客,實在是罪過罪過~
好啦~廢話不多說,進入我們今天的主題。今天我們將和大家學習其他的數據存儲的方法,一起來學習SQLite資料庫和ContentProvider的使用,複習Android界面編程等知識。
基礎知識
SQLite
一. SQLite的介紹
1.SQLite簡介
SQLite是一款輕型的資料庫,是遵守ACID的關聯式資料庫管理系統,它的設計目標是嵌入 式的,而且目前已經在很多嵌入式產品中使用了它,它占用資源非常的低,在嵌入式設備中,可能只需要幾百K的記憶體就夠了。它能夠支持 Windows/Linux/Unix等等主流的操作系統,同時能夠跟很多程式語言相結合,比如Tcl、PHP、Java、C++、.Net等,還有ODBC介面,同樣比起 Mysql、PostgreSQL這兩款開源世界著名的資料庫管理系統來講,它的處理速度比他們都快。
2.特點:
輕量級——SQLite和C/S模式的資料庫軟體不同,它是進程內的資料庫引擎,因此不存在資料庫的客戶端和伺服器。使用SQLite一般只需要帶上它的一個動態 庫,就可以享受它的全部功能。而且那個動態庫的尺寸也挺小,以版本3.6.11為例,Windows下487KB、Linux下347KB。
不需要"安裝"——SQLite的核心引擎本身不依賴第三方的軟體,使用它也不需要"安裝"。有點類似那種綠色軟體。
單一文件——資料庫中所有的信息(比如表、視圖等)都包含在一個文件內。這個文件可以自由複製到其它目錄或其它機器上。
跨平臺/可移植性——除了主流操作系統 windows,linux之後,SQLite還支持其它一些不常用的操作系統。
弱類型的欄位——同一列中的數據可以是不同類型。
開源
二. SQLiteDatabase的介紹
Android提供了創建和是用SQLite資料庫的API。SQLiteDatabase代表一個資料庫對象,提供了操作資料庫的一些方法。在Android的SDK目錄下有sqlite3工具,我們可以利用它創建資料庫、創建表和執行一些SQL語句。
1、打開或者創建資料庫
在Android 中使用SQLiteDatabase的靜態方法openOrCreateDatabase(String path,SQLiteDatabae.CursorFactory factory)打開或者創建一個資料庫。它會自動去檢測是否存在這個資料庫,如果存在則打開,不存在則創建一個資料庫;創建成功則返回一個SQLiteDatabase對象,否則拋出異常FileNotFoundException。
下麵是創建名為“stu.db”資料庫的代碼:
openOrCreateDatabase(String path,SQLiteDatabae.CursorFactory factory)
參數1 資料庫創建的路徑
參數2 一般設置為null就可以了
db=SQLiteDatabase.openOrCreateDatabase("/data/data/com.lingdududu.db/databases/stu.db",null);
2、創建表
創建一張表的步驟很簡單:
編寫創建表的SQL語句
調用SQLiteDatabase的execSQL()方法來執行SQL語句
下麵的代碼創建了一張用戶表,屬性列為:id(主鍵並且自動增加)、sname(學生姓名)、snumber(學號)
private void createTable(SQLiteDatabase db){
//創建表SQL語句
String stu_table="create table usertable(_id integer primary key autoincrement,sname text,snumber text)";
//執行SQL語句
db.execSQL(stu_table);
}
3、插入數據
插入數據有兩種方法:
①SQLiteDatabase的insert(String table,String nullColumnHack,ContentValues values)方法,
參數1 表名稱,
參數2 空列的預設值
參數3 ContentValues類型的一個封裝了列名稱和列值的Map;
②編寫插入數據的SQL語句,直接調用SQLiteDatabase的execSQL()方法來執行
第一種方法的代碼:
private void insert(SQLiteDatabase db){
//實例化常量值
ContentValues cValue = new ContentValues();
//添加用戶名
cValue.put("sname","xiaoming");
//添加密碼
cValue.put("snumber","01005");
//調用insert()方法插入數據
db.insert("stu_table",null,cValue);
}
第二種方法的代碼:
private void insert(SQLiteDatabase db){
//插入數據SQL語句
String stu_sql="insert into stu_table(sname,snumber) values('xiaoming','01005')";
//執行SQL語句
db.execSQL(sql);
}
4、實現增加、更新和刪除這 3 種操作有兩種方法:
不管是哪種方法,記得先 getWritableDatabase()
(a)用 execSQL 方法直接執行相應的 SQL 語句,比如增加(如下)。
SQLiteDatabase db = getWritableDatabase();
String insert_sql="INSERT INTO <表名>(<列 1>,<列 2>,…)values(<值 1>,<值 2>,…)";
db.execSQL(insert_sql);
(b)使用相應的 insert、update 和 delete 方法
I.insert 方法需要使用 ContentValues 來存放要添加的數據,見下圖
II.update 方法需要使用 ContentValues 和 Where 語句。(下圖只是說明性代碼)
III、delete 方法需要使用 where 語句。(下圖只是說明性代碼)
5、查詢數據
實現查詢操作可以使用 rawQuery 或 query 函數,它們的區別類似於上面,前者直接執行
SQL 語句,後者是通過參數組合產生 SQL 語句。(下圖只是說明性代碼)
進行查詢前,記得先 getReadableDatabase()
rawQuery 或者 query 函數返回的都是 Cursor,關於 Cursor 類的詳細介紹請看下麵
的鏈接:
http://www.cnblogs.com/TerryBlog/archive/2010/07/05/1771459.html
三. SQLiteOpenHelper
該類是SQLiteDatabase一個輔助類。這個類主要生成一 個資料庫,並對資料庫的版本進行管理。當在程式當中調用這個類的方法getWritableDatabase()或者 getReadableDatabase()方法的時候,如果當時沒有數據,那麼Android系統就會自動生成一個資料庫。 SQLiteOpenHelper 是一個抽象類,我們通常需要繼承它,並且實現裡面的3個函數:
1.onCreate(SQLiteDatabase)
在資料庫第一次生成的時候會調用這個方法,也就是說,只有在創建資料庫的時候才會調用,當然也有一些其它的情況,一般我們在這個方法裡邊生成資料庫表。
2. onUpgrade(SQLiteDatabase,int,int)
當資料庫需要升級的時候,Android系統會主動的調用這個方法。一般我們在這個方法裡邊刪除數據表,並建立新的數據表,當然是否還需要做其他的操作,完全取決於應用的需求。
3. onOpen(SQLiteDatabase):
這是當打開資料庫時的回調函數,一般在程式中不是很常使用。
Content Provider 使用
1.適用場景
1) ContentProvider為存儲和讀取數據提供了統一的介面
2) 使用ContentProvider,應用程式可以實現數據共用
3) android內置的許多數據都是使用ContentProvider形式,供開發者調用的(如視頻,音頻,圖片,通訊錄等)
2.ContentProvider簡介
當應用繼承ContentProvider類,並重寫該類用於提供數據和存儲數據的方法,就可以向其他應用共用其數據。雖然使用其他方法也可以對外共用數據,但數據訪問方式會因數據存儲的方式而不同,如:採用文件方式對外共用數據,需要進行文件操作讀寫數據;採用sharedpreferences共用數據,需要使用sharedpreferences API讀寫數據。而使用ContentProvider共用數據的好處是統一了數據訪問方式。
3.使用 getContentResolver 方法讀取聯繫人列表
Cursor cursor =getContentResolver().query(ContactsContract.Contacts. CONTENT_URI , null, null, null, null);
判斷某條聯繫人的信息中,是否有電話號碼。
int isHas =Integer. parseInt (cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts. HAS_PHONE_NUMBER )));
取出該條聯繫人信息中的電話號碼
Cursor c =getContentResolver().query(ContactsContract.CommonDataKinds.Phone. CON TENT_URI , null, ContactsContract.CommonDataKinds.Phone. CONTACT_ID + " = " + id, null, null); while (c.moveToNext()) { number +=c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Phone. N N UMBER )) + " "; } c.close();
自定義的對話框實現
使用 LayoutInflater 類,如下:
如此之後,我們就可以將一個 layout 的內容(dialoglayout)全都顯示在對話框
(builder)里了。之後便可以對 layout 裡面的元素進行操作了。
實驗內容
實現一個生日備忘簿
技術要求:
1、 使用 SQLite 資料庫保存生日的相關信息,使得每次運行程式都可以顯示出已經存 儲在資料庫里的內容;
2、 使用 ContentProvider 來獲取對應壽星的電話號碼;
功能要求:
1、 主界麵包含增加生日條目按鈕和生日信息列表;(見圖 1,圖 2)
2、 點擊<增加條目>按鈕跳轉到次界面;
3、 次界面輸入生日相關信息後點擊<增加>按鈕會返回主界面(同時更新主界面的生日 信息列表),且姓名欄位不能為空,姓名欄位不能重覆 (見圖 3,圖 4)
4、 主界面中的列表點擊事項處理:
a) 單擊(查看並可修改該生日條目):(見圖 5)
i. 彈出對話框,顯示該條目的相關信息,並提供修改。
ii. 同時,顯示該生日條目壽星的電話號碼;
iii. 點擊<保存修改>按鈕,更新主界面的生日信息列表
b) 長按(可刪除該生日條目):(見圖 6)
i. 彈出對話框,顯示是否刪除;
ii. 點擊<是>按鈕,刪除該生日條目,並更新主界面的生日信息列表
圖 1 首次啟動 圖 2 增加一些條目後
圖 3 名字不能重覆 圖 4 長按處理
圖 5 點擊處理 圖 6 名字不能為空
實驗過程
本次實驗主要是實現一個聯繫人生日備忘錄,實現數據存儲的功能。本次實驗主要涉及 SQLite 資料庫的使用以及在獲取通訊錄中聯繫人電話信息時的 ContentProvider 的使用。
首先,寫好幾個界面的 XML 佈局文件。這裡我們需要使用初始界面、增粘條 目界面、自定義對話框界面以及 listview 的 item 界面共四個 xml 佈局文件。需 要註意的是,為了保證列表標題欄和列表項能夠對齊,這裡將標題欄佈局和 listview 的 item 界面佈局中的三個 TextView 的 layout_weight 屬性比例設置 為 1:1:2。
接下來完成 MainActivity.java 類,在初始化控制項後,主要完成幾個點擊按 鈕的事件。單擊增加條目按鈕,我們將跳轉到AddActivity.java 的界面中:
並設置了一個退出按鈕:
在增加了 listview 的條目之後,單擊 listview 我們將獲取手機中的通訊錄 中的聯繫人電話信息,並跳轉到自定義對話框中進行編輯。
獲取手機中的通訊錄中的聯繫人電話信息需要使用 Content Provider。首先我們在 AndroidManifest.xml 文件里聲明讀取通訊錄的許可權(這裡具體的方 法在“實驗中遇到的問題及解決方法”一欄中詳述,這裡就不加贅述)。然後使 用 getContentResolver 方法讀取聯繫人列表,並判斷某條聯繫人的信息中,是 否有電話號碼,若存在相應的聯繫人號碼,則取出該條聯繫人信息中的電話號碼; 如果手機通訊錄中沒有對應的聯繫人則將手機設為無:
自定義的對話框實現需要使用 LayoutInflater 類:
之後,我們就可以將一個 layout 的內容(dialoglayout)全都顯示在對話框(builder)里,然後便可以對 layout 裡面的元素進行操作。
在自定義對話框中,我們需要按照要求進行設置:
除了這些以外,我們還需要實時更新 UI,這裡,我們定義一個狀態更新的 函數,在需要調用的時候進行調用進行實時更新(同時也進行資料庫內容的更新):
在 AddActivity 類中,我們主要還是完成各個按鈕的事件。 在增加條目的按鈕事件中,我們需要根據需要輸出相應的 Toast 信息(當編輯框為空時,輸出相應的 Toast 信息;當姓名編輯框與資料庫中已存在的數據相 同時,輸出相應的 Toast 信息):
還有清除和取消按鈕,這裡就不加贅述。
在我們這次實驗的 主要內容資料庫中 ( MyDataBase 類中 ) , 使 用 SQLiteOpenHelper 的子類能更方便實現要求。首先我們需要創建類:
創建資料庫,可直接執行創建資料庫的 SQl 語句:
在對應的位置使用相應的 insert、update 和 delete 方法,完成實現增加、更新和刪除這3種操作 。 這裡, 我們先getWritableDatabase(),用 execSQL方法直接執行相應的SQL語句(實現查 詢操作使用 rawQuery):
完成實驗~
實驗截圖
知識總結
SQLite 簡介:
SQLite 是一個非常流行的嵌入式資料庫,它支持 SQL 語言,並且只利用很 少的記憶體就有很好的性能。此外它還是開源的,任何人都可以使用它。
(1)Android 開發中使用 SQLite 資料庫
Activities 可以通過 Content Provider 或者 Service 訪問一個資料庫。 創建資料庫
Android 不自動提供資料庫。在 Android 應用程式中使用 SQLite,必須自 己創建資料庫,然後創建表、索引,填充數據。Android 提供了 SQLiteOpenHelper 幫助你創建一個資料庫,你只要繼承 SQLiteOpenHelper 類,就可以輕鬆的創建
資料庫。SQLiteOpenHelper 類根據開發應用程式的需要,封裝了創建和更新數 據庫使用的邏輯。SQLiteOpenHelper 的子類,至少需要實現三個方法:
a.構造函數
調用父類 SQLiteOpenHelper 的構造函數。這個方法需要四個參數:上下文 環境(例如,一個 Activity),資料庫名字,一個可選的游標工廠(通常是 Null), 一個代表你正在使用的資料庫模型版本的整數。
b.onCreate()方法
它需要一個 SQLiteDatabase 對象作為參數,根據需要對這個對象填充表和 初始化數據。
c.onUpgrage() 方法
它需要三個參數,一個 SQLiteDatabase 對象,一個舊的版本號和一個新的 版本號,這樣你就可以清楚如何把一個資料庫從舊的模型轉變到新的模型。
(2)調用 getReadableDatabase() 或 getWriteableDatabase() 方法,你可以 得到 SQLiteDatabase 實例,具體調用那個方法,取決於你是否需要改變資料庫 的內容:
db=(new DatabaseHelper(getContext())).getWritableDatabase();
return (db == null) ? false : true;
上面這段代碼會返回一個 SQLiteDatabase 類的實例,使用這個對象,你就 可以查詢或者修改資料庫。
當完成了對資料庫的操作(例如 Activity 已經關閉),需要調用 SQLiteDatabase 的 Close() 方法來釋放掉資料庫連接。
(3)創建表和索引
為了創建表和索引,需要調用 SQLiteDatabase 的 execSQL() 方法來執行 DDL 語句。如果沒有異常,這個方法沒有返回值。
SQLite 會自動為主鍵列創建索引。 通常情況下,第一次創建資料庫時創建了表和索引。如果你不需要改變表的
schema,不需要刪除表和索引 . 刪除表和索引,需要使用 execSQL() 方法調用 DROP INDEX 和 DROP TABLE 語句。
(4)給表添加數據
有兩種方法可以給表添加數據。
像上面創建表一樣,你可以使用 execSQL() 方法執行 INSERT, UPDATE, DELETE 等語句來更新表的數據。execSQL() 方法適用於所有不返回結果的 SQL 語 句 。 另 一 種 方 法 是 使 用 SQLiteDatabase 對 象 的 insert(), update(), delete() 方法。
ContentProvider 簡介:
當應用繼承 ContentProvider 類,並重寫該類用於提供數據和存儲數據的方 法,就可以向其他應用共用其數據。雖然使用其他方法也可以對外共用數據,但 數據訪問方式會因數據存儲的方式而不同,如:採用文件方式對外共用數據,需 要進行文件操作讀寫數據;採用 sharedpreferences 共用數據, 需要使用 sharedpreferences API 讀寫數據。而使用 ContentProvider 共用數據的好處是 統一了數據訪問方式。
適用場景
1)ContentProvider 為存儲和讀取數據提供了統一的介面
2)使用 ContentProvider,應用程式可以實現數據共用
3)android 內置的許多數據都是使用 ContentProvider 形式,供開發者調用 的(如視頻,音頻,圖片,通訊錄等)
創建ContentProvider
要創建我們自己的Content Provider的話,我們需要遵循以下幾步:
a. 創建一個繼承了ContentProvider父類的類
b. 定義一個名為CONTENT_URI,並且是public static final的Uri類型的類變數,你必須為其指定一個唯一的字元串值,最好的方案是以類的全名稱, 如:
public static final Uri CONTENT_URI = Uri.parse( “content://com.google.android.MyContentProvider”);
c. 定義你要返回給客戶端的數據列名。如果你正在使用Android資料庫,必須為其定義一個叫_id的列,它用來表示每條記錄的唯一性。
d. 創建你的數據存儲系統。大多數Content Provider使用Android文件系統或SQLite資料庫來保持數據,但是你也可以以任何你想要的方式來存儲。
e. 如果你要存儲位元組型數據,比如點陣圖文件等,數據列其實是一個表示實際保存文件的URI字元串,通過它來讀取對應的文件數據。處理這種數據類型的Content Provider需要實現一個名為_data的欄位,_data欄位列出了該文件在Android文件系統上的精確路徑。這個欄位不僅是供客戶端使用,而且也可以供ContentResolver使用。客戶端可以調用ContentResolver.openOutputStream()方法來處理該URI指向的文件資源;如果是ContentResolver本身的話,由於其持有的許可權比客戶端要高,所以它能直接訪問該數據文件。
f. 聲明public static String型的變數,用於指定要從游標處返回的數據列。
g. 查詢返回一個Cursor類型的對象。所有執行寫操作的方法如insert(), update() 以及delete()都將被監聽。我們可以通過使用ContentResover().notifyChange()方法來通知監聽器關於數據更新的信息。
h. 在AndroidMenifest.xml中使用<provider>標簽來設置Content Provider。
源碼下載
源碼下載點擊這裡~
註
1、本實驗實驗環境:
操作系統 Windows 10
實驗軟體 Android Studio 2.2.1
虛擬設備:Nexus_5X
API:23
2、貼代碼的時候由於插入代碼框的大小問題,代碼格式不太嚴整,望見諒~