李华明Himi 原创,转载务必在明显处注明:
不少童鞋说个人代码运行后,点击home或者back后会程序异常,若是你也这样遇到过,那么你确定没有仔细读完Himi的博文,第十九篇Himi专门写了关于这些错误的缘由和解决方法,这里我在博客都补充说明下,省的童鞋们总疑惑这一块;请点击下面联系进入阅读:css
【Android游戏开发十九】(必看篇)SurfaceView运行机制详解—剖析Back与Home按键及切入后台等异常处理!html
对于游戏中的数据进行保存方式,在Android中经常使用的有四种保存方式,这里我先给你们统一先简单的介绍下:java
1. SharedPreferenceandroid
此保存方式试用于简单数据的保存,文如其名属于配置性质的保存,不适合数据比较大的保存方式;数据库
2. 文件存储 (FIleInputStream/FileOutputStream)api
此保存方式比较适合游戏的保存和使用,能够保存较大的数据,由于相对于SQLite来讲更容易让童鞋们接受,此方式不只能把数据存储在系统中也能将数据保存到SDcard中;数组
3.SQLite app
此保存方式比较适合游戏的保存和使用,能够保存较大的数据,而且能够将本身的数据存储到文件系统或者数据库当中,也能够将本身的数据存储到SQLite数据库当中,也能将数据保存到SDcard中;ide
4.ContentProvider (不推荐用于游戏保存)学习
此保存方式不推荐用于游戏保存,由于此方式不只能存储较大数据,还支持多个程序之间就的数据进行交换!!! 可是因为游戏中基本就不可能去访问外部应用的数据,因此对于此方式我不予讲解, 有兴趣的能够去自行百度 google 学习;
以上简单的对几种经常使用的保存方式进行的概述,那么,下面会详细的去分析每一个的优缺点以及每种保存的实现和须要注意的地方!
下面我首先向你们介绍第一种保存方式:
保存方式之: 《SharedPreference》
优势: 简单、方便、适合简单数据的快速保存
缺点:1.存数的文件只能在同一包内使用,不能在不一样包之间使用!
2.默认将数据存放在系统路径下 /data/data/com.himi/ ,没有找到放SD卡上的方法。
总结:其实本保存方式如同它的名字同样是个配置保存,虽然方便,但只适合存储比较简单的数据!
main.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="保存数据练习!" android:textSize="20sp" android:textColor="#ff0000" android:id="@+id/tv_title" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="请输入账号" /> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/editText_Login" android:text=""></EditText> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="请输入密码" /> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/editText_Password" android:text=""></EditText> <Button android:id="@+id/button_save" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="保存"></Button> <Button android:id="@+id/button_load" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="取出数据" android:visibility="invisible"></Button> </LinearLayout>
先把xml文件放上来的缘由是由于我在此篇中介绍的 SharedPreference 和 文件存储 (FIleInputStream/FileOutputStream),都共用此xml,很简单,两个textview 两个 editview 以及两个button,这里就很少说了;
下面是SharedPreference 的代码实现和详细讲解:
/** * @author Himi * @保存方式:SharedPreference * @注意:SharedPreference 能够跨程序包使用,多谢二楼童鞋提醒! * @操做模式: Context.MODE_PRIVATE:新内容覆盖原内容 * Context.MODE_APPEND:新内容追加到原内容后 * Context.MODE_WORLD_READABLE:容许其余应用程序读取 * Context.MODE_WORLD_WRITEABLE:容许其余应用程序写入,会覆盖原数据。 */ public class MainActivity extends Activity implements OnClickListener { private EditText et_login, et_password; private Button btn_save; private TextView tv_title; private SharedPreferences sp; @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); btn_save = (Button) findViewById(R.id.button_save); btn_save.setOnClickListener(this); et_login = (EditText) findViewById(R.id.editText_Login); et_password = (EditText) findViewById(R.id.editText_Password); tv_title = (TextView) findViewById(R.id.tv_title); // 这里咱们先调用 getSharedPreferences()来实例化一个SharedPreferences, // 第二个参数是指:操做模式(上面对各类操做模式已有解释) sp = getSharedPreferences("Setting_himi", MODE_PRIVATE); /* * 下面代码是咱们要在程序刚启动的时候咱们来读取以前的数据, * 固然咱们尚未保存任何数据因此确定找不到!! * 若是找不到也不要紧会默认返回一个参数值,看下面的方法含义便知! */ sp.getString("login", ""); // getString()相似哈希表,一个key 一个volue , // 这个方法若是找不到对应的第一个参数(key),那么将以第二个参数做为此key的返回值 et_login.setText(sp.getString("login", "")); et_password.setText(sp.getString("password", "")); } @Override public void onClick(View v) { if (v == btn_save) { if (et_login.getText().toString().equals("")) tv_title.setText("请输入账号!"); else if (et_password.getText().toString().equals("")) tv_title.setText("请输入密码!"); else { sp.edit() .putString("login", et_login.getText().toString()) .putString("password", et_password.getText().toString()) .commit(); // 从sp.edit()开始进入编辑状态,直到commit()提交! tv_title.setText("保存成功!可从新打开此程序,测试是否已经保存数据!" + "/n(或者在'File Explorer'窗口下-data-data-com.himi路径下" + "是否存在" +"了'Setting_himi.xml')"); } } } }
代码中的注释的很清楚了,比较简单,很少说了。
保存方式之: 《文件存储 OutputStream/InputStream》
优势: 1.适合游戏存储,能存储较大数据;
2.不只能存储到系统中,也能存储到SD卡中!
总结:若是童鞋们对SQL不太熟习的话那么选择此种方式最为合适的啦、嘿嘿
/** * @author Himi * @保存方式:Stream 数据流方式 * @注意1:默认状况下,使用openFileOutput 方法建立的文件只能被其调用的应用使用, * 其余应用没法读取这个文件,若是须要在不一样的应用中共享数据; * * @注意2:由于android os内部闪存有限,因此适合保存较少的数据,固然咱们也有解决的方法, * 就是把数据保存在SD开中,这样就能够了,后面我也会向你们讲解 ! * * @提醒1 调用FileOutputStream 时指定的文件不存在,Android 会自动建立它。 * 另外,在默认状况下,写入的时候会覆盖原 文件内容,若是想把新写入的内 * 容附加到原文件内容后,则能够指定其mode为Context.MODE_APPEND。 * * @提醒2 启动程序就初始化的时候必定要注意处理!代码中有注释!必定要仔细看! * * @提醒3 这里我给你们讲两种方式,一种是原生态file流来写入/读入, * 另一种是用Data流包装file流进行写入/读入 其实用data流来包装进行操做; * 缘由是:包装后支持了更多的写入/读入操做,好比:file流写入不支持 * writeUTF(String str); 可是用Data包装后就会支持。 * * @操做模式: Context.MODE_PRIVATE:新内容覆盖原内容 * Context.MODE_APPEND:新内容追加到原内容后 * Context.MODE_WORLD_READABLE:容许其余应用程序读取 * Context.MODE_WORLD_WRITEABLE:容许其余应用程序写入,会覆盖原数据。 */ public class MainActivity extends Activity implements OnClickListener { private EditText et_login, et_password; private Button btn_save; private TextView tv_title; private FileOutputStream fos; private FileInputStream fis; private DataOutputStream dos; private DataInputStream dis; @Override public void onCreate(Bundle savedInstanceState) { String temp = null; super.onCreate(savedInstanceState); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); this.requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.main); btn_save = (Button) findViewById(R.id.button_save); btn_save.setOnClickListener(this); et_login = (EditText) findViewById(R.id.editText_Login); et_password = (EditText) findViewById(R.id.editText_Password); tv_title = (TextView) findViewById(R.id.tv_title); try { // openFileInput 不像 sharedPreference 中 // getSharedPreferences的方法那样找不到会返回默认值, // 这里找不到数据文件就会报异常,因此finally里关闭流尤其重要!!! if (this.openFileInput("save.himi") != null) { // --------------单纯用file来读入的方式----------------- // fis = this.openFileInput("save.himi"); // ByteArrayOutputStream byteArray = new // ByteArrayOutputStream(); // byte[] buffer = new byte[1024]; // int len = 0; // while ((len = fis.read(buffer)) > 0) { // byteArray.write(buffer, 0, len); // } // temp = byteArray.toString(); // -------------- 用data流包装后的读入的方式------------ fis = this.openFileInput("save.himi");//备注1 dis = new DataInputStream(fis); et_login.setText(dis.readUTF()); et_password.setText(dis.readUTF()); // 这里也是在刚启动程序的时候去读入存储的数据 // 读的时候要注意顺序; 例如咱们写入数据的时候 //先写的字符串类型,咱们也要先读取字符串类型,一一对应! } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { // 在finally中关闭流!由于若是找不到数据就会异常咱们也能对其进行关闭操做 ; try { if (this.openFileInput("save.himi") != null) { // 这里也要判断,由于找不到的状况下,两种流也不会实例化。 // 既然没有实例化,还去调用close关闭它,确定"空指针"异常!!! fis.close(); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } @Override public void onClick(View v) { if (Environment.getExternalStorageState() != null) { // 这个方法在试探终端是否有sdcard! Log.v("Himi", "有SD卡"); } if (v == btn_save) { if (et_login.getText().toString().equals("")) tv_title.setText("请输入账号!"); else if (et_password.getText().toString().equals("")) tv_title.setText("请输入密码!"); else { try { // ------单纯用file来写入的方式-------------- //fos = new FileOutputStream(f); // fos.write(et_login.getText().toString().getBytes()); // fos.write(et_password.getText().toString().getBytes()); // ------data包装后来写入的方式-------------- fos = this.openFileOutput("save.himi", MODE_PRIVATE);//备注2 dos = new DataOutputStream(fos); dos.writeUTF(et_login.getText().toString()); dos.writeUTF(et_password.getText().toString()); tv_title.setText("保存成功!可从新打开此程序,测试是" + "否已经保存数据!/n(或者在'File Explorer'" + "窗口下-data-data-com.himi-files路径下" + "是否存在了'save.himi')"); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { // 在finally中关闭流 这样即便try中有异常咱们也能对其进行关闭操做 ; try { dos.close(); fos.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } } }
以上代码中实现了两种流形式来完成写入和读入,这里咱们为何要使用Data流来包装,其实不光是得到更多的操做方式,最主要的是方便快捷,你好比用file来读入的时候,明显的复杂了一些不说,它还一次性把全部数据都取出来了,不便于对数据的处理!
强调的有几点:
1: 在一开始对数据的访问再次提醒童鞋们,这个跟sharedPreference的获取方式不同,sharedPreference 的获取方式能够获得一个默认的值,可是你用我们获取的是个文件 并且直接就去open这个文件,一旦不存在一定异常,因此这一块的异常处理,以及finally的处理必定要处理得当。
2.其实在一开始用data包装的时候发现写入的字符串在读入的时候发现字符乱码了,查了api才发现,api规定当写入字符串的时候必须写入UTF-8格式的编码,可是后来不知道怎么了就没事了。 - -、因此这里若是童鞋们遇到此问题,我给出你们一个解决方法,就是在写入的时候咱们不要去DataOutputStream 来包装而是用,OutputStreamWriter ,由于在构造的能够设定编码!
OutputStreamWriter osw = new OutputStreamWriter(fis,"UTF-8");
String content = EncodingUtils.getString(buffer, "UTF-8"); 这个也能把字符数组转码制!
这样写入的就确定是UTF-8编码的字符啦、
下面介绍如何把咱们的数据经过 OutputStream/InputStream 存入SD卡中!
其实将咱们的数据放入SD卡中,无疑就须要对代码进行两处的修改:
注意:必定要有SD卡!对于如何建立SD卡在前一篇文章中已经说了两种方式,不会的童鞋能够去看下;
第一:检查是否装有SD卡;
第二: 修改读入的地方(备注1)
fis = this.openFileInput("save.himi"); //这里没有路径,路径是默认的 data-data-com.himi-files下
替换成咱们的SD卡的路径就能够了:
File path = new File("/sdcard/himi/save.himi");//这里新建一个File目录路径
fis = new FileInputStream(path);传入路径
第三 : 修改写入的地方(备注2)
fos = this.openFileOutput("save.himi", MODE_PRIVATE);这里也是默认路径,须要对其修改,
注意:这里修改了,那么在finally中的断定你们也要对应的适当修改;
注意:若是是系统路径,当没有此文件的时候,android 会默认建立一个!可是咱们放入SD卡的时候要本身建立目录路径和文件!
if (Environment.getExternalStorageState() != null) {// 这个方法在试探终端是否有sdcard! Log.v("Himi", "有SD卡"); File path = new File("/sdcard/himi");// 建立目录 File f = new File("/sdcard/himi/save.himi");// 建立文件 if (!path.exists()) {// 目录不存在返回false path.mkdirs();// 建立一个目录 } if (!f.exists()) {// 文件不存在返回false f.createNewFile();// 建立一个文件 } fos = new FileOutputStream(f);// 将数据存入sd卡中 }
第四: 由于咱们要在SD卡中进行写入的操做,因此要在配置文件中声明权限!
AndroidMainfest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.himi" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".MainActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-sdk android:minSdkVersion="4" /> </manifest>
这一句就是啦~
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
为了让你们看到所放的位置,因此把整个xml放出来供参考;
那么当建立路径和文件的时候,咱们对其检查SD卡中是否已经存在exists()方法 ,若是已经存在就不去建立,这样避免下次再次写入数据的时候又新建了文件和路径、
其实咱们在能够在启动程序的时候判断若是没有此文件,咱们能够直接紧接着建立一个文件,这些都属于优化上的了,我主要是让你们引入,学会,那么其余的简化啦,优化啦,其余方式去实现啦都留给各位同窗本身了、
OK、今天就先介绍到这里,后面会单独剖析SQLite如何存入数据,以及对数据操做的! 但愿你们继续关注!
(推荐你们订阅本博客,由于咱的更新速度但是很快的~娃哈哈)
源码下载地址: http://www.himigame.com/android-game/327.html
新的一年了小明祝福你们新的一年里,事业顺利,身体健康,全家幸福美满!
上张本项目的截图: