错误堆栈:

java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Object[]
at android.support.v4.util.SimpleArrayMap.allocArrays(SourceFile:183)
at android.support.v4.util.SimpleArrayMap.put(SourceFile:437)

错误原因:

由于SimpleArrayMap 里面使用了一个静态变量的缓存,mBaseCache,

    static Object[] mBaseCache;

该变量默认有两个数据,第1个元素是一个object[],用于存放上次的缓存的mBaseCache

第二个元素是int[],用于存在hash。具体赋值代码可以看下面

synchronized (ArrayMap.class) {
if (mBaseCacheSize < CACHE_SIZE) {
array[0] = mBaseCache;
array[1] = hashes;
for (int i=(size<<1)-1; i>=2; i--) {
array[i] = null;
}
mBaseCache = array;
mBaseCacheSize++;
if (DEBUG) Log.d(TAG, "Storing 1x cache " + array
+ " now have " + mBaseCacheSize + " entries");
}
}

使用该数组的地方在:

SimpleArrayMap 的allocArrays 方法里

synchronized (ArrayMap.class) {
if (mBaseCache != null) {
final Object[] array = mBaseCache;
mArray = array;
mBaseCache = (Object[])array[0];
mHashes = (int[])array[1];
array[0] = array[1] = null;
mBaseCacheSize--;
if (DEBUG) Log.d(TAG, "Retrieving 1x cache " + mHashes
+ " now have " + mBaseCacheSize + " entries");
return;
}
}

下面这段代码是有风险的,如果mBaseCache 在多线程被修改了,就会把ClassCastException 异常。

        mBaseCache = (Object[])array[0];

解决方法:

如果项目某个地方报这个错误,请把这个地方的ArrayMap替换成 HasMap. HasMap 多线程不会崩溃,虽然,他不是特别完好的支持。不需要把项目中所有的地方都替换掉,没有必要。单独线程,ArrayMap 完全没有问题。

错误复现:这个复现起来超级麻烦,我花了一周的时间,才找到复现的漏洞,分享给大家:

    /**
* 复现该问题 用了四个线程
* java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Object[]
* at android.support.v4.util.SimpleArrayMap.allocArrays(SimpleArrayMap.java:157)
* at android.support.v4.util.SimpleArrayMap.put(SimpleArrayMap.java:399)
* at com.example.fragment.MainFragment$14.run(MainFragment.java:280)
* 1.首先 线程1 执行到put 方法的 * mArray[index<<1] = key;
* mArray[(index<<1)+1] = value;
* mSize++;
* return null;
* 最上面这个位置 目的是让这个数组不再是空的
*
* 2.执行线程2 也执行到
* mArray[index<<1] = key;
* mArray[(index<<1)+1] = value;
* mSize++;
* return null;
* 最上面这个位置 目的是让这个put 的东西,放在第0个位置,因为put里面会生成index,
* 让两个线程都放到index 是0 的位置
*
* 3.把线程1执行完,这样数据里面已经放进去一个数据了
*
* 4.执行线程3 到removeAt 方法的 freeArrays 的 mBaseCache = array; 之前
* public V removeAt(int index) {
* final Object old = mArray[(index << 1) + 1];
* if (mSize <= 1) {
* // Now empty.
* if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to 0");
* freeArrays(mHashes, mArray, mSize);
*
* mBaseCache = array;----------- freeArrays
*
* 这个的目的是调用freeArray 方法,让当前的map释放当前的数组。这样就可以生成mBaseCache了
*
* 5.把线程2 执行完
* 这样就会把mBaseCache 赋值的数组,重新赋值
*
* 6.把线程3执行完
* ok,现在mBaseCache已经被污染了
*
* 7.执行线程4
*
*/
private void CMETestCastException() {
final ArrayMap testArrayMap = new ArrayMap();
final ArrayMap testArrayMap2 = new ArrayMap(); new Thread("线程1"){
@Override
public void run() {
super.run();
testArrayMap.put("2324","fffff");
} }.start(); new Thread("线程2"){
@Override
public void run() {
super.run();
testArrayMap.put("test","string");
} }.start(); new Thread("线程3"){
@Override
public void run() {
super.run();
testArrayMap.removeAt(0);
} }.start(); new Thread("线程4"){
@Override
public void run() {
super.run();
testArrayMap2.put("aaa","string");
} }.start();
}

复现这个问题的时候,关键是把mBaseCache 污染掉。这里四个线程的话,需要调试,调试步骤就是上面我注释的。

总结:

如果当前的map 会有多个线程访问,请使用HasMap. 该问题,google 并没有解决。在高版本上,直接扔CME ConcurrentModificationException.

