李华明Himi 原创,转载务必在明显处注明:
转载自 【黑米GameDev街区】 原文链接:  http://www.himigame.com/android-game/327.html

很多童鞋说我的代码运行后,点击home或者back后会程序异常,如果你也这样遇到过,那么你肯定没有仔细读完Himi的博文,第十九篇Himi专门写了关于这些错误的原因和解决方法,这里我在博客都补充说明下,省的童鞋们总疑惑这一块;请点击下面联系进入阅读:

【Android游戏开发十九】(必看篇)SurfaceView运行机制详解—剖析Back与Home按键及切入后台等异常处理!

对于游戏中的数据进行保存方式,在Android中常用的有四种保存方式,这里我先给大家统一先简单的介绍下:

1.  SharedPreference

此保存方式试用于简单数据的保存,文如其名属于配置性质的保存,不适合数据比较大的保存方式;

2. 文件存储 (FIleInputStream/FileOutputStream)

此保存方式比较适合游戏的保存和使用,可以保存较大的数据,因为相对于SQLite来说更容易让童鞋们接受,此方式不仅能把数据存储在系统中也能将数据保存到SDcard中;

3.SQLite 

此保存方式比较适合游戏的保存和使用,可以保存较大的数据,并且可以将自己的数据存储到文件系统或者数据库当中,也可以将自己的数据存储到SQLite数据库当中,也能将数据保存到SDcard中;

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;@Overridepublic 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 blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {// 在finally中关闭流!因为如果找不到数据就会异常我们也能对其进行关闭操作 ;try {if (this.openFileInput("save.himi") != null) {// 这里也要判断,因为找不到的情况下,两种流也不会实例化。// 既然没有实例化,还去调用close关闭它,肯定"空指针"异常!!!fis.close();}} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}@Overridepublic 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);//备注2dos = 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 blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {// 在finally中关闭流 这样即使try中有异常我们也能对其进行关闭操作 ;try {dos.close();fos.close();} catch (IOException e) {// TODO Auto-generated catch blocke.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()) {// 目录不存在返回falsepath.mkdirs();// 创建一个目录}if (!f.exists()) {// 文件不存在返回falsef.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

新的一年了小明祝福大家新的一年里,事业顺利,身体健康,全家幸福美满!

上张本项目的截图:

【Android游戏开发十二】(保存游戏数据 [上文])详解SharedPreference 与 FIleInputStream/FileOutputStream将数据存储到SD卡中!相关推荐

  1. 【正点原子Linux连载】第三十二章 U-Boot启动流程详解 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  2. Android绘图Canvas十八般武器之Shader详解及实战篇(上)

    本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 前言 Android中绘图离不开的就是Canvas了,Canvas是一个庞大的知识体系,有java层的,也有jni层深入到Frame ...

  3. android 访问存储卡,Android:无法访问存储在SD卡中的文件

    我正在研究一个读取存储在SD卡(内部/数据/本地)中的.txt文件的应用程序.我遇到的问题是,它似乎可以访问文件路径,但它不能访问文件本身(尽管它具有读取和写入权限),但会引发FileNotFound ...

  4. 【Visual C++】游戏开发笔记二十三 游戏基础物理建模 五 粒子系统模拟 二

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 本系列文 ...

  5. 【Visual C++】游戏开发笔记二十三 游戏基础物理建模(五) 粒子系统模拟(二)

    本系列文章由zhmxy555(毛星云)编写,转载请注明出处. http://blog.csdn.net/zhmxy555/article/details/7607916 作者:毛星云    邮箱: h ...

  6. XNA游戏开发之(六)——保存游戏数据

    [原创]Alex 游戏过程中,通常需要存储游戏状态.在下次启动游戏时,重新载入游戏,继续游戏. 实现过程非常简单,首先建立一个结构体定义需要保存的游戏参数,然后通过XmlSerializer类将其保存 ...

  7. 游戏开发(十二) 之 塔防 程序设计思路

    最近工作较忙,没有大块时间弄,东拼西凑出来点时间,昨天把基础游戏流程做完了,大概说下其中的开发重点. 1.数据驱动显示. 这点一定记得,很重要.之前看过很多人写塔防的战斗逻辑,几乎都是用moveto, ...

  8. Unity实战 RTS3D即时战略游戏开发(十二) 战斗AI的控制

    大家好,我是Zander,我们接着来开发Rts3D即时战略游戏开发.这节课我们接着来讲AI的相关部分:战斗AI. 游戏中要进行战斗,就需要让AI会攻击,首先在一定范围内如果有足够的单位则进行攻击,在A ...

  9. unity3D游戏开发十二之疯狂的小球

    下面我们通过一个具体的实例来了解如何使用物理引擎,该实例通过第三人称视角控制游戏中的小球对象,游戏通过是否与钻石碰撞来界定是否寻找到钻石并获得积分,获得积分满10分后,赢得游戏,当小球冲出跑道时,游戏 ...

最新文章

  1. 电子学会青少年编程等级考试Python一级题目解析11
  2. 负载均衡技术中的真集群和伪集群
  3. 友情链接交换看的不是“真友情”!
  4. linux nice线程,linux nice 线程
  5. 【TensorFlow】ValueError: Shape must be rank 1 but is rank 0 for ' ’ with input shapes: [].问题
  6. 引用dll动态库,动态库中弹出对话框输入,将输入参数,作为变量继续调用。...
  7. jar打包 剔除第三方依赖以及它的依赖_面试官:为什么Spring Boot的jar可以直接运行?...
  8. Liferay MinifierFilter的研究
  9. gitea无法连接mysql_Git push ERROR: Repository not found fatal:无法连接远程数据库
  10. 概率论-随机事件及其概率
  11. Java nginx 双向ssl_nginx配置ssl双向验证 nginx https ssl证书配置
  12. C++循环的内存释放问题?
  13. 10年经验17张图带你进入gitflow企业项目代码版本管理的最佳实践
  14. stm32mp1 Cortex M4开发篇6:TIM定时器中断
  15. 绝地求生 java 雷达透视_绝地求生jar雷达透视辅助
  16. EVEREST - 测试软硬件系统信息的工具
  17. python绘制科赫雪花and科赫雪花进阶版
  18. 批流融合系统-SparkV2/Beam
  19. pg_hint_plan 使用hint固定SQL执行计划
  20. 【计算机网络】网线规格的鉴别与接线方法

热门文章

  1. ImportError: cannot import name ‘PILLOW_VERSION‘ from ‘PIL‘ (/Users/bei/miniforge3/envs/pytorch/lib/
  2. D1. Great Vova Wall (Version 1)
  3. 简单的笔刷效果【OpenGL】
  4. 10-13 查询各专业学生的平均成绩(MSSQL)
  5. js设置字体大小自适应屏幕分辨率
  6. Flutter Widgets: Container
  7. 网络正常,“但”edge浏览器网页拒绝访问请“检查代理”,解决方案-DNS设置
  8. 第十六届全国大学生智能车竞赛全国总决赛名单-公示版
  9. 管理企业的书籍推荐,做好企业管理必备!
  10. 使用 Room 将数据保存到本地数据库学习日志+demo