好久没有写项目、开发相关的内容了,刚好最近在做项目的更新时,遇到一个比较有意思的坑就随手记录一下。

  因为项目的上一上线版本是由Unity5.3发的包,而最新的项目来不及同步更新到5.3版本发包测试,所以只好仍然使用老的Unity版本4.3进行发包,然后,问题来了:PlayerPrefs的存储方式变了,新版本Unity5向下兼容Unity4,但是想再回来的时候,数据没办法了。
  先说一下Android平台上apk的数据存储位置(针对Unity4的版本):
安装在内置的flash存储器上的时候,PlayerPrefs的数据存储位置是\data\data\com.company.product\shared_prefs\com.company.product.xml
安装在内置的SD卡存储器上的时候,PlayerPrefs的存储位置是\sdcard\Android\data\com.company.product\shared_prefs\com.company.product.xml
(查看及获取该文件需要权限)

  其中shared_prefs是SharedPreferences的缩写,是Android平台提供的一个轻量级存储类,可以保存应用的一些常用配置,比如Activity的状态等,Activity暂停时其状态就会保存到SharedPereferences中;当Activity重载时,系统回调方法onSaveInstanceState时,会再从SharedPreferences中将值取出。(SharedPreferences可以用来进行数据共享,包括应用程序之间、一个应用的不同组件之间等。eg:两个activity除了可以通过Intent传递数据,也可以通过ShreadPreferences来共享数据)。

而在升级到Unity5之后,底层数据的存储位置发生了变化,数据存储文件变成了com.company.product.v2.playerprefs.xml,并且数据的存储方式也有所改变,在Unity4的时候,PlayerPrefs会将数据直接进行存储,在Unity5中,数据存储时会先进行一步Url编码,比如key或value值中含有“=”号的,在存储时会被编码为%3D,因此,要做到Unity4的向上兼容,数据的编码解码也要考虑进去。下面直接上代码:
Ps:是否安装到sd卡由PlayerSetting->Other Settings->Install Location的设置决定;在Windows平台下,PlayerPrefs被存储在注册表的HKEY_CURRENT_USER\Software\[company name]\[product name]中,company和product名由Project Setting的设置决定。

java端简化逻辑:

 public class UnityPlayerClient extends UnityPlayerActivity
{
  protected static final String TAG = "UnityPlayerClient";   @Override
  protected void onCreate(Bundle savedInstanceState)
  {
    super.onCreate(savedInstanceState);
  } // protected SharedPreferences curPlayerPrefs = null;
// public SharedPreferences CurPlayerPrefs()
// {
// if(null == curPlayerPrefs)
// {
// curPlayerPrefs = this.getSharedPreferences(this.getPackageName() + ".v2.playerprefs", Context.MODE_PRIVATE);
// }
// return curPlayerPrefs;
// }   protected static boolean isUnityVersionNew = false;
  protected static SharedPreferences playerPrefs = null;
  public static SharedPreferences PlayerPrefs()
  {
    if(null == playerPrefs)
    {
      Context tAppContext = UnityPlayer.currentActivity.getApplicationContext();
      String tPath = "/data/data/" + tAppContext.getPackageName() + "/shared_prefs/" + tAppContext.getPackageName() + ".v2.playerprefs.xml";
      File tFile = new File(tPath);
      if(tFile.exists())
      {
        isUnityVersionNew = true;
        playerPrefs = tAppContext.getSharedPreferences(tAppContext.getPackageName() + ".v2.playerprefs", Context.MODE_PRIVATE);
      }
      else
      {
        isUnityVersionNew = false;
        playerPrefs = tAppContext.getSharedPreferences(tAppContext.getPackageName(), Context.MODE_PRIVATE);
39       }
    }
    return playerPrefs;
  }
  private static String handleKey(String _key)
  {
    try
    {
      _key = isUnityVersionNew ? URLEncoder.encode(_key, "utf-8") : _key;
    }
    catch (Exception e)
    {     }
    return _key;
  }
  private static String handleValue(String _value)
  {
    try
    {
      _value = isUnityVersionNew ? URLDecoder.decode(_value, "utf-8") : _value;
    }
    catch (Exception e)
    {     }
    return _value;
  }
  public static String GetString(String _key)
  {
    return handleValue(PlayerPrefs().getString(handleKey(_key), ""));
  }
  public static String GetString(String _key, String _defaultValue)
  {
    return handleValue(PlayerPrefs().getString(handleKey(_key), _defaultValue));
  }
  public static boolean SetString(String _key, String _value)
  {
    return PlayerPrefs().edit().putString(handleKey(_key), handleKey(_value)).commit();
  }   public static int GetInt(String _key)
  {
    return PlayerPrefs().getInt(handleKey(_key), 0);
  }
  public static int GetInt(String _key, int _defaultValue)
  {
    return PlayerPrefs().getInt(handleKey(_key), _defaultValue);
  }
  public static boolean SetInt(String _key, int _value)
  {
    return PlayerPrefs().edit().putInt(handleKey(_key), _value).commit();
  }   public static float GetFloat(String _key)
  {
    return PlayerPrefs().getFloat(handleKey(_key), 0);
  }
  public static float GetFloat(String _key, float _defaultValue)
  {
    return PlayerPrefs().getFloat(handleKey(_key), _defaultValue);
  }
  public static boolean SetFloat(String _key, float _value)
  {
    return PlayerPrefs().edit().putFloat(handleKey(_key), _value).commit();
  }   public static boolean GetBool(String _key)
  {
    return PlayerPrefs().getBoolean(handleKey(_key), false);
  }
  public static boolean GetBool(String _key, boolean _defaultValue)
  {
    return PlayerPrefs().getBoolean(handleKey(_key), _defaultValue);
  }
  public static boolean SetBool(String _key, boolean _value)
  {
    return PlayerPrefs().edit().putBoolean(handleKey(_key), _value).commit();
  }   public static boolean HasKey(String _key)
  {
    return PlayerPrefs().contains(handleKey(_key));
  }
  public static boolean DeleteKey(String _key)
  {
    return PlayerPrefs().edit().remove(handleKey(_key)).commit();
  }
  public static boolean DeleteAll()
  {
    return PlayerPrefs().edit().clear().commit();
  }
}

Unity端简化逻辑

 public class LocalInfoClient : IDataSave
{
  private LocalInfoClient() { } //泛型单件   private IDataSave _curDataSave = null;
  protected IDataSave curDataSave
  {
    get
    {
      if(null == _curDataSave)
      {
#if UNITY_EDITOR
        _curDataSave = new InfoWithoutEncry();
#elif (UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7 || UNITY_4_8 || UNITY_4_9)
        if(true) //从项目配置文件中读取
        {
          _curDataSave = new Info4UnityNew();
        }
        else
        {
          _curDataSave = new InfoBase();
        }
#else
        _curDataSave = new InfoBase();
#endif
      }
      return _curDataSave;
    }
  }   public void SetInt(string _key, int _value)
  {
    curDataSave.SetInt(_key, _value);
  }   public int GetInt(string _key, int _defaultValue)
  {
    return curDataSave.GetInt(_key, _defaultValue);
  }   public void SetString(string _key, string _value)
  {
    curDataSave.SetString(_key, _value);
  }   public string GetString(string _key, string _defaultValue)
  {
    return curDataSave.GetString(_key, _defaultValue);
  }   public void SetFloat(string _key, float _value)
  {
    curDataSave.SetFloat(_key, _value);
  }   public float GetFloat(string _key, float _defaultValue)
  {
    return curDataSave.GetFloat(_key, _defaultValue);
  }   public bool HasKey(string _key)
  {
    return curDataSave.HasKey(_key);
  }   public void DeleteKey(string _key)
  {
    curDataSave.DeleteKey(_key);
  }   public void DeleteAll()
  {
    curDataSave.DeleteAll();
  }   public void SetBool(string _key, bool _value)
  {
    curDataSave.SetBool(_key, _value);
  }   public bool GetBool(string _key, bool _defaultValue)
  {
    return curDataSave.GetBool(_key, _defaultValue);
  }
} public interface IDataSave
{
  void SetInt(string _key, int _value);
  int GetInt(string _key, int _defaultValue);   void SetString(string _key, string _value);
  string GetString(string _key, string _defaultValue);   void SetFloat(string _key, float _value);
  float GetFloat(string _key, float _defaultValue);   bool HasKey(string _key);
  void DeleteKey(string _key);
  void DeleteAll();   void SetBool(string _key, bool _value);
  bool GetBool(string _key, bool _defaultValue);
}

其中Info4UnityNew类为实现了IDataSave接口并负责与java端UnityPlayerClient通信的一种封装。

Unity4向上(Unity5)兼容PlayerPrefs的数据存储的更多相关文章

  1. 【转】 [Unity3D]手机3D游戏开发:场景切换与数据存储(PlayerPrefs 类的介绍与使用)

    http://blog.csdn.net/pleasecallmewhy/article/details/8543181 在Unity中的数据存储和iOS中字典的存储基本相同,是通过关键字实现数据存储 ...

  2. IOS数据存储之Sqlite数据库

    前言: 之前学习了数据存储的NSUserDefaults,归档和解档,沙盒文件存储,但是对于数据量比较大,需要频繁查询,删除,更新等操作的时候无论从效率上还是性能上,上述三种明显不能满足我们的日常开发 ...

  3. 第十二章:Android数据存储(下)

    一.SQLite介绍 提到数据存储问题,数据库是不得不提的.数据库是用来存储关系型数据的不二利器.Android为开发者提供了强大的数据库支持,可以用来轻松地构造基于数据库的应用.Android的数据 ...

  4. Android基础总结(5)——数据存储,持久化技术

    瞬时数据:指那些存储在内存当中,有可能会因为程序广播或其他原因导致内存被回收而丢失的数据. 数据持久化:指将那些内存中的瞬时数据保存到存储设备中,保证即使在手机或电脑关机的情况下,这些数据仍然不丢失. ...

  5. linux高级数据存储

    linux内此存储模式由5部分组成,自低向上的顺序: 物理卷,内核块设备驱动,内核文件系统驱动,虚拟文件系统,应用程序数据结构; 系统中所有的文件仅按此模式存储,无论是数据还是元数据,均在此模式下统一 ...

  6. Base-Android快速开发框架(三)--数据存储之SQLite

    SQLite,是一款轻量级的关系型数据库,Android原生集成的一个数据库.具有轻量级.独立性.隔离性.安全性等特点.是Android做数据存储的必备知识之一. 在实际的项目中,我们常用于一些对象的 ...

  7. H5本地存储详细使用教程(localStorage + JSON数据存储应用框架)

    一.Web Storage教程 1.概述: 对于Web Storage来说,实际上是Cookies存储的进化版.如果了解Cookie的人几乎一看Web Storage就会用,如果你从来没用过没了解过C ...

  8. Android 数据存储02之文件读写

    Android文件读写 版本 修改内容 日期 修改人 V1.0 原始版本 2013/2/25 skywang Android文件读写的有两种方式.一种,是通过标准的JavaIO库去读写.另一种,是通过 ...

  9. 51单片机RAM 数据存储区学习笔记

    转自:http://www.eepw.com.cn/article/216237_2.htm 1.RAM keil C语言编程 RAM是程序运行中存放随机变量的数据空间.在keil中编写程序,如果当前 ...

随机推荐

  1. IndexReader和IndexWriter的生命周期

    http://youyang-java.iteye.com/blog/1731205 对于IndexReader而言,反复使用 IndexReader .open打开会有很大的开销,所以一般在整个程序 ...

  2. 同步两台linux服务器时间同步方案

    Linux自带了ntp服务 -- /etc/init.d/ntpd,这个服务不仅可以设置让本机和某台/某些机器做时间同步,他本身还可以扮演一个time server的角色,让其他机器和他同步时间. 配 ...

  3. Standard Attachments in Oracle Form 标准附件

    Standard Attachments in Oracle Form 默认情况下"附件"按钮是灰色的,本文将展示如何让某个Form的附件按钮变亮,并能上传附件. 以用户Form为 ...

  4. Linux kernel ‘uio_mmap_physical’函数缓冲区溢出漏洞

    漏洞名称: Linux kernel ‘uio_mmap_physical’函数缓冲区溢出漏洞 CNNVD编号: CNNVD-201311-154 发布时间: 2013-11-13 更新时间: 201 ...

  5. 【转】MFC中用CFile读取和写入文件2

    原文网址:http://blog.sina.com.cn/s/blog_623a7fa40100hh1u.html CFile提供了一些常用的操作函数,如表1-2所示. 表1-2  CFile操作函数 ...

  6. (转载)CentOS: 开放80、22、3306端口操作

    (转载)http://blog.sina.com.cn/s/blog_3eba8f1c0100tsox.html #/sbin/iptables -I INPUT -p tcp --dport 80 ...

  7. Servlet3.0学习总结(三)——基于Servlet3.0的文件上传

    在Servlet2.5中,我们要实现文件上传功能时,一般都需要借助第三方开源组件,例如Apache的commons-fileupload组件,在Servlet3.0中提供了对文件上传的原生支持,我们不 ...

  8. STL总结之list

    STL中list和我们传统意义上的链表一样可以进行动态节点添加和释放. 优点:适合动态删除和添加 缺点:不支持随机访问.   C++标准对list模板声明: template < class T ...

  9. LoadRunner调用Java程序—性能测试

    为了充分利用LoadRunner的场景控制和分析器,帮助我们更好地控制脚本加载过程,从而展现更直观有效的场景分析图表.本次将重点讨论LoadRunner如何调用Java测试代码,完成压力测试. 通常我 ...

  10. 正则表达式(来源http://deerchao.net/tutorials/regex/regex.htm)

    目录 跳过目录 本文目标 如何使用本教程 正则表达式到底是什么东西? 入门 测试正则表达式 元字符 字符转义 重复 字符类 分枝条件 反义 分组 后向引用 零宽断言 负向零宽断言 注释 贪婪与懒惰 处 ...