前言
啦啦啦各位小伙伴們許久不見了~學期末和過年期間自己忙着做其他事沒能及時更新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、貼代碼的時候由於插入代碼框的大小問題,代碼格式不太嚴整,望見諒~
