【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
相关文章
相关标签/搜索