【Android遊戲開發十三】(保存遊戲數據 [下文])詳解SQLite存儲方式,並把SQLite的數據庫文件存儲在SD卡中!!!

 李華明Himi 原創,轉載務必在明顯處註明:
轉載自 【黑米GameDev街區】 原文連接:  http://www.himigame.com/android-game/329.html

 

不少童鞋說個人代碼運行後,點擊home或者back後會程序異常,若是你也這樣遇到過,那麼你確定沒有仔細讀完Himi的博文,第十九篇Himi專門寫了關於這些錯誤的緣由和解決方法,這裏我在博客都補充說明下,省的童鞋們總疑惑這一塊;請點擊下面聯繫進入閱讀:css

【Android遊戲開發十九】(必看篇)SurfaceView運行機制詳解—剖析Back與Home按鍵及切入後臺等異常處理!html


 上一篇跟各位童鞋介紹了SharedPreference 和 File流如何存儲數據,而且推薦使用FileOutputStream/FileInputStream來存儲我們遊戲數據,那麼這一篇則是像你們介紹另一種適合遊戲數據存儲的方式:SQLite 輕量級數據庫!java

 

先介紹幾個基本概念知識:mysql

 

 什麼是SQLite:android

       SQLite是一款輕量級數據庫,它的設計目的是嵌入式,並且它佔用的資源很是少,在嵌入式設備中,只須要幾百KB!!!!!sql

 

SQLite的特性:數據庫

 

  • 輕量級
    使用 SQLite 只須要帶一個動態庫,就能夠享受它的所有功能,並且那個動態庫的尺寸想當小。
  • 獨立性
    SQLite 數據庫的核心引擎不須要依賴第三方軟件,也不須要所謂的「安裝」。
  • 隔離性
    SQLite 數據庫中全部的信息(好比表、視圖、觸發器等)都包含在一個文件夾內,方便管理和維護。
  • 跨平臺
    SQLite 目前支持大部分操做系統,不至電腦操做系統更在衆多的手機系統也是可以運行,好比:Android。
  • 多語言接口
    SQLite 數據庫支持多語言編程接口。
  • 安全性
    SQLite 數據庫經過數據庫級上的獨佔性和共享鎖來實現獨立事務處理。這意味着多個進程能夠在同一時間從同一數據庫讀取數據,但只能有一個能夠寫入數據.

      優勢:1.能存儲較多的數據。編程

                2.能將數據庫文件存放到SD卡中!安全

 

 

什麼是 SQLiteDatabase? app

     一個 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點起來上班 呵呵~習慣了~)

(推薦你們訂閱本博客,由於咱的更新速度但是很快的~娃哈哈)

本篇源碼: http://www.himigame.com/android-game/329.html

 

 

 

 

 


原文連接: http://blog.csdn.net/xiaominghimi/article/details/6114629
相關文章
相關標籤/搜索