? ? ?上一篇跟各位童鞋介紹了SharedPreference 和 File流如何存儲數據,并且推薦使用FileOutputStream/FileInputStream來存儲咱們游戲數據,那么這一篇則是像大家介紹另外一種適合游戲數據存儲的方式:SQLite 輕量級數據庫!
???? 先介紹幾個基本概念知識:?
什么是SQLite:
??? SQLite是一款輕量級數據庫,它的設計目的是嵌入式,而且它占用的資源非常少,在嵌入式設備中,只需要幾百KB!!!!!?
SQLite的特性:?
- 輕量級
使用 SQLite 只需要帶一個動態庫,就可以享受它的全部功能,而且那個動態庫的尺寸想當小。
- 獨立性
SQLite 數據庫的核心引擎不需要依賴第三方軟件,也不需要所謂的“安裝”。
- 隔離性
SQLite 數據庫中所有的信息(比如表、視圖、觸發器等)都包含在一個文件夾內,方便管理和維護。
- 跨平臺
SQLite 目前支持大部分操作系統,不至電腦操作系統更在眾多的手機系統也是能夠運行,比如:Android。
- 多語言接口
SQLite 數據庫支持多語言編程接口。
- 安全性
SQLite 數據庫通過數據庫級上的獨占性和共享鎖來實現獨立事務處理。這意味著多個進程可以在同一時間從同一數據庫讀取數據,但只能有一個可以寫入數據.
優點:1.能存儲較多的數據。
2.能將數據庫文件存放到SD卡中!?
什么是 SQLiteDatabase?
??? 一個 SQLiteDatabase 的實例代表了一個SQLite 的數據庫,通過SQLiteDatabase 實例的一些方法,我們可以執行SQL 語句,對數 據庫進行增、刪、查、改的操作。需要注意的是,數據庫對于一個應用來說是私有的,并且在一個應用當中,數據庫的名字也是惟一的。
?什么是 SQLiteOpenHelper ?
??? 根據這名字,我們可以看出這個類是一個輔助類。這個類主要生成一個數據庫,并對數據庫的版本進行管理。當在程序當中調用這個類的 方法getWritableDatabase(),或者getReadableDatabase()方法的時候,如果當時沒有數據,那么Android 系統就會自動生成一 個數 據庫。SQLiteOpenHelper 是一個抽象類,我們通常需要繼承它,并且實現里邊的3 個函數,
?什么是 ContentValues 類?
??? ContentValues 類和Hashmap/Hashtable 比較類似,它也是負責存儲一些名值對,但是它存儲的名值對當中的名是一個String 類型,而值都是基本類型。
?什么是 Cursor ?
??? Cursor 在Android 當中是一個非常有用的接口,通過Cursor 我們可以對從數據庫查詢出來的結果集進行隨 機的讀寫訪問。
???? OK,基本知識就介紹到這里,下面開始上代碼:還是按照我的一貫風格,代碼中該解釋的地方都已經在代碼中及時注釋和講解了!
順便來張項目截圖:?
?先給出xml:?
<?xml?version="1.0"?encoding="utf-8"?>? <LinearLayout?xmlns:android="http://schemas.android.com/apk/res/android"? ????android:orientation="vertical"?android:layout_width="fill_parent"? ????android:layout_height="fill_parent">? ????<TextView?android:layout_width="fill_parent"? ????????android:layout_height="wrap_content"?android:text="SQL?練習!(如果你使用的SD卡存儲數據方式,為了保證正常操作,請你先點擊創建一張表然后再操作)"? ????????android:textSize="20sp"?android:textColor="#ff0000"?android:id="@+id/tv_title"?/>? ????<Button?android:id="@+id/sql_addOne"?android:layout_width="fill_parent"? ????????android:layout_height="wrap_content"?android:text="插入一條記錄"></Button>? ????<Button?android:id="@+id/sql_check"?android:layout_width="fill_parent"? ? ????????android:layout_height="wrap_content"?android:text="查詢數據庫"></Button>? ????<Button?android:id="@+id/sql_edit"?android:layout_width="fill_parent"? ????????android:layout_height="wrap_content"?android:text="修改一條記錄"></Button>? ????<Button?android:id="@+id/sql_deleteOne"?android:layout_width="fill_parent"? ????????android:layout_height="wrap_content"?android:text="刪除一條記錄"></Button>? ????<Button?android:id="@+id/sql_deleteTable"?android:layout_width="fill_parent"? ????????android:layout_height="wrap_content"?android:text="刪除數據表單"></Button>? ????<Button?android:id="@+id/sql_newTable"?android:layout_width="fill_parent"? ????????android:layout_height="wrap_content"?android:text="新建數據表單"></Button>? </LinearLayout>? ??????xml中定義了我們需要練習用到的幾個操作按鈕,這里不多解釋了,下面看java源碼:先看我們繼承的 SQLiteOpenHelper 類?
package?com.himi; ?import?android.content.Context; ?import?android.database.sqlite.SQLiteDatabase; ?import?android.database.sqlite.SQLiteOpenHelper; ?import?android.util.Log; ?/** ??*? ??*?@author?Himi ??*?@解釋?此類我們只需要傳建一個構造函數?以及重寫兩個方法就OK啦、 ??*? ??*/ ?public?class?MySQLiteOpenHelper?extends?SQLiteOpenHelper?{ ? ????public?final?static?int?VERSION?=?1;//?版本號 ? ????public?final?static?String?TABLE_NAME?=?"himi";//?表名 ? ????public?final?static?String?ID?=?"id";//?后面ContentProvider使用 ? ????public?final?static?String?TEXT?=?"text"; ? ????public?static?final?String?DATABASE_NAME?=?"Himi.db"; ? ????public?MySQLiteOpenHelper(Context?context)?{ ?????????//?在Android?中創建和打開一個數據庫都可以使用openOrCreateDatabase?方法來實現, ?????????//?因為它會自動去檢測是否存在這個數據庫,如果存在則打開,不過不存在則創建一個數據庫; ?????????//?創建成功則返回一個?SQLiteDatabase對象,否則拋出異常FileNotFoundException。 ?????????//?下面是來創建一個名為"DATABASE_NAME"的數據庫,并返回一個SQLiteDatabase對象? ????????? ?????????super(context,?DATABASE_NAME,?null,?VERSION);? ?????}? ?????@Override ?????//?在數據庫第一次生成的時候會調用這個方法,一般我們在這個方法里邊生成數據庫表; ?????public?void?onCreate(SQLiteDatabase?db)?{? ? ????????String?str_sql?=?"CREATE?TABLE?"?+?TABLE_NAME?+?"("?+?ID ? ????????????????+?"?INTEGER?PRIMARY?KEY?AUTOINCREMENT,"?+?TEXT?+?"?text?);"; ?????????//?CREATE?TABLE?創建一張表?然后后面是我們的表名 ?????????//?然后表的列,第一個是id?方便操作數據,int類型 ?????????//?PRIMARY?KEY?是指主鍵?這是一個int型,用于唯一的標識一行; ?????????//?AUTOINCREMENT?表示數據庫會為每條記錄的key加一,確保記錄的唯一性; ?????????//?最后我加入一列文本?String類型 ?????????//?----------注意:這里str_sql是sql語句,類似dos命令,要注意空格! ?????????db.execSQL(str_sql); ?????????//?execSQL()方法是執行一句sql語句 ?????????//?雖然此句我們生成了一張數據庫表和包含該表的sql.himi文件, ?????????//?但是要注意?不是方法是創建,是傳入的一句str_sql這句sql語句表示創建!! ?????} ?????@Override ?????public?void?onUpgrade(SQLiteDatabase?db,?int?oldVersion,?int?newVersion)?{ ?????????//?一般默認情況下,當我們插入?數據庫就立即更新 ?????????//?當數據庫需要升級的時候,Android?系統會主動的調用這個方法。 ?????????//?一般我們在這個方法里邊刪除數據表,并建立新的數據表, ?????????//?當然是否還需要做其他的操作,完全取決于游戲需求。 ?????????Log.v("Himi",?"onUpgrade"); ?????}? ?}?????????我喜歡代碼中立即附上解釋,感覺這樣代碼比較讓大家更容易理解和尋找,當然如果童鞋們不喜歡,可以告訴我,我改~嘿嘿~
?????下面看最重要的MainActivity中的代碼:?
package?com.himi; ?import?java.io.File; ?import?java.io.IOException; ?import?android.app.Activity; ?import?android.content.ContentValues; ?import?android.database.Cursor; ?import?android.database.sqlite.SQLiteDatabase; ?import?android.os.Bundle; ?import?android.view.View; ?import?android.view.Window; ?import?android.view.WindowManager; ?import?android.view.View.OnClickListener; ?import?android.widget.Button; ?import?android.widget.TextView; ?//?------------第三種保存方式--------《SQLite》--------- ?/** ??*?@author?Himi ??*?@保存方式:SQLite?輕量級數據庫、 ??*?@優點:?可以將自己的數據存儲到文件系統或者數據庫當中,?也可以將自己的數據存 ??*?????????儲到SQLite數據庫當中,還可以存到SD卡中 ??*?@注意1:數據庫對于一個游戲(一個應用)來說是私有的,并且在一個游戲當中,? ??*?????????數據庫的名字也是唯一的。 ??*?@注意2?apk中創建的數據庫外部的進程是沒有權限去讀/寫的,? ??*?????????我們需要把數據庫文件創建到sdcard上可以解決類似問題. ??*?@注意3?當你刪除id靠前的數據或者全部刪除數據的時候,SQLite不會自動排序, ??*????????也就是說再添加數據的時候你不指定id那么SQLite默認還是在原有id最后添加一條新數據 ??*?@注意4?android?中?的SQLite?語法大小寫不敏感,也就是說不區分大小寫; ??*??? ??*/ ?public?class?MainActivity?extends?Activity?implements?OnClickListener?{ ?????private?Button?btn_addOne,?btn_deleteone,?btn_check,?btn_deleteTable, ?????????????btn_edit,?btn_newTable; ?????private?TextView?tv; ?????private?MySQLiteOpenHelper?myOpenHelper;//?創建一個繼承SQLiteOpenHelper類實例 ?????private?SQLiteDatabase?mysql?;? ?//---------------以下兩個成員變量是針對在SD卡中存儲數據庫文件使用 ? //??private?File?path?=?new?File("/sdcard/himi");//?創建目錄 ? //??private?File?f?=?new?File("/sdcard/himi/himi.db");//?創建文件 ? ????@Override ?????public?void?onCreate(Bundle?savedInstanceState)?{ ?????????super.onCreate(savedInstanceState); ?????????getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, ?????????????????WindowManager.LayoutParams.FLAG_FULLSCREEN); ?????????this.requestWindowFeature(Window.FEATURE_NO_TITLE); ?????????setContentView(R.layout.main); ? ????????tv?=?(TextView)?findViewById(R.id.tv_title); ? ????????btn_addOne?=?(Button)?findViewById(R.id.sql_addOne); ? ????????btn_check?=?(Button)?findViewById(R.id.sql_check); ? ????????btn_deleteone?=?(Button)?findViewById(R.id.sql_deleteOne); ? ????????btn_deleteTable?=?(Button)?findViewById(R.id.sql_deleteTable); ? ????????btn_newTable?=?(Button)?findViewById(R.id.sql_newTable); ? ????????btn_edit?=?(Button)?findViewById(R.id.sql_edit); ? ????????btn_edit.setOnClickListener(this); ?????????btn_addOne.setOnClickListener(this); ?????????btn_check.setOnClickListener(this); ?????????btn_deleteone.setOnClickListener(this); ?????????btn_deleteTable.setOnClickListener(this); ?????????btn_newTable.setOnClickListener(this); ? ????????myOpenHelper?=?new?MySQLiteOpenHelper(this);//?實例一個數據庫輔助器 ? //備注1??----如果你使用的是將數據庫的文件創建在SD卡中,那么創建數據庫mysql如下操作: ?//??????if?(!path.exists())?{//?目錄存在返回false ?//??????????path.mkdirs();//?創建一個目錄 ?//??????} ?//??????if?(!f.exists())?{//?文件存在返回false ?//??????????try?{ ?//??????????????f.createNewFile();//創建文件? ?//??????????}?catch?(IOException?e)?{ ?//??????????????//?TODO?Auto-generated?catch?block ?//??????????????e.printStackTrace(); ?//??????????} ?//??????}? ?????} ?????@Override ?????public?void?onClick(View?v)?{?? ?????????try?{? ?//備注2----如果你使用的是將數據庫的文件創建在SD卡中,那么創建數據庫mysql如下操作: ? //??????????????mysql?=?SQLiteDatabase.openOrCreateDatabase(f,?null);? ? //備注3---?如果想把數據庫文件默認放在系統中,那么創建數據庫mysql如下操作: ? ????????????????mysql?=?myOpenHelper.getWritableDatabase();?//?實例數據庫 ? ????????????if?(v?==?btn_addOne)?{//?添加數據 ? ????????????????//?----------------------?讀寫句柄來插入--------- ?????????????????//?ContentValues?其實就是一個哈希表HashMap,?key值是字段名稱, ?????????????????//Value值是字段的值。然后?通過?ContentValues?的?put?方法就可以 ?????????????????//把數據放到ContentValues中,然后插入到表中去! ? ????????????????ContentValues?cv?=?new?ContentValues(); ? ????????????????cv.put(MySQLiteOpenHelper.TEXT,?"測試新的數據"); ?????????????????mysql.insert(MySQLiteOpenHelper.TABLE_NAME,?null,?cv); ?????????????????//?inser()?第一個參數?標識需要插入操作的表名 ?????????????????//?第二個參數?:默認傳null即可 ?????????????????//?第三個是插入的數據 ?????????????????//?----------------------?SQL語句插入-------------- ? ????????????????//?String?INSERT_DATA?= ? ????????????????//?"INSERT?INTO?himi?(id,text)?values?(1,?'通過SQL語句插入')"; ?????????????????//?db.execSQL(INSERT_DATA); ?????????????????tv.setText("添加數據成功!點擊查看數據庫查詢"); ? ????????????}?else?if?(v?==?btn_deleteone)?{//?刪除數據 ? ????????????????//?----------------------?讀寫句柄來刪除 ?????????????????mysql.delete("himi",?MySQLiteOpenHelper.ID?+?"=1",?null); ?????????????????//?第一個參數?需要操作的表名 ?????????????????//?第二個參數為?id+操作的下標?如果這里我們傳入null,表示全部刪除 ?????????????????//?第三個參數默認傳null即可 ?????????????????//?-----------------------?SQL語句來刪除 ? ????????????????//?String?DELETE_DATA?=?"DELETE?FROM?himi?WHERE?id=1"; ? ????????????????//?db.execSQL(DELETE_DATA); ?????????????????tv.setText("刪除數據成功!點擊查看數據庫查詢"); ? ????????????}?else?if?(v?==?btn_check)?{//?遍歷數據 ? //備注4------ ? ????????????????Cursor?cur?=?mysql.rawQuery("SELECT?*?FROM?" ? ????????????????????????+?MySQLiteOpenHelper.TABLE_NAME,?null); ?????????????????if?(cur?!=?null)?{ ? ????????????????????String?temp?=?""; ? ????????????????????int?i?=?0; ? ????????????????????while?(cur.moveToNext())?{//直到返回false說明表中到了數據末尾 ?????????????????????????temp?+=?cur.getString(0);? ?????????????????????????//?參數0?指的是列的下標,這里的0指的是id列 ?????????????????????????temp?+=?cur.getString(1); ?????????????????????????//?這里的0相對于當前應該是咱們的text列了 ?????????????????????????i++; ?????????????????????????temp?+=?"??";?//?這里是我整理顯示格式?,呵呵~ ? ????????????????????????if?(i?%?3?==?0)?//?這里是我整理顯示格式?,呵呵~ ? ????????????????????????????temp?+=?"/n";//?這里是我整理顯示格式?,呵呵~ ?????????????????????} ?????????????????????tv.setText(temp); ?????????????????} ? ????????????}?else?if?(v?==?btn_edit)?{//?修改數據 ? ????????????????//?------------------------句柄方式來修改?------------- ? ????????????????ContentValues?cv?=?new?ContentValues(); ? ????????????????cv.put(MySQLiteOpenHelper.TEXT,?"修改后的數據"); ?????????????????mysql.update("himi",?cv,?"id?"?+?"="?+?Integer.toString(3),?null); ?????????????????//?------------------------SQL語句來修改?------------- ? ????????????????//?String?UPDATA_DATA?= ? ????????????????//?"UPDATE?himi?SET?text='通過SQL語句來修改數據'??WHERE?id=1"; ? ????????????????//?db.execSQL(UPDATA_DATA); ?????????????????tv.setText("修改數據成功!點擊查看數據庫查詢"); ? ????????????}?else?if?(v?==?btn_deleteTable)?{//?刪除表 ? ????????????????mysql.execSQL("DROP?TABLE?himi"); ?????????????????tv.setText("刪除表成功!點擊查看數據庫查詢"); ? ????????????}?else?if?(v?==?btn_newTable)?{//?新建表 ? ????????????????String?TABLE_NAME?=?"himi"; ? ????????????????String?ID?=?"id"; ? ????????????????String?TEXT?=?"text"; ? ????????????????String?str_sql2?=?"CREATE?TABLE?"?+?TABLE_NAME?+?"("?+?ID ? ????????????????????????+?"?INTEGER?PRIMARY?KEY?AUTOINCREMENT,"?+?TEXT ?????????????????????????+?"?text?);"; ?????????????????mysql.execSQL(str_sql2); ?????????????????tv.setText("新建表成功!點擊查看數據庫查詢"); ?????????????} ?????????????//?刪除數據庫: ?????????????//?this.deleteDatabase("himi.db"); ?????????}?catch?(Exception?e)?{ ?????????????tv.setText("操作失敗!"); ?????????}?finally?{//?如果try中異常,也要對數據庫進行關閉 ?????????????mysql.close(); ?????????} ?????} ?}?????? 以上代碼中我們實現了兩種存儲方式:
??? 一種存儲默認系統路徑/data-data-com.himi-databases下,另外一種則是保存在了/sdcard-himi下,生成數據庫文件himi.db
???? 那么這里兩種實現方式大概步驟和區別說下:?
-----------如果我們使用默認系統路徑存儲數據庫文件:
??? 第一步:新建一個類繼承SQLiteOpenHelper;寫一個構造,重寫兩個函數!
??? 第二步:在新建的類中的onCreate(SQLiteDatabase db) 方法中創建一個表;
??? 第三步:在進行刪除數據、添加數據等操作的之前我們要得到數據庫讀寫句柄得到一個數據庫實例;
??? 注意:?繼承寫這個輔助類,是為了在我們沒有數據庫的時候自動為我們生成一個數據庫,并且生成數據庫文件,這里也同時創建了一張表,因為我們在onCreate里是在數據庫中創建一張表的操作;這里還要注意在我們new 這個我們這個MySQLiteOpenHelper 類實例對象的時候并沒有創建數據庫喲~!而是在我們調用?(備注3)MySQLiteOpenHelper ..getWritableDatabase() 這個方法得到數據庫讀寫句柄的時候,android 會分析是否已經有了數據庫,如果沒有會默認為我們創建一個數據庫并且在系統路徑data-data-com.himi-databases下生成himi.db 文件!(如果我們使用sd卡存儲數據庫文件,就沒有必要寫這個類了,而是我們自己Open自己的文件得到一個數據庫,西西,反而方便~ )
?-----------如果我們需要把數據庫文件存儲到SD卡中:
??? 第一步:確認模擬器存在SD卡,關于SD卡的兩種創建方法見我的博文:【Android 2D游戲開發之十】
??? 第二步:(備注1)先創建SD卡目錄和路徑已經我們的數據庫文件!這里不像上面默認路徑中的那樣,如果沒有數據庫會默認系統路徑生成一個數據庫和一個數據庫文件!我們必須手動創建數據庫文件!
??? 第三步:在進行刪除數據、添加數據等操作的之前我們要得到數據庫讀寫句柄得到一個數據庫實例;(備注2)此時的創建也不是像系統默認創建,而是我們通過打開第一步創建好的文件得到數據庫實例。這里僅僅是創建一個數據庫!!!!
??? 第四步:在進行刪除數據、添加數據等操作的之前我們還要創建一個表!
??? 第五步:在配置文件AndroidMainfest.xml 聲明寫入SD卡的權限,上一篇已經介紹權限了,不知道的自己去看下吧。
??? 有些童鞋不理解什么默認路徑方式中就有表?那是因為我們在它默認給我們創建數據庫的時候我們有創建表的操作,就是MySQLiteOpenHelper類中的onCreate()方法里的操作!所以我們如果要在進行刪除數據、添加數據等操作的之前還要創建一個表,創建表的方法都是一樣的。?
??? 總結:不管哪種方式我們都要-創建數據庫-創建表-然后進行操作!?
備注4:
??? 在Android中查詢數據是通過Cursor類來實現的,當我們使用SQLiteDatabase.query()方法時,會得到一個Cursor對象,Cursor指向的就是每一條數據。它提供了很多有關查詢的方法,具體方法如下:?
以下是方法和說明:?
move 以當前的位置為參考,將Cursor移動到指定的位置,成功返回true, 失敗返回false?
moveToPosition 將Cursor移動到指定的位置,成功返回true,失敗返回false?
moveToNext 將Cursor向前移動一個位置,成功返回true,失敗返回false?
moveToLast 將Cursor向后移動一個位置,成功返回true,失敗返回 false。?
movetoFirst 將Cursor移動到第一行,成功返回true,失敗返回false?
isBeforeFirst 返回Cursor是否指向第一項數據之前?
isAfterLast 返回Cursor是否指向最后一項數據之后?
isClosed 返回Cursor是否關閉?
isFirst 返回Cursor是否指向第一項數據?
isLast 返回Cursor是否指向最后一項數據?
isNull 返回指定位置的值是否為null
?getCount 返回總的數據項數
?getInt 返回當前行中指定的索引數據
?????對于SQLite的很多童鞋有接觸過,但是就不知道怎么存儲在SD中,所以我也研究了下,這篇也寫了把sd卡中的方式也提供給大家。
???? OK 這元旦放假幾天就光給大家寫這個了,呵呵~凌晨2點了,咳咳~該睡覺了。(我一般凌成3點睡覺,早8點起來上班 呵呵~習慣了~)
本文轉自 xiaominghimi 51CTO博客,原文鏈接:http://blog.51cto.com/xiaominghimi/606759,如需轉載請自行聯系原作者
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎
總結
以上是生活随笔為你收集整理的【Android游戏开发十三】(保存游戏数据 [下文])详解SQLite存储方式的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。