ArrayMap java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Object[]的更多相关文章

  1. java.lang.ClassCastException: oracle.sql.TIMESTAMP cannot be cast to java.sql.Timestamp

    http://stackoverflow.com/questions/13269564/java-lang-classcastexception-oracle-sql-timestamp-cannot ...

  2. java.lang.ClassCastException: android.widget.RelativeLayout cannot be cast to android.widget.TextView

    最近在学习drawerLayout时,遇到这个bug.如下示: java.lang.ClassCastException: android.widget.RelativeLayout cannot b ...

  3. [Ljava.lang.String; cannot be cast to java.lang.String报错的原因

    完整错误信息: java.lang.ClassCastException: [Ljava.lang.String; cannot be cast to java.lang.String 报这个错的原因 ...

  4. java.lang.ClassCastException: android.app.Application cannot be cast to

    出这个异常的原因是在项目中添加了新lication类(public class Application extends lication)之后,没有在AndroidManifest.xml中添加该类的 ...

  5. 关于android使用ksoap2报Caused by: java.lang.ClassCastException: org.ksoap2.SoapFault cannot be cast to org.ksoap2.serialization.SoapObject

    Caused by: java.lang.ClassCastException: org.ksoap2.SoapFault cannot be cast to org.ksoap2.serializa ...

  6. java.lang.ClassCastException: oracle.sql.CLOB cannot be cast to oracle.sql.CLOB

    错误现象: [framework] 2016-05-26 11:34:53,590 -INFO  [http-bio-8080-exec-7] -1231863 -com.dhcc.base.db.D ...

  7. java.lang.ClassCastException: android.widget.TextView cannot be cast to android.widget.EditText

    Caused by: Java.lang.ClassCastException: Android.widget.TextView cannot be cast to android.widget.Ed ...

  8. 安卓出现错误: java.lang.ClassCastException: android.widget.TextView cannot be cast to android.widget.EditText

    Caused by: Java.lang.ClassCastException: Android.widget.TextView cannot be cast to android.widget.Ed ...

  9. java.lang.ClassCastException:android.widget.Button cannot be cast to android.widget.ImageView

    今天遇到一个错误也不知道怎么回事,上网搜了一下: 出现的问题是:java.lang.ClassCastException:android.widget.Button cannot be cast to ...

随机推荐

  1. BZOJ4001:[TJOI2015]概率论(卡特兰数,概率期望)

    Description Input 输入一个正整数N,代表有根树的结点数 Output 输出这棵树期望的叶子节点数.要求误差小于1e-9 Sample Input 1 Sample Output 1. ...

  2. redux中间件和redux-thunk实现原理

    redux-thunk这个中间件可以使我们把这样的异步请求或者说复杂的逻辑可以放到action里面去处理,redux-thunk使redux的一个中间件,为什么叫做中间件 我们说中间件,那么肯定是谁和 ...

  3. 【luogu P1262 间谍网络】 题解

    题目链接:https://www.luogu.org/problemnew/show/P1262 注意: 1.缩点时计算出入度是在缩完点的图上用color计算.不要在原来的点上计算. 2.枚举出入度时 ...

  4. 使用 get post 注意事项

    快速判断: 如下情况使用GET方法:客户端与服务端的交互像是一个提问(如查询操作.搜索操作.读操作) 如下情况使用POST方法:        1.交互是一个命令或订单(order),比提问包含更多信 ...

  5. Angularjs实例3

    <!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...

  6. java容易混淆的的内部类相关概念

    关于内部类: 作用: 1. 内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类 2. 内部类的方法可以直接访问外部类的所有数据,包括私有的数据 3. 内部类所实现的 ...

  7. centos 安装配置 rabbitmq 以及nginx转发

    安装erlang cd /tmp wget http://erlang.org/download/otp_src_18.3.tar.gz . cd /opt/otp_src_18. yum -y in ...

  8. JS JavaScript深拷贝、浅拷贝

    浅拷贝:浅拷贝只是复制了内存地址,如果原地址中的对象改变了,浅拷贝出来的对象也会相应改变. 深拷贝:开辟了一块新的内存存放地址和地址指向的对象,原地址的任何对象改变了,深拷贝出来的对象不变. 浅拷贝数 ...

  9. golang-Tag

    Tag 理解 Golang中可以对struct定义Tag 例如: type TestTag struct{ UserName string `json:"name"` Age In ...

  10. Kong Api 初体验

    请查看原文: https://www.fangzhipeng.com/nginx/kong/2016/07/11/kong-api-gateway/ Kong是一个可扩展的开源API层(也称为API网 ...