很多童鞋说我的代码运行后,点击home或者back后程序会出现异常。如果你遇到过这种情况,那么你一定没有仔细阅读Himi的博文。第十九篇Himi写的关于这些错误的原因和解决方法,这里我会在博客中补充说明,省童鞋们对此一直很疑惑;请点击以下链接阅读:
【Android游戏开发19】(必读)SurfaceView运行机制详解-Back和Home键解析及切入后台等异常处理!
上一篇文章给大家介绍了SharedPreference和File流是如何存储数据的,推荐使用FileOutputStream/FileInputStream来存储我们的游戏数据,所以这篇文章就像介绍了另一种游戏数据存储方式:SQLite轻量级数据库!
先介绍一些基本概念:
什么是 SQLite:
SQLite是一个轻量级的数据库,它是为嵌入式而设计的,占用资源极少,在嵌入式设备中,只有几百KB!!!!!!
SQLite 的特点:
优点:1.可以存储更多数据。
2.将数据库文件保存到 SD 卡!
什么是 SQLiteDatabase?
SQLiteDatabase 的一个实例代表一个 SQLite 数据库。通过SQLiteDatabase实例的一些方法,我们可以执行SQL语句对数据库进行增删查改。需要注意的是,数据库是应用程序私有的,数据库的名称在应用程序内也是唯一的。
什么是 SQLiteOpenHelper?
根据名字,我们可以看出这个类是一个辅助类。该类主要生成数据库,管理数据库的版本。在程序中调用该类的getWritableDatabase()或getReadableDatabase()方法时,如果此时没有数据,Android系统会自动生成数据库。 SQLiteOpenHelper是一个抽象类,我们通常需要继承它并实现里面的三个功能,
什么是ContentValues类?
ContentValues类类似于Hashmap/Hashtable。它还负责存储一些名称-值对,但是它存储的名称-值对中的名称是一个
字符串类型,值是原始类型。
什么是光标?
Cursor 是 Android 中一个非常有用的界面。通过Cursor,我们可以对从数据库中查询到的结果集进行随机读写访问。
好了,基础知识就介绍到这里了,开始代码吧:还是按照我一贯的风格,代码中该解释的地方已经及时注释和解释在代码中了!
顺便给个项目截图:
先给出xml:
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 *@要解释这个类,我们只需要传递一个构造函数并重写两个方法。 * */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最终静态字符串 TEXT = “文本”;公共静态最终字符串 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类型,用于唯一行的ID;//AUTOINCREMENT表示数据库会为每条记录的key加一个,以保证记录的唯一性;//最后我加一列text String类型 // — ——– 注意:这里的str_sql是sql语句,类似于dos命令,注意空格! db.execSQL(str_sql);//execSQL()方法是执行一条sql语句//虽然这句话我们生成了一个数据库表和包含该表的sql.himi文件,//但是需要注意的是方法不是创建,是str_sql的传入语句。这条sql语句意味着创建! ! }@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {// 默认情况下,当我们插入数据库时,会立即更新 // 当数据库需要升级时,Android系统会主动调用该方法. // 一般我们在这个方法中删除数据表,新建一个数据表。 // 当然,我们是否需要做其他操作,完全取决于游戏的需要。 Log.v(“Himi”, “onUpgrade”);} }
我喜欢在代码中立即附上解释。感觉这段代码让大家更容易理解和查找。当然,如果童鞋不喜欢android 数据储存方式,可以告诉我,我会换的~呵呵~
看下面最重要的MainActivity中的代码:
包 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 * @Save方法:SQLite轻量级数据库,* @优势:可以将自己的数据存储在文件系统或数据库中,也可以将自己的数据存储在SQLite数据库,或者SD卡中*@注1:数据库是游戏(应用程序)私有,并且*数据库名称在游戏中也是唯一的。
* @Note 2 在apk中创建的数据库外的进程没有读/写权限, * 我们需要在sdcard上创建数据库文件来解决类似的问题。 * @Note 3 当你删除第一个 id 数据或所有数据被删除时,SQLite 不会自动排序, * 也就是说,添加数据时,如果不指定 id,SQLite 会在第默认情况下以原始 id 结尾。 * @Note 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”);//创建目录//私有文件 f = new File(“/sdcard/himi/himi.db”);//创建文件@Overridepublic 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)fin dViewById(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();//创建a directory//} //if (!f.exists()) {//文件存在 return false//try {//f.createNewFile();//创建文件//} catch (IOException e){// // TODO 自动生成的catch块//e.printStackTrace();//}//} }@Overridepublic void onClick(View v) {try { //注2—-如果你使用数据库文件创建SD卡,然后创建数据库mysql如下: // mysql = SQLiteDatabase.openOrCreateDatabase(f, n ull); //注3—如果你想把数据库文件默认放在系统中,那么创建数据库mysql如下: mysql = myOpenHelper.getWritableDatabase(); // 实例数据库 if (v == btn_addOne) {// 添加数据 // ———— 要插入的读写句柄 ———// ContentValues其实就是一个哈希表 HashMap ,key value 是字段名, //Value value 是字段的值。
然后通过ContentValues的put方法,可以 // 把数据放到ContentValues中,然后再插入到表格中! ContentValues cv = new ContentValues();cv.put(MySQLiteOpenHelper.TEXT, “测试新数据”) ;mysql.insert(MySQLiteOpenHelper.TABLE_NAME, null, cv);// insert() 第一个参数标识表名需要插入操作//第二个参数:默认传null//第三个是插入数据// ———————- SQL语句插入————-// String INSERT_DATA =// “INSERT INTO himi(id,text) values(1, ‘Insert through SQL statement’)”;// 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()) {//until return false e表示表已经到了数据的末尾 temp += cur.getString(0);//参数0指的是列的下标,其中0指的是to the id column temp += cur.getString(1);//这里的0是i++对比当前文本应该是我们的; temp += ” “; // 这里是我的显示格式,呵呵~ if (i % 3 == 0) //这里是我的显示格式,hehe~temp += “/n”;//这里是我的显示格式,hehe~}tv.setText(temp);}}否则 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 statement’ 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 一张表的操作;这里还要注意,当我们新建我们的 MySQLiteOpenHelper 类实例对象时,并没有创建数据库~!但是当我们调用(注意3)MySQLiteOpenHelper ..getWritableDatabase()这个方法获取数据库读写句柄的时候,android会分析是否已经有数据库,如果没有,它会默认为我们创建一个数据库并且会在系统路径data-data-com.himi-databases下生成himi.db文件!
(如果我们用sd卡存储数据库文件,就不用写这个类了,但是我们打开自己的文件获取一个数据库,嘻嘻,不过方便~)
————如果我们需要将数据库文件存储到SD卡:
第一步:确认模拟器中有SD卡。两种创建SD卡的方法见我的博文:[Android 2D Game Development No. 10]
第二步:(注意1)先创建SD卡目录和路径,已经是我们的数据库文件了!和上面的默认路径不同,如果没有数据库,默认系统路径会生成一个数据库和一个数据库文件!我们必须手动创建数据库文件!
第三步:在删除数据、添加数据等之前,我们需要获取数据库的读写句柄来获取一个数据库实例; (注意2)此时的创建不像系统默认创建,而是我们通过打开第一步创建的文件来获取数据库实例。这只是为了创建数据库!!!
第4步:在我们删除数据、添加数据等之前,我们需要创建一个表!
第五步:在配置文件AndroidMainfest.xml中声明写入SD卡的权限。权限在上一篇文章中已经介绍过了。如果您不知道,请自行查看。
有些童鞋看不懂表中的默认路径是什么?那是因为我们默认在它为我们创建数据库的时候有创建表的操作,也就是MySQLiteOpenHelper类中onCreate()方法中的操作!所以如果我们想在删除数据、添加数据等之前先建表,建表的方法是一样的。
总结:无论哪种方式,我们都会 – 创建数据库 – 创建表 – 并执行此操作!
注4:
Android 中查询数据是通过 Cursor 类实现的。当我们使用 SQLiteDatabase.query() 方法时,我们会得到一个 Cursor 对象,Cursor 指向每一条数据。它提供了很多关于查询的方法,具体方法如下:
以下是操作方法和说明:
move以当前位置为参考,将Cursor移动到指定位置,成功返回true,失败返回false
moveToPosition 将光标移动到指定位置,成功返回true,失败返回false
moveToNext 将光标前移一位,成功返回trueandroid 数据储存方式,失败返回false
moveToLast 将光标向后移动一位,成功返回 true,失败返回 false。
movetoFirst 将光标移动到第一行,成功返回true,失败返回false
isBeforeFirst 返回 Cursor 是否指向第一项数据之前
isAfterLast 返回 Cursor 是否指向最后一项数据之后
isClosed 返回游标是否关闭
isFirst 返回 Cursor 是否指向数据的第一项
isLast 返回 Cursor 是否指向最后一项数据
isNull 返回指定位置的值是否为空
getCount 返回数据项的总数
getInt 返回当前行的指定索引数据
接触过很多SQLite的童鞋,但是不知道怎么存到SD里,所以也研究了一下,这篇文章也写了SD卡里的方法给大家。
好的,这几天元旦放假了,就给大家写一下,嘿嘿~凌晨2:00,咳咳~该睡觉了。 (我一般3:00睡觉,8:00起床上班~习惯了~)
(建议大家订阅这个博客,因为我们的更新速度很快~哇哈哈)
本文源码:
暂无评论内容