android开发游记:使用sharepreference存储复杂对象的解决方案
2015-11-09 15:55
465 查看
sharepreference是android系统中的一种数据持久化的解决方案,我们经常用它来存储APP的配置信息和一些简单数据类型,但是我们可不可以用它来存放复杂数据类型呢?
sharepreference保存数据是按照健-值的形式的,不支持复杂数据类型的存储,但是我们可以把复杂数据类型转换成简单数据类型进行存储,最实际的就是转换为具有特殊意义的字符串,然后使用sharepreference形式来进行持久化,提前数据的时候就把字符串提取出来再转换成原来的类型就可以。
先写一个方法用于把byte转为十六进制字符串:
上面的代码首先进行判空后将byte进行遍历逐位取出,通过Integer.toHexString方法转换为16进制的字符串再进行拼接,仔细看可能注意到我把byte的每一位与0xFF进行了与运算。
可以看出bArray[i] & 0xFF运算后得出的仍然是个int,那么为何要和 0xFF进行与运算呢?直接 Integer.toHexString(b[i]),将byte强转为int不行吗?答案是不行的。
原因是为什么呢?
byte所占大小为8位而int型占32位
java的二进制数据存储采用的是补码
如果不进行&0xff,那么当一个byte会转换成int时,由于int是32位,而byte只有8位这时会进行补位,
例如补码11111111的十进制数为-1转换为int时变为11111111111111111111111111111111好多1啊,呵呵!即0xffffffff但是这个数是不对的,这种补位就会造成误差。
和0xff相与后,高24比特就会被清0了,结果就对了。
如果不清楚补码的话,就该复习下计算机基础理论了,下面贴上一些讲解,不行了解底层原理的可以跳过了:
byte是一个字节保存的,有8个位,即8个0、1。
8位的第一个位是符号位,
也就是说0000 0001代表的是数字1
1000 0000代表的就是-1
所以正数最大位0111 1111,也就是数字127
负数最大为1111 1111,也就是数字-128
上面说的是二进制原码,但是在java中采用的是补码的形式,下面介绍下什么是补码
1、反码:
一个数如果是正,则它的反码与原码相同;
一个数如果是负,则符号位为1,其余各位是对原码取反;
2、补码:利用溢出,我们可以将减法变成加法
对于十进制数,从9得到5可用减法:
9-4=5 因为4+6=10,我们可以将6作为4的补数
改写为加法:
9+6=15(去掉高位1,也就是减10)得到5.
对于十六进制数,从c到5可用减法:
c-7=5 因为7+9=16 将9作为7的补数
改写为加法:
c+9=15(去掉高位1,也就是减16)得到5.
在计算机中,如果我们用1个字节表示一个数,一个字节有8位,超过8位就进1,在内存中情况为(100000000),进位1被丢弃。
⑴一个数为正,则它的原码、反码、补码相同
⑵一个数为负,刚符号位为1,其余各位是对原码取反,然后整个数加1
1的原码为 10000001
1的反码为 11111110
+ 1
1的补码为 11111111
0的原码为 00000000
0的反码为 11111111(正零和负零的反码相同)
+1
0的补码为 100000000(舍掉打头的1,正零和负零的补码相同)
接着是保存复杂数据类型的方法,必须要说明的是,该类型必须实现Serializable可序列化接口:
上面第方法首先把传入的对象转换为字节流,再调用上面实现的字节转16进制字符串的方法将字节流转为字符串后进行保存。
现在保存实现了,那么接下来就是读取了
首先,先实现一个方法把16进制字符串转换为byte数组:
这里要先将16进制的字符逐一取出转换为16进制数,再转为10进制最后转为byte,存入数组,这里byte转int就不用进行与运算了,因为低精度转高精度不会丢失精度。
接下来,是通过sharepreference方式获取存入的复杂对象的方法:
将存入的16进制字符串取出调用上面的字符串转byte[]的方法得到byte数组,再通过字节流生成一个对象,对象是Object类型,直接返回,在调用方法后自行进行强制转换,转换为原来的类型即可。
到这里,我们就实现了使用sharepreference存储复杂对象。
sharepreference保存数据是按照健-值的形式的,不支持复杂数据类型的存储,但是我们可以把复杂数据类型转换成简单数据类型进行存储,最实际的就是转换为具有特殊意义的字符串,然后使用sharepreference形式来进行持久化,提前数据的时候就把字符串提取出来再转换成原来的类型就可以。
先写一个方法用于把byte转为十六进制字符串:
[code]/** * desc:将数组转为16进制 * * @param bArray * @return modified: */ private static String bytesToHexString(byte[] bArray) { if (bArray == null) { return null; } if (bArray.length == 0) { return ""; } StringBuffer sb = new StringBuffer(bArray.length); String sTemp; for (int i = 0; i < bArray.length; i++) { sTemp = Integer.toHexString(0xFF & bArray[i]); if (sTemp.length() < 2) sb.append(0); sb.append(sTemp.toUpperCase()); } return sb.toString(); }
上面的代码首先进行判空后将byte进行遍历逐位取出,通过Integer.toHexString方法转换为16进制的字符串再进行拼接,仔细看可能注意到我把byte的每一位与0xFF进行了与运算。
可以看出bArray[i] & 0xFF运算后得出的仍然是个int,那么为何要和 0xFF进行与运算呢?直接 Integer.toHexString(b[i]),将byte强转为int不行吗?答案是不行的。
原因是为什么呢?
byte所占大小为8位而int型占32位
java的二进制数据存储采用的是补码
如果不进行&0xff,那么当一个byte会转换成int时,由于int是32位,而byte只有8位这时会进行补位,
例如补码11111111的十进制数为-1转换为int时变为11111111111111111111111111111111好多1啊,呵呵!即0xffffffff但是这个数是不对的,这种补位就会造成误差。
和0xff相与后,高24比特就会被清0了,结果就对了。
如果不清楚补码的话,就该复习下计算机基础理论了,下面贴上一些讲解,不行了解底层原理的可以跳过了:
byte是一个字节保存的,有8个位,即8个0、1。
8位的第一个位是符号位,
也就是说0000 0001代表的是数字1
1000 0000代表的就是-1
所以正数最大位0111 1111,也就是数字127
负数最大为1111 1111,也就是数字-128
上面说的是二进制原码,但是在java中采用的是补码的形式,下面介绍下什么是补码
1、反码:
一个数如果是正,则它的反码与原码相同;
一个数如果是负,则符号位为1,其余各位是对原码取反;
2、补码:利用溢出,我们可以将减法变成加法
对于十进制数,从9得到5可用减法:
9-4=5 因为4+6=10,我们可以将6作为4的补数
改写为加法:
9+6=15(去掉高位1,也就是减10)得到5.
对于十六进制数,从c到5可用减法:
c-7=5 因为7+9=16 将9作为7的补数
改写为加法:
c+9=15(去掉高位1,也就是减16)得到5.
在计算机中,如果我们用1个字节表示一个数,一个字节有8位,超过8位就进1,在内存中情况为(100000000),进位1被丢弃。
⑴一个数为正,则它的原码、反码、补码相同
⑵一个数为负,刚符号位为1,其余各位是对原码取反,然后整个数加1
1的原码为 10000001
1的反码为 11111110
+ 1
1的补码为 11111111
0的原码为 00000000
0的反码为 11111111(正零和负零的反码相同)
+1
0的补码为 100000000(舍掉打头的1,正零和负零的补码相同)
接着是保存复杂数据类型的方法,必须要说明的是,该类型必须实现Serializable可序列化接口:
[code] /** * desc:保存对象 * * @param context * @param key * @param obj * 要保存的对象,只能保存实现了serializable的对象 modified: */ public static void saveObject(Context context, String key, Object obj) { try { // 保存对象 SharedPreferences.Editor sharedata = context.getSharedPreferences("user", Context.MODE_PRIVATE).edit(); // 先将序列化结果写到byte缓存中,其实就分配一个内存空间 ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream os = new ObjectOutputStream(bos); // 将对象序列化写入byte缓存 os.writeObject(obj); // 将序列化的数据转为16进制保存 String bytesToHexString = bytesToHexString(bos.toByteArray()); // 保存该16进制数组 sharedata.putString(key, bytesToHexString); sharedata.commit(); } catch (IOException e) { e.printStackTrace(); Log.e("", "保存obj失败"); } }
上面第方法首先把传入的对象转换为字节流,再调用上面实现的字节转16进制字符串的方法将字节流转为字符串后进行保存。
现在保存实现了,那么接下来就是读取了
首先,先实现一个方法把16进制字符串转换为byte数组:
[code]/** * desc:将16进制的数据转为数组 * * @param data * @return modified: */ private static byte[] StringToBytes(String data) { String hexString = data.toUpperCase().trim(); if (hexString.length() % 2 != 0) { return null; } byte[] retData = new byte[hexString.length() / 2]; for (int i = 0; i < hexString.length(); i++) { int int_ch; // 两位16进制数转化后的10进制数 char hex_char1 = hexString.charAt(i); // 两位16进制数中的第一位(高位*16) int int_ch1; if (hex_char1 >= '0' && hex_char1 <= '9') int_ch1 = (hex_char1 - 48) * 16; // // 0 的Ascll - 48 else if (hex_char1 >= 'A' && hex_char1 <= 'F') int_ch1 = (hex_char1 - 55) * 16; // // A 的Ascll - 65 else return null; i++; char hex_char2 = hexString.charAt(i); // /两位16进制数中的第二位(低位) int int_ch2; if (hex_char2 >= '0' && hex_char2 <= '9') int_ch2 = (hex_char2 - 48); // // 0 的Ascll - 48 else if (hex_char2 >= 'A' && hex_char2 <= 'F') int_ch2 = hex_char2 - 55; // // A 的Ascll - 65 else return null; int_ch = int_ch1 + int_ch2; retData[i / 2] = (byte) int_ch;// 将转化后的数放入Byte里 } return retData; }
这里要先将16进制的字符逐一取出转换为16进制数,再转为10进制最后转为byte,存入数组,这里byte转int就不用进行与运算了,因为低精度转高精度不会丢失精度。
接下来,是通过sharepreference方式获取存入的复杂对象的方法:
[code]/** * desc:获取保存的Object对象 * * @param context * @param key * @return modified: */ public static Object readObject(Context context, String key) { try { SharedPreferences sharedata = context.getSharedPreferences("user", Context.MODE_PRIVATE); if (sharedata.contains(key)) { String string = sharedata.getString(key, ""); if (TextUtils.isEmpty(string)) { return null; } else { // 将16进制的数据转为数组,准备反序列化 byte[] stringToBytes = StringToBytes(string); ByteArrayInputStream bis = new ByteArrayInputStream(stringToBytes); ObjectInputStream is = new ObjectInputStream(bis); // 返回反序列化得到的对象 Object readObject = is.readObject(); return readObject; } } } catch (StreamCorruptedException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } // 所有异常返回null return null; }
将存入的16进制字符串取出调用上面的字符串转byte[]的方法得到byte数组,再通过字节流生成一个对象,对象是Object类型,直接返回,在调用方法后自行进行强制转换,转换为原来的类型即可。
到这里,我们就实现了使用sharepreference存储复杂对象。
相关文章推荐
- Android 利用gson将json字符串数组转陈对象的方法
- 安卓权限配置问题
- android drawBitmapMesh
- 如何打开Android X86对houdini的支持
- Android性能优化典范C1
- Android fill_parent、wrap_content和match_parent的区别(转载整理)
- Android的init过程(二):初始化语言(init.rc)解析
- Android 使用MediaRecorder录音调用stop()方法的时候报错
- android中string.xml中%1$s、%1$d等的用法
- Android 原生listview item伸展收缩效果
- Android的init过程详解(一)
- Android常用Json解析框架GSON和FastJson的使用
- Android 关于启动过程的一些文章
- Android Fragment 真正的完全解析(上)
- Android Studio讲解二、快捷键和常用插件
- Android listview 禁止滑动
- 10个经典的Android开源应用项目
- Android开发之SurfaceView详解
- Android 热补丁和热修复
- Android init.rc文件浅析