編輯:Android開發實例
前言
對於一個應用程序而言,數據持久化是必不可少的,Android程序也不例外。這篇博客將介紹Android中關於SQLite的使用,SQLite是一種嵌入式的數據庫引擎,專門適用於資源有限的設備上進行適量的數據存儲,而Android就全面支持標准的SQLite數據庫。在本片博客中,將說明SQLite數據庫的創建以及維護,還有使用SQLite執行CRUD的兩種方式,以及SQLite中事務的使用,最後都會使用示例講解文章中所提到的概念性的內容。
SQLite
Android對SQLite數據庫,提供了完全的支持,而所有創建的SQLite數據庫,僅限於當前應用訪問,如果其他應用需要訪問,則必須提供的Content Provider的支持,並且SQLite數據庫會隨著Android應用的卸載而被刪除。SQLite是一個嵌入式的數據庫引擎,最後是以文件的形式保存數據的。從本質上來看,SQLite的操作方式只是一種更為便捷的文件操作,當應用程序創建或打開一個SQLite數據庫時,其實只是打開一個文件准備讀寫。因為SQLite僅適用於資源有限的小型設備,所以本身就不應該把大量數據存儲在設備的SQLite數據庫裡,SQLite只適合存儲一些小型的數據。
為了使SQLite和其他數據庫間的兼容性最大化,SQLite支持對列上類型進行“類型近似”,列的類型近似指的是存儲在列上的數據進行推薦類型存儲。所以雖然SQLite內部只支持NULL、INTEGER、REAL(浮點書)、TEXT(文本)和BLOB(大二進制對象)這五種數據類型,但實際上SQLite完全可以接受varchar(n)、char(n)、decimal(p,s)、date等類型數據,只不過SQLite會在運算或保存時將它們轉換為上面五種數據類型中相應的類型。大多數數據庫的引擎都是使用靜態的、強類型的數據類型,數據的類型是由它的容器決定的,這個容器是指被存放的特定列。而SQLite使用的是動態類型,在SQLite中,值的數據類型跟值本身相關,而不是與它的容器相關,所以SQLite允許把各種類型的數據保存到任何類型字段中,開發者可以不用關心聲明該字段說使用的數據類型。但是有一種情況例外,定義為INTEGER PRIMARY KEY的字段只能存儲64位整數,當向這種字段保存除整數意外的其他類型的數據時,SQLite會產生錯誤。
SQLite數據庫創建與維護
從官方文檔上了解到,在Android項目中,創建SQLite數據庫推薦繼承SQLiteOpenHelper類,然後重寫其中的onCreate()方法,在onCreate()方法中,對執行數據庫創建的SQL語句。而SQLiteOpenHelper不僅僅用於SQLite數據的創建,還可以對其進行維護,以及獲得SQLiteDatabase這個數據庫操作對象。
SQLiteOpenHelper提供了兩個構造器,用於傳遞當前上下文對象以及SQLite數據庫版本信息,在SQLiteOpenHelper的繼承類的構造函數中,會調用它,構造器的簽名如下:
上面的構造函數中,都是用於創建一個SQLite數據庫,context為一個當前應用的上下文對象;name是數據庫名稱;factory是一個允許子類在查詢時使用的游標,一般不用傳Null;version是數據庫版本號;errorHandler是一個接口,傳遞當數據庫錯誤的時候,執行的補救方法。
在SQLiteOpenHelper中,可以進行SQLite數據庫的創建、維護、日志以及獲取可讀寫的數據庫對象,通過下面幾個常用方法得到支持:
下面提供一個簡單的SQLiteOpenHelper的繼承類代碼,用於創建數據庫以及表結構:
- package com.example.sqlitedbdemo.db;
- import android.content.Context;
- import android.database.sqlite.SQLiteDatabase;
- import android.database.sqlite.SQLiteOpenHelper;
- public class DbOpenHelper extends SQLiteOpenHelper {
- private static String name = "mydb.db";
- private static int version = 1;
- public DbOpenHelper(Context context) {
- super(context, name, null, version);
- }
- @Override
- public void onCreate(SQLiteDatabase db) {
- // 只能支持基本數據類型
- String sql = "create table person(id integer primary key autoincrement,name varchar(64),address varchar(64))";
- db.execSQL(sql);
- }
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- // TODO Auto-generated method stub
- String sql="alter table person add sex varchar(8)";
- db.execSQL(sql);
- }
- }
Tips:當創建好SQLite數據庫的之後,可以在/data/data/<package name>/databases目錄下找到SQLite數據庫文件。
執行CRUD操作
當使用SQLiteOpenHelper的getReadableDatabase()或者getWritableDatabase()方法獲取到SQLiteDatabase對象,就可以對這個數據庫進行操作了。
對於熟悉SQL語句的開發者而言,其實只需要使用兩個方法,即可執行所有CRUD操作,以下方法提供多個重載方法:
下面以一個示例講解一下單純使用SQL語句實現CRUD操作:
接口代碼:
- package com.examle.sqlitedbdemo.service;
- import java.util.List;
- import java.util.Map;
- public interface PersonService {
- public boolean addPerson(Object[] params);
- public boolean deletePerson(Object[] params);
- public boolean updatePerson(Object[] params);
- public Map<String, String> viewPerson(String[] selectionArgs);
- public List<Map<String, String>> listPersonMaps(String[] selectionArgs);
- }
接口的實現代碼:
- package com.examle.sqlitedbdemo.dao;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import android.content.Context;
- import android.database.Cursor;
- import android.database.sqlite.SQLiteDatabase;
- import com.examle.sqlitedbdemo.service.PersonService;
- import com.example.sqlitedbdemo.db.DbOpenHelper;
- public class PersonDao implements PersonService {
- private DbOpenHelper helper = null;
- public PersonDao(Context context) {
- helper = new DbOpenHelper(context);
- }
- @Override
- public boolean addPerson(Object[] params) {
- boolean flag = false;
- SQLiteDatabase database = null;
- try {
- // insert一條數據
- String sql = "insert into person(name,address,sex) values(?,?,?)";
- database = helper.getWritableDatabase();
- // 執行SQL
- database.execSQL(sql, params);
- flag = true;
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- if (database != null) {
- // finally中關閉數據庫
- database.close();
- }
- }
- return flag;
- }
- @Override
- public boolean deletePerson(Object[] params) {
- boolean flag = false;
- SQLiteDatabase database = null;
- try {
- // 刪除一條數據
- String sql = "delete from person where id=?";
- database = helper.getWritableDatabase();
- database.execSQL(sql, params);
- flag = true;
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- if (database != null) {
- database.close();
- }
- }
- return flag;
- }
- @Override
- public boolean updatePerson(Object[] params) {
- boolean flag = false;
- SQLiteDatabase database = null;
- try {
- // 更新一條數據
- String sql = "update person set name=?,address=?,sex=? where id=?";
- database = helper.getWritableDatabase();
- // 執行SQL
- database.execSQL(sql, params);
- flag = true;
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- if (database != null) {
- database.close();
- }
- }
- return flag;
- }
- @Override
- public Map<String, String> viewPerson(String[] selectionArgs) {
- Map<String, String> map = new HashMap<String, String>();
- SQLiteDatabase database = null;
- try {
- // 查詢單條記錄
- String sql = "select * from person where id=?";
- // 以只讀的形式打開數據庫
- database = helper.getReadableDatabase();
- // 執行SQL語句,返回一個游標
- Cursor cursor = database.rawQuery(sql, selectionArgs);
- int colums = cursor.getColumnCount();
- while (cursor.moveToNext()) {
- for (int i = 0; i < colums; i++) {
- String cols_name = cursor.getColumnName(i);
- String cols_value = cursor.getString(cursor
- .getColumnIndex(cols_name));
- if (cols_value == null) {
- cols_value = "";
- }
- map.put(cols_name, cols_value);
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- if (database != null) {
- database.close();
- }
- }
- return map;
- }
- @Override
- public List<Map<String, String>> listPersonMaps(String[] selectionArgs) {
- List<Map<String, String>> list = new ArrayList<Map<String, String>>();
- String sql = "select * from person";
- SQLiteDatabase database = null;
- try {
- database = helper.getReadableDatabase();
- Cursor cursor = database.rawQuery(sql, selectionArgs);
- int colums = cursor.getColumnCount();
- while (cursor.moveToNext()) {
- Map<String, String> map = new HashMap<String, String>();
- for (int i = 0; i < colums; i++) {
- String cols_name = cursor.getColumnName(i);
- String cols_value = cursor.getString(cursor
- .getColumnIndex(cols_name));
- if (cols_value == null) {
- cols_value = "";
- }
- map.put(cols_name, cols_value);
- }
- list.add(map);
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- if (database != null) {
- database.close();
- }
- }
- return list;
- }
- }
再寫一個測試類測試這個數據操作類是否有效,Android下JUnit的配置參見另外一篇博客:Android--JUnit單元測試:
- package com.example.sqlitedbdemo.db;
- import java.util.List;
- import java.util.Map;
- import com.examle.sqlitedbdemo.dao.PersonDao;
- import com.examle.sqlitedbdemo.service.PersonService;
- import android.test.AndroidTestCase;
- import android.util.Log;
- public class TestDb extends AndroidTestCase {
- private final String TAG = "main";
- public TestDb() {
- // TODO Auto-generated constructor stub
- }
- public void createDB() {
- DbOpenHelper helper = new DbOpenHelper(getContext());
- helper.getWritableDatabase();
- }
- public void insertDb() {
- PersonService service = new PersonDao(getContext());
- Object[] params1 = { "張龍", "beijing", "male" };
- boolean flag = service.addPerson(params1);
- Object[] params2 = { "趙虎", "shanghai", "male" };
- flag = flag&&service.addPerson(params2);
- Object[] params3 = { "王朝", "HK", "male" };
- flag = flag&&service.addPerson(params3);
- Object[] params4 = { "馬漢", "beijing", "female" };
- flag = flag&&service.addPerson(params4);
- Log.i(TAG, "-----插入數據----->>" + flag);
- }
- public void deleteDb() {
- PersonService service = new PersonDao(getContext());
- Object[] params = { 1 };
- boolean flag = service.deletePerson(params);
- Log.i(TAG, "-----刪除數據----->>" + flag);
- }
- public void updateDb() {
- PersonService service=new PersonDao(getContext());
- Object[] params = { "張三", "上海", "男","2" };
- boolean flag=service.updatePerson(params);
- Log.i(TAG, "---------->>" + flag);
- }
- public void getDb(){
- PersonService service=new PersonDao(getContext());
- Map<String, String> map = service.viewPerson(new String[]{"2"});
- Log.i(TAG, "---------->>" + map.toString());
- }
- public void listDb() {
- PersonService service = new PersonDao(getContext());
- List<Map<String, String>> list = service.listPersonMaps(null);
- Log.i(TAG, "---------->>" + list.toString());
- }
- }
insertDB()後,如果是在模擬器上調試,可以使用FIle Explorer工具導出mydb.db文件,使用SQLite Expert Professional(這是一個SQLite的管理軟件,博客最後提供下載地址),打開數據庫:
執行deleteDb()刪除第一條數據:
執行updateDb()更新第二條數據:
執行getDb(),查詢第二條數據,執行listDb(),查詢全部數據,查看日志輸出:
而如果是從事Android開發,還有必要了解另外一種操作SQLite的方式,使用SQLiteDatabase所提供的方法實現CRUD操作。主要有以下幾個方法:
下面以一個示例程序講解一下使用SQLiteDatabase所提供的方法實現CRUD操作:
接口代碼:
- package com.examle.sqlitedbdemo.service;
- import java.util.List;
- import java.util.Map;
- import android.content.ContentValues;
- public interface PersonService2 {
- public boolean addPerson(ContentValues values);
- public boolean deletePerson(String whereClause, String[] whereArgs);
- public boolean updatePerson(ContentValues values, String whereClause,
- String[] whereArgs);
- public Map<String, String> viewPerson(String selection,
- String[] selectionArgs);
- public List<Map<String, String>> listPersonMaps(String selection,
- String[] selectionArgs);
- }
實現代碼:
- package com.examle.sqlitedbdemo.dao;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import android.content.ContentValues;
- import android.content.Context;
- import android.database.Cursor;
- import android.database.sqlite.SQLiteDatabase;
- import com.examle.sqlitedbdemo.service.PersonService2;
- import com.example.sqlitedbdemo.db.DbOpenHelper;
- public class PersonDao2 implements PersonService2 {
- private DbOpenHelper helper = null;
- public PersonDao2(Context context) {
- helper = new DbOpenHelper(context);
- }
- @Override
- public boolean addPerson(ContentValues values) {
- boolean flag = false;
- SQLiteDatabase database = null;
- long id = -1;
- try {
- database = helper.getWritableDatabase();
- // 執行insert,返回當前行ID
- id = database.insert("person", null, values);
- flag = (id != -1 ? true : false);
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- if (database != null) {
- database.close();
- }
- }
- return flag;
- }
- @Override
- public boolean deletePerson(String whereClause, String[] whereArgs) {
- boolean flag = false;
- SQLiteDatabase database = null;
- int count = 0;
- try {
- database = helper.getWritableDatabase();
- // 執行刪除操作,返回影響行數
- count = database.delete("person", whereClause, whereArgs);
- flag = (count > 0 ? true : false);
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- if (database != null) {
- database.close();
- }
- }
- return flag;
- }
- @Override
- public boolean updatePerson(ContentValues values, String whereClause,
- String[] whereArgs) {
- boolean flag = false;
- SQLiteDatabase database = null;
- int count = 0;
- try {
- database = helper.getWritableDatabase();
- // 執行更新操作,返回影響行數
- count = database.update("person", values, whereClause, whereArgs);
- flag = (count > 0 ? true : false);
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- if (database != null) {
- database.close();
- }
- }
- return flag;
- }
- @Override
- public Map<String, String> viewPerson(String selection,
- String[] selectionArgs) {
- SQLiteDatabase database = null;
- Cursor cursor = null;
- Map<String, String> map = new HashMap<String, String>();
- try {
- database = helper.getReadableDatabase();
- // 設置查詢條件
- cursor = database.query(true, "person", null, selection,
- selectionArgs, null, null, null, null);
- int cols_len = cursor.getColumnCount();
- while (cursor.moveToNext()) {
- for (int i = 0; i < cols_len; i++) {
- String cols_key = cursor.getColumnName(i);
- String cols_value = cursor.getString(cursor
- .getColumnIndex(cols_key));
- if (cols_value == null) {
- cols_value = "";
- }
- map.put(cols_key, cols_value);
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return map;
- }
- @Override
- public List<Map<String, String>> listPersonMaps(String selection,
- String[] selectionArgs) {
- List<Map<String, String>> list = new ArrayList<Map<String, String>>();
- SQLiteDatabase database = null;
- Cursor cursor = null;
- try {
- database = helper.getReadableDatabase();
- cursor = database.query(false, "person", null, selection,
- selectionArgs, null, null, null, null);
- int cols_len = cursor.getColumnCount();
- while (cursor.moveToNext()) {
- Map<String, String> map = new HashMap<String, String>();
- for (int i = 0; i < cols_len; i++) {
- String cols_key = cursor.getColumnName(i);
- String cols_value = cursor.getString(cursor
- .getColumnIndex(cols_key));
- if (cols_value == null) {
- cols_value = "";
- }
- map.put(cols_key, cols_value);
- }
- list.add(map);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return list;
- }
- }
最後和上面一下,創建一個測試類來測試這個數據庫操作:
- package com.example.sqlitedbdemo.db;
- import java.util.List;
- import java.util.Map;
- import com.examle.sqlitedbdemo.dao.PersonDao2;
- import com.examle.sqlitedbdemo.service.PersonService2;
- import android.content.ContentValues;
- import android.test.AndroidTestCase;
- import android.util.Log;
- public class TestDb2 extends AndroidTestCase {
- private final String TAG = "main";
- public TestDb2() {
- // TODO Auto-generated constructor stub
- }
- public void addPerson() {
- PersonService2 service2 = new PersonDao2(getContext());
- ContentValues values1 = new ContentValues();
- values1.put("name", "張龍");
- values1.put("address", "beijing");
- values1.put("sex", "male");
- boolean flag = service2.addPerson(values1);
- ContentValues values2 = new ContentValues();
- values2.put("name", "趙虎");
- values2.put("address", "shanghai");
- values2.put("sex", "male");
- flag = flag&&service2.addPerson(values2);
- ContentValues values3 = new ContentValues();
- values3.put("name", "王朝");
- values3.put("address", "HK");
- values3.put("sex", "male");
- flag = flag&&service2.addPerson(values3);
- ContentValues values4 = new ContentValues();
- values4.put("name", "王朝");
- values4.put("address", "HK");
- values4.put("sex", "male");
- flag = flag&&service2.addPerson(values4);
- Log.i(TAG, "----------->>" + flag);
- }
- public void deletePerson() {
- PersonService2 service2 = new PersonDao2(getContext());
- boolean flag = service2.deletePerson(" id =?", new String[]{"1"});
- Log.i(TAG, "----------->>" + flag);
- }
- public void updatePerson(){
- PersonService2 service2 = new PersonDao2(getContext());
- ContentValues values = new ContentValues();
- values.put("name", "張三");
- values.put("address", "上海");
- values.put("sex", "男");
- boolean flag=service2.updatePerson(values, " id=? ", new String[]{"2"});
- Log.i(TAG, "----------->>" + flag);
- }
- public void viewPerson(){
- PersonService2 service2 = new PersonDao2(getContext());
- Map<String, String> map=service2.viewPerson(" id=? ", new String[]{"2"});
- Log.i(TAG, "----------->>" + map.toString());
- }
- public void listPerson(){
- PersonService2 service2 = new PersonDao2(getContext());
- List<Map<String, String>> list=service2.listPersonMaps(null,null);
- Log.i(TAG, "----------->>" + list.toString());
- }
- }
實現的功能和上面一樣,這裡就不展示效果圖了,但是因為是上面兩種操作數據庫的方式是在一個應用中完成的,並且數據一樣,執行第二個測試類的時候,需要把之前創建的數據庫刪除,詳情參見源碼。
SQLite事務
SQLite的事務通過SQLiteDatabase中包含的兩個方法對其進行控制:beginTransaction(),開始事務;endTransaction(),結束事務。除此之外,SQLiteDatabase還提供了一個inTransaction()方法用來判斷當前上下文是否處於事務環境中。當程序執行endTransaction()方法時將會結束事務,到底是回滾事務還是提交事務取決於SQLiteDatabase是否調用了setTransactionSuccessful()方法來設置事務標志,如果程序事務執行中調用該方法設置了事務成功則提交事務,否則程序將回滾事務。
示例源碼下載
總結
上面就基本講解了SQLite在Android中的時候,雖然有兩種操作方式,並且直接使用SQL語句操作數據庫對於熟悉SQL語句的開發者開說,是非常貼心的,但是在Android中,還是有必要了解一下使用SQLiteDatabase提供的方法操作數據庫的方式,因為Android有一個內容提供者(Content Provider),可以使用外部應用訪問內部應用的數據,它傳遞數據的形式,大部分是與SQLiteDatabase內置方法的參數一致的,所以如果同一使用SQLiteDatabase提供的方法操作數據庫,是很方便的,無需額外轉換SQL語句。
SQLiteExpertSetup下載地址:part1、part2。
作為Android應用開發者,不得不面對一個尴尬的局面,就是自己辛辛苦苦開發的應用可以被別人很輕易的就反編譯出來。Google似乎也發現了這個問題,從SDK2.3
在工作中又很多需求都不是android系統自帶的控件可以達到效果的,內置的TabHost就是,只能達到簡單的效果 ,所以這個時候就要自定義控件來達到效果:這個效果
Android應用程序可以在許多不同地區的許多設備上運行。為了使應用程序更具交互性,應用程序應該處理以適合應用程序將要使用的語言環境方面的文字,數字,文件等。在本章中,我
JSON代表JavaScript對象符號。它是一個獨立的數據交換格式,是XML的最佳替代品。本章介紹了如何解析JSON文件,並從中提取所需的信息。Android提供了四個