Android问题篇之代码问题(五)

140 阅读2分钟

代码问题

4. 'getHistoricalRawPointerCoords: Invalid array index 4 for MotionEvent { action=MOVE, id[0]=0, x[0]=860.977, y[0]=1669.37, toolType[0]=2, historySize=4, eventTime=1255512616446, downTime=1255120573000, deviceId=9, source=TOUCHSCREEN | STYLUS, displayId=0, eventId=1045901346}

  • MotionEvent使用相关问题
    • 1.传递至其他线程处理的MotionEvent
    • 2.手势检测相关传参:GestureDetector、ScaleGestureDetector
  • 修复
    • 使用副本MotionEvent.obtain(event)

3. 01-18 09:01:21.125 400-729/system_process E/InputDispatcher: channel '4axxxxc4 activity.XxxActivity (server)' ~ Channel is unrecoverably broken and will be disposed!

  • 内存溢出,需优化内存

2. switch-case枚举NullPointerException

  • switch-case使用原理为调用枚举的ordinal方法,最终比较的是int值
  • 若不能保证枚举不为null,请改为if判断

1. java.util.ConcurrentModificationException

  • 循环遍历集合的过程中删除元素出现,并发修改异常
// 示例代码
for(Test test : tests) {
	if (test.isDelete()) {
        tests.remove(test)
	}
}

原因

  • 使用增强for循环遍历(底层迭代器实现)+ArrayList.remove()删除
  • ArrayList的remove方法中调用fastRemove修改变量modCound,自增1
  • ArrayList#Itr(继承自Iterator)的remove方法修改的是自身变量expectedModCount
  • 两变量作用都是记录修改次数
  • ArrayList.remove后modCound != expectedModCount会抛出ConcurrentModificationException
  • modCount:记录ArrayList被修改的次数,继承自AbstractList
  • expectedModCount:期望被修改次数,定义在Itr中,初始值为modCount
  • 增强for遍历调用itr.next,ArrayList.remove修改modCount,但未同步修改Itr.expectedModCount,所以下次Itr.next抛异常

修复

1. 调用迭代器的remove方法,同步修改modCount与expectedModCount
for(Iterator<Test> it = tests.iterator(); it.hasNext(); ) {
	Test test = it.next();
	if (test.isDelete()) {
	    it.remove()
	}
}

2. 每次删除时i自减1
for(int i = 0; i < tests.size(); i++) {
    Test test = tests.get(i);
	if (test.isDelete()) {
        tests.remove(i);
        i--;
	}
}

3. 倒序记录删除下标,末尾开始删除

注意:频繁操作可使用LinkedList,线程安全可使用CopyOnWriteArrayList

附ArrayList#Itr源码

private class Itr implements Iterator<E> {
    protected int limit = ArrayList.this.size;

    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;

    public boolean hasNext() {
        return cursor < limit;
    }

    @SuppressWarnings("unchecked")
    public E next() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        int i = cursor;
        if (i >= limit)
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i + 1;
        return (E) elementData[lastRet = i];
    }

    public void remove() {
        if (lastRet < 0)
            throw new IllegalStateException();
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();

        try {
            ArrayList.this.remove(lastRet);
            cursor = lastRet;
            lastRet = -1;
            expectedModCount = modCount;
            limit--;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public void forEachRemaining(Consumer<? super E> consumer) {
        Objects.requireNonNull(consumer);
        final int size = ArrayList.this.size;
        int i = cursor;
        if (i >= size) {
            return;
        }
        final Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length) {
            throw new ConcurrentModificationException();
        }
        while (i != size && modCount == expectedModCount) {
            consumer.accept((E) elementData[i++]);
        }
        // update once at end of iteration to reduce heap write traffic
        cursor = i;
        lastRet = i - 1;

        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}