前言
手上的一个项目,在我测试用的手机上都正常,今天在同事手机上登录失败.测试机系统版本是Android4.4,出现问题的手机系统版本是Android5.0,测试排查之后发现Android系统版本上面map内顺序排列不同的原因导致.
错误原因分析
项目是采用socket通信,访问接口时需要给服务器发送一些信息,这些信息在写入流之前是以字符串的格式存在的
这个数据字符串里面有一个字段拼接,这个顺序需要严格和服务器保持一致,
逻辑是:
1.按照规定的顺序放到一个map里面,然后循环取出拼接,
2.循环取出拼接(因为是列表是封装的公共方法,需要对应不同的接口参数,所以用的是反射得到取出)
然而这是错的,错的!!!!原因在于:HashMap是无序排列的!尽管看着数据是顺序添加排列进去的,所以不能保证取出数据的顺序.
这是取数据的代码:
ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
//java的反射
// Class userCla = (Class) bean.getClass();
StringBuilder myBuilder = new StringBuilder();
Class userCla = (Class) model_in.getClass();
Field[] fs = userCla.getFields();
for (int i = 0; i < fs.length; i++) {
try {
Field f = fs[i];
f.setAccessible(true);
Object val = f.get(model_in);
String name = f.getName();
//将所有键的名值对放入map中
if (name.compareTo("Sys_Key") != 0) {
try {
map.put(f.getName(), val.toString());
}catch (Exception ex){
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
问题解决方法:
在拼接前使用某些方法将map排序一下
我使用的方法是:新建一个TreeMap,制定按照字母升序的规则,将所有数据重新排序(注意: 使用这样的规则,也是因为服务端那边取数据也是这样的规则,如果你遇到了这种情况,请结合自己项目服务端的情况做排序处理)
这里贴一下我排序采用的方法:
/**
* 对Map按Key升序排序
*
* @param oriMap 要排序的map
* @return
*/
public static Map<String, String> sortMapByKey(Map<String, String> oriMap) {
if (oriMap == null || oriMap.isEmpty()) {
return null;
}
Map<String, String> sortedMap = new TreeMap<String, String>(
new Comparator<String>() {
@Override
public int compare(String lhs, String rhs) {
return lhs.compareToIgnoreCase(rhs);
}
});
sortedMap.putAll(oriMap);
return sortedMap;
}
问题思考:
为什么在测试机4.4上一直都正常,同事的5.0就不行了?
HashMap虽说是无序,但是肯定是有一定顺序的,否则不会再测试机上每次都能保持一样的顺序取出,登录成功了,虽然不知道是什么,顺序规则,但是很显然,而且这个顺序和系统版本有关,不同android版本的map列表有可能不同.
最后:
说到这,其实发现问题解决的思路和刚开始的想法完全不同,原因在于,代码之前写的真是太不严谨了,对Java几种表的了解不够,基础知识不足,对服务端那边的情况不清楚,不然早严格排序就好了,还哪来这么多幺蛾子,作为一个教训,谨记之~
附上一篇关于Java Map,List ,Set 有序无序的博客http://www.cnblogs.com/grasshopper/p/4168972.html