編輯:Android技術基礎
本節我們繼續來學習Android數據存儲與訪問的第三種方式:SQLite數據庫,和其他的SQL數據庫不同, 我們並不需要在手機上另外安裝一個數據庫軟件,Android系統已經集成了這個數據庫,我們無需像 使用其他數據庫軟件(Oracle,MSSQL,MySql等)又要安裝,然後完成相關配置,又要改端口之類的! 引言就說這麼多,接下來我們來學習下這個東西~
答:下面請聽小豬娓娓道來:
①SQLite是一個輕量級的關系型數據庫,運算速度快,占用資源少,很適合在移動設備上使用, 不僅支持標准SQL語法,還遵循ACID(數據庫事務)原則,無需賬號,使用起來非常方便!
②前面我們學習了使用文件與SharedPreference來保存數據,但是在很多情況下, 文件並不一定是有效的,如多線程並發訪問是相關的;app要處理可能變化的復雜數據結構等等! 比如銀行的存錢與取錢!使用前兩者就會顯得很無力或者繁瑣,數據庫的出現可以解決這種問題, 而Android又給我們提供了這樣一個輕量級的SQLite,為何不用?
③SQLite支持五種數據類型:NULL,INTEGER,REAL(浮點數),TEXT(字符串文本)和BLOB(二進制對象) 雖然只有五種,但是對於varchar,char等其他數據類型都是可以保存的;因為SQLite有個最大的特點: 你可以各種數據類型的數據保存到任何字段中而不用關心字段聲明的數據類型是什麼,比如你 可以在Integer類型的字段中存放字符串,當然除了聲明為主鍵INTEGER PRIMARY KEY的字段只能夠存儲64位整數! 另外, SQLite 在解析CREATE TABLE 語句時, 會忽略 CREATE TABLE 語句中跟在字段名後面的數據類型信息如下面語句會忽略 name字段的類型信息: CREATE TABLE person (personid integer primary key autoincrement, name varchar(20))
小結下特點:
SQlite通過文件來保存數據庫,一個文件就是一個數據庫,數據庫中又包含多個表格,表格裡又有 多條記錄,每個記錄由多個字段構成,每個字段有對應的值,每個值我們可以指定類型,也可以不指定 類型(主鍵除外)
PS:對了,Android內置的SQLite是SQLite 3版本的~
嘿嘿,學習一些新東西的時候,最不喜歡的莫過於遇到一些新名詞,是吧,我們先來說下幾個 我們在使用數據庫時用到的三個類:
- SQLiteOpenHelper:抽象類,我們通過繼承該類,然後重寫數據庫創建以及更新的方法, 我們還可以通過該類的對象獲得數據庫實例,或者關閉數據庫!
- SQLiteDatabase:數據庫訪問類:我們可以通過該類的對象來對數據庫做一些增刪改查的操作
- Cursor:游標,有點類似於JDBC裡的resultset,結果集!可以簡單理解為指向數據庫中某 一個記錄的指針!
對於涉及數據庫的app,我們不可能手動地去給他創建數據庫文件,所以需要在第一次啟用app 的時候就創建好數據庫表;而當我們的應用進行升級需要修改數據庫表的結構時,這個時候就需要 對數據庫表進行更新了;對於這兩個操作,安卓給我們提供了SQLiteOpenHelper的兩個方法, onCreate( )與onUpgrade( )來實現
方法解析:
- onCreate(database):首次使用軟件時生成數據庫表
- onUpgrade(database,oldVersion,newVersion):在數據庫的版本發生變化時會被調用, 一般在軟件升級時才需改變版本號,而數據庫的版本是由程序員控制的,假設數據庫現在的 版本是1,由於業務的變更,修改了數據庫表結構,這時候就需要升級軟件,升級軟件時希望 更新用戶手機裡的數據庫表結構,為了實現這一目的,可以把原來的數據庫版本設置為2 或者其他與舊版本號不同的數字即可!
代碼示例:
public class MyDBOpenHelper extends SQLiteOpenHelper { public MyDBOpenHelper(Context context, String name, CursorFactory factory, int version) {super(context, "my.db", null, 1); } @Override //數據庫第一次創建時被調用 public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE person(personid INTEGER PRIMARY KEY AUTOINCREMENT,name VARCHAR(20))"); } //軟件版本號發生改變時調用 @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("ALTER TABLE person ADD phone VARCHAR(12) NULL"); } }
代碼解析:
上述代碼第一次啟動應用,我們會創建這個my.db的文件,並且會執行onCreate()裡的方法, 創建一個Person的表,他又兩個字段,主鍵personId和name字段;接著如我我們修改db的版本 號,那麼下次啟動就會調用onUpgrade()裡的方法,往表中再插入一個字段!另外這裡是插入 一個字段,所以數據不會丟失,如果是重建表的話,表中的數據會全部丟失,下一節我們會 來教大家如何解決這個問題!
流程小結:
- Step 1:自定義一個類繼承SQLiteOpenHelper類
- Step 2:在該類的構造方法的super中設置好要創建的數據庫名,版本號
- Step 3:重寫onCreate( )方法創建表結構
- Step 4:重寫onUpgrade( )方法定義版本號發生改變後執行的操作
當我們調用上面的MyDBOpenhelper的對象的getWritableDatabase()就會在下述目錄下創建我們的db 數據庫文件:
我們發現數據庫有兩個,前者是我們創建的數據庫,而後者則是為了能讓數據庫支持事務而產生的 臨時的日志文件!一般的大小是0字節! 而在File Explorer裡我們確是打不開文件的,連txt都打不開,何況是.db! 所以下面給大家兩條路選:
- 1.先導出來,然後用SQLite的圖形化工具查看
- 2.配置adb環境變量後,通過adb shell來查看(命令行,裝比利器)!
嗯,接著給大家演示上述兩種方法,選自己喜歡的一種就可以了~~
這類軟件有很多,筆者用的是SQLite Expert Professional,當然你也可以使用其他工具 又需要的可以下載:SQLiteExpert.zip
把我們的db文件導出到電腦桌面,打開SQLiteExpert,界面如下:
別問我怎麼玩,導入db後自己慢慢玩,用法很簡單,不懂百度~
至於方法二,本來是想試試的,後來發現sqlite命令找不到,試了幾次就算了, 後面用到在細扣,有興趣可以找下郭霖的《第一行代碼——Android》按著流程圖試試! 這裡只貼前面的一部分,命令部分自己看書!
1.配置SDK環境變量:
右鍵我的電腦 ——> 高級系統設置 -> 環境變量 -> 新建系統變量 -> 把SDK的platform-tools路徑拷貝下: 比如筆者的:C:\Software\Coding\android-sdks-as\platform-tools
確定,然後再找到Path的環境變量,編輯,然後在結尾加上:%SDK_HOME%;
然後打開命令行,輸入adb,唰唰唰一堆東西,就說明配置成功了!
——————重點——————: 在執行後續命令行指令之前,針對你的測試的機器可能有幾種: 1.原生模擬器:那行,你跳過這裡,繼續往下 2.Genymotion模擬器:沒戲,Genymotion Shell執行不了下述命令 3.真機(已root):那麼你打開File Explorer看看data/data/目錄下有東西沒?沒麼? 下面提供一個方法,就是先裝個RE文件管理器,然後授予RE Root權限,接著來到根目錄: 然後長按data目錄,會彈出這樣的對話框:
接著等他慢慢修改權限,修改完畢後,我們再次打開DDMS的File Explorer,我們可以看到:
好的,可以看到data/data裡的東西了! ——————————————————————
2.進入adb shell,接著鍵入下述指令,來到我們app的databases目錄下:
接著依次輸入下述指令:
- sqlite3 my.db :打開數據庫文件
- .table 查看數據庫中有哪些表 接著你直接輸入數據庫語句就可以了,比如查詢:Select * from person
- .schema:查看建表語句
- .quit:退出數據庫的編輯
- .exit:退出設備控制台
...因為system/bin/sh sqlite3: not found,這個問題,後面Sqlite命令的都用不了, 要看效果圖就自行查詢郭大俠的書吧~而下面我們還是先導出db文件,然後用圖形化的 數據庫工具來查看!
假如你沒學過數據庫相關的語法,或者你懶,不想寫數據庫語法,就可以使用Android給我們 提供的操作數據庫的一些API方法,下面我們寫個簡單的例子來掩飾下這些API的用法!
代碼示例:
運行效果圖:
實現代碼:
布局過於簡單,就四個Button,就不貼了,直接貼MainActivity.java的代碼:
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private Context mContext; private Button btn_insert; private Button btn_query; private Button btn_update; private Button btn_delete; private SQLiteDatabase db; private MyDBOpenHelper myDBHelper; private StringBuilder sb; private int i = 1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mContext = MainActivity.this; myDBHelper = new MyDBOpenHelper(mContext, "my.db", null, 1); bindViews(); } private void bindViews() { btn_insert = (Button) findViewById(R.id.btn_insert); btn_query = (Button) findViewById(R.id.btn_query); btn_update = (Button) findViewById(R.id.btn_update); btn_delete = (Button) findViewById(R.id.btn_delete); btn_query.setOnClickListener(this); btn_insert.setOnClickListener(this); btn_update.setOnClickListener(this); btn_delete.setOnClickListener(this); } @Override public void onClick(View v) { db = myDBHelper.getWritableDatabase(); switch (v.getId()) { case R.id.btn_insert: ContentValues values1 = new ContentValues(); values1.put("name", "呵呵~" + i); i++; //參數依次是:表名,強行插入null值得數據列的列名,一行記錄的數據 db.insert("person", null, values1); Toast.makeText(mContext, "插入完畢~", Toast.LENGTH_SHORT).show(); break; case R.id.btn_query: sb = new StringBuilder(); //參數依次是:表名,列名,where約束條件,where中占位符提供具體的值,指定group by的列,進一步約束 //指定查詢結果的排序方式 Cursor cursor = db.query("person", null, null, null, null, null, null); if (cursor.moveToFirst()) { do { int pid = cursor.getInt(cursor.getColumnIndex("personid")); String name = cursor.getString(cursor.getColumnIndex("name")); sb.append("id:" + pid + ":" + name + "\n"); } while (cursor.moveToNext()); } cursor.close(); Toast.makeText(mContext, sb.toString(), Toast.LENGTH_SHORT).show(); break; case R.id.btn_update: ContentValues values2 = new ContentValues(); values2.put("name", "嘻嘻~"); //參數依次是表名,修改後的值,where條件,以及約束,如果不指定三四兩個參數,會更改所有行 db.update("person", values2, "name = ?", new String[]{"呵呵~2"}); break; case R.id.btn_delete: //參數依次是表名,以及where條件與約束 db.delete("person", "personid = ?", new String[]{"3"}); break; } } }
當然,你可能已經學過SQL,會寫相關的SQL語句,而且不想用Android提供的這些API, 你可以直接使用SQLiteDatabase給我們提供的相關方法:
- execSQL(SQL,Object[]):使用帶占位符的SQL語句,這個是執行修改數據庫內容的sql語句用的
- rawQuery(SQL,Object[]):使用帶占位符的SQL查詢操作 另外前面忘了介紹下Curosr這個東西以及相關屬性,這裡補充下: ——Cursor對象有點類似於JDBC中的ResultSet,結果集!使用差不多,提供一下方法移動查詢結果的記錄指針:
- move(offset):指定向上或者向下移動的行數,整數表示向下移動;負數表示向上移動!
- moveToFirst():指針移動到第一行,成功返回true,也說明有數據
- moveToLast():指針移動到最後一樣,成功返回true;
- moveToNext():指針移動到下一行,成功返回true,表明還有元素!
- moveToPrevious():移動到上一條記錄
- getCount( )獲得總得數據條數
- isFirst():是否為第一條記錄
- isLast():是否為最後一項
- moveToPosition(int):移動到指定行
使用代碼示例:
public void save(Person p) { SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); db.execSQL("INSERT INTO person(name,phone) values(?,?)", new String[]{p.getName(),p.getPhone()}); }
public void delete(Integer id) { SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); db.execSQL("DELETE FROM person WHERE personid = ?", new String[]{id}); }
public void update(Person p) { SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); db.execSQL("UPDATE person SET name = ?,phone = ? WHERE personid = ?", new String[]{p.getName(),p.getPhone(),p.getId()}); }
public Person find(Integer id) { SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); Cursor cursor = db.rawQuery("SELECT * FROM person WHERE personid = ?", new String[]{id.toString()}); //存在數據才返回true if(cursor.moveToFirst()) { int personid = cursor.getInt(cursor.getColumnIndex("personid")); String name = cursor.getString(cursor.getColumnIndex("name")); String phone = cursor.getString(cursor.getColumnIndex("phone")); return new Person(personid,name,phone); } cursor.close(); return null; }
public List<Person> getScrollData(int offset,int maxResult) { List<Person> person = new ArrayList<Person>(); SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); Cursor cursor = db.rawQuery("SELECT * FROM person ORDER BY personid ASC LIMIT= ?,?", new String[]{String.valueOf(offset),String.valueOf(maxResult)}); while(cursor.moveToNext()) { int personid = cursor.getInt(cursor.getColumnIndex("personid")); String name = cursor.getString(cursor.getColumnIndex("name")); String phone = cursor.getString(cursor.getColumnIndex("phone")); person.add(new Person(personid,name,phone)) ; } cursor.close(); return person; }
public long getCount() { SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); Cursor cursor = db.rawQuery("SELECT COUNT (*) FROM person",null); cursor.moveToFirst(); long result = cursor.getLong(0); cursor.close(); return result; }
PS:除了上面獲取條數的方法外還可以使用cursor.getCount()方法獲得數據的條數, 但是SQL語句要改改!比如SELECT * FROM person;
本節給大家介紹了Android內置SQLite的基本用法,還是比較簡單的,下一節再來研究點稍微 高級一點的東西,SQLite事務,應用更新數據庫裡數據怎麼處理,以及數據庫存儲大二進制文件 的方法!好的,本節就到這裡~
在Android應用中,設置日期和時間是非常簡單的事,由日期和時間對話框控件搞定。 一、界面 1、打開“src/com.genwoxue.datetime
定時器Chronometer在Android應用中,對於需要計時的場合應用比較方便,類似於VB中的Timer控件。 一、設計界面 1、打開“res
本節引言:我們前面已經學習了ListView的一些基本用法咧,但是細心的你可能發現了,我們的數據一開始定義好的,都是靜態的,但是實際開發中,我們的數
本章著重通過一個網絡通信應用再次了解一下Handle、Message、Looper實際用法。 一、設計界面1、布局文件打開res/layout/activity