好久没有写项目、开发相关的内容了,刚好最近在做项目的更新时,遇到一个比较有意思的坑就随手记录一下。
因为项目的上一上线版本是由Unity5.3发的包,而最新的项目来不及同步更新到5.3版本发包测试,所以只好仍然使用老的Unity版本4.3进行发包,然后,问题来了:PlayerPrefs的存储方式变了,新版本Unity5向下兼容Unity4,但是想再回来的时候,数据没办法了。
先说一下Android平台上apk的数据存储位置(针对Unity4的版本):
安装在内置的flash存储器上的时候,PlayerPrefs的数据存储位置是datadatacom.company.productshared_prefscom.company.product.xml
安装在内置的SD卡存储器上的时候,PlayerPrefs的存储位置是sdcardAndroiddatacom.company.productshared_prefscom.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_USERSoftware[company name][product name]中,company和product名由Project Setting的设置决定。
java端简化逻辑:
1 public class UnityPlayerClient extends UnityPlayerActivity 2 { 3 protected static final String TAG = "UnityPlayerClient"; 4 5 @Override 6 protected void onCreate(Bundle savedInstanceState) 7 { 8 super.onCreate(savedInstanceState); 9 } 10 11 // protected SharedPreferences curPlayerPrefs = null; 12 // public SharedPreferences CurPlayerPrefs() 13 // { 14 // if(null == curPlayerPrefs) 15 // { 16 // curPlayerPrefs = this.getSharedPreferences(this.getPackageName() + ".v2.playerprefs", Context.MODE_PRIVATE); 17 // } 18 // return curPlayerPrefs; 19 // } 20 21 protected static boolean isUnityVersionNew = false; 22 protected static SharedPreferences playerPrefs = null; 23 public static SharedPreferences PlayerPrefs() 24 { 25 if(null == playerPrefs) 26 { 27 Context tAppContext = UnityPlayer.currentActivity.getApplicationContext(); 28 String tPath = "/data/data/" + tAppContext.getPackageName() + "/shared_prefs/" + tAppContext.getPackageName() + ".v2.playerprefs.xml"; 29 File tFile = new File(tPath); 30 if(tFile.exists()) 31 { 32 isUnityVersionNew = true; 33 playerPrefs = tAppContext.getSharedPreferences(tAppContext.getPackageName() + ".v2.playerprefs", Context.MODE_PRIVATE); 34 } 35 else 36 { 37 isUnityVersionNew = false; 38 playerPrefs = tAppContext.getSharedPreferences(tAppContext.getPackageName(), Context.MODE_PRIVATE); 39 } 40 } 41 return playerPrefs; 42 } 43 private static String handleKey(String _key) 44 { 45 try 46 { 47 _key = isUnityVersionNew ? URLEncoder.encode(_key, "utf-8") : _key; 48 } 49 catch (Exception e) 50 { 51 52 } 53 return _key; 54 } 55 private static String handleValue(String _value) 56 { 57 try 58 { 59 _value = isUnityVersionNew ? URLDecoder.decode(_value, "utf-8") : _value; 60 } 61 catch (Exception e) 62 { 63 64 } 65 return _value; 66 } 67 public static String GetString(String _key) 68 { 69 return handleValue(PlayerPrefs().getString(handleKey(_key), "")); 70 } 71 public static String GetString(String _key, String _defaultValue) 72 { 73 return handleValue(PlayerPrefs().getString(handleKey(_key), _defaultValue)); 74 } 75 public static boolean SetString(String _key, String _value) 76 { 77 return PlayerPrefs().edit().putString(handleKey(_key), handleKey(_value)).commit(); 78 } 79 80 public static int GetInt(String _key) 81 { 82 return PlayerPrefs().getInt(handleKey(_key), 0); 83 } 84 public static int GetInt(String _key, int _defaultValue) 85 { 86 return PlayerPrefs().getInt(handleKey(_key), _defaultValue); 87 } 88 public static boolean SetInt(String _key, int _value) 89 { 90 return PlayerPrefs().edit().putInt(handleKey(_key), _value).commit(); 91 } 92 93 public static float GetFloat(String _key) 94 { 95 return PlayerPrefs().getFloat(handleKey(_key), 0); 96 } 97 public static float GetFloat(String _key, float _defaultValue) 98 { 99 return PlayerPrefs().getFloat(handleKey(_key), _defaultValue); 100 } 101 public static boolean SetFloat(String _key, float _value) 102 { 103 return PlayerPrefs().edit().putFloat(handleKey(_key), _value).commit(); 104 } 105 106 public static boolean GetBool(String _key) 107 { 108 return PlayerPrefs().getBoolean(handleKey(_key), false); 109 } 110 public static boolean GetBool(String _key, boolean _defaultValue) 111 { 112 return PlayerPrefs().getBoolean(handleKey(_key), _defaultValue); 113 } 114 public static boolean SetBool(String _key, boolean _value) 115 { 116 return PlayerPrefs().edit().putBoolean(handleKey(_key), _value).commit(); 117 } 118 119 public static boolean HasKey(String _key) 120 { 121 return PlayerPrefs().contains(handleKey(_key)); 122 } 123 public static boolean DeleteKey(String _key) 124 { 125 return PlayerPrefs().edit().remove(handleKey(_key)).commit(); 126 } 127 public static boolean DeleteAll() 128 { 129 return PlayerPrefs().edit().clear().commit(); 130 } 131 }
Unity端简化逻辑
1 public class LocalInfoClient : IDataSave 2 { 3 private LocalInfoClient() { } //泛型单件 4 5 private IDataSave _curDataSave = null; 6 protected IDataSave curDataSave 7 { 8 get 9 { 10 if(null == _curDataSave) 11 { 12 #if UNITY_EDITOR 13 _curDataSave = new InfoWithoutEncry(); 14 #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) 15 if(true) //从项目配置文件中读取 16 { 17 _curDataSave = new Info4UnityNew(); 18 } 19 else 20 { 21 _curDataSave = new InfoBase(); 22 } 23 #else 24 _curDataSave = new InfoBase(); 25 #endif 26 } 27 return _curDataSave; 28 } 29 } 30 31 public void SetInt(string _key, int _value) 32 { 33 curDataSave.SetInt(_key, _value); 34 } 35 36 public int GetInt(string _key, int _defaultValue) 37 { 38 return curDataSave.GetInt(_key, _defaultValue); 39 } 40 41 public void SetString(string _key, string _value) 42 { 43 curDataSave.SetString(_key, _value); 44 } 45 46 public string GetString(string _key, string _defaultValue) 47 { 48 return curDataSave.GetString(_key, _defaultValue); 49 } 50 51 public void SetFloat(string _key, float _value) 52 { 53 curDataSave.SetFloat(_key, _value); 54 } 55 56 public float GetFloat(string _key, float _defaultValue) 57 { 58 return curDataSave.GetFloat(_key, _defaultValue); 59 } 60 61 public bool HasKey(string _key) 62 { 63 return curDataSave.HasKey(_key); 64 } 65 66 public void DeleteKey(string _key) 67 { 68 curDataSave.DeleteKey(_key); 69 } 70 71 public void DeleteAll() 72 { 73 curDataSave.DeleteAll(); 74 } 75 76 public void SetBool(string _key, bool _value) 77 { 78 curDataSave.SetBool(_key, _value); 79 } 80 81 public bool GetBool(string _key, bool _defaultValue) 82 { 83 return curDataSave.GetBool(_key, _defaultValue); 84 } 85 } 86 87 88 public interface IDataSave 89 { 90 void SetInt(string _key, int _value); 91 int GetInt(string _key, int _defaultValue); 92 93 void SetString(string _key, string _value); 94 string GetString(string _key, string _defaultValue); 95 96 void SetFloat(string _key, float _value); 97 float GetFloat(string _key, float _defaultValue); 98 99 bool HasKey(string _key); 100 void DeleteKey(string _key); 101 void DeleteAll(); 102 103 void SetBool(string _key, bool _value); 104 bool GetBool(string _key, bool _defaultValue); 105 }
其中Info4UnityNew类为实现了IDataSave接口并负责与java端UnityPlayerClient通信的一种封装。