第三部分 solution及测试
从上面的分析可以看出安卓希望的ListView+Adapter使用方式是更新数据,然后调用notifyDataSetChanged()触发重绘,整个过程在UI线程串行执行,框架逻辑会保证健壮可用。所以exception的描述也是说Make sure the content of your adapter is not modified from a background thread, but only from the UI thread。所以在UI线程更新数据显然是一种solution。但这就失去了工作线程的并行优势,容易引起UI卡顿。所以需要一种保持工作线程执行数据更新的解决此crash的方案。
这里设想一种方案:catch这个exception,并post给UI线程执行notifyDataSetChanged(),触发重绘。
这个方案的疑问在于,catch这个exception,不让应用进程挂掉,继续活下去,是否足够健壮能够恢复正确的UI绘制?下面从框架源代码分析和app demo测试两个角度验证一下这个方案。
先源码分析。前文分析可知触发重绘的关键在于requestLayout()。这里先简单看看requestLayout()。这个方法源自View,先看看View中对于这个方法的注释:
/**
* Call this when something has changed which has invalidated the
* layout of this view. This will schedule a layout pass of the view
* tree. This should not be called while the view hierarchy is currently in a layout
* pass ({@link #isInLayout()}. If layout is happening, the request may be honored at the
* end of the current layout pass (and then layout will run again) or after the current
* frame is drawn and the next layout occurs.
*
* <p>Subclasses which override this method should call the superclass method to
* handle possible request-during-layout errors correctly.</p>
*/
@CallSuper
public void requestLayout() {
......
}
注释中并没有说得很确定,且ListView距离View已经有多层的继承关系,requestLayout()可能被重写。从View到ListView的继承链为View->ViewGroup->AdapterView->AbsListView->ListView,对于requestLayout()方法检查结果如下:
ViewGroup.requestLayout() 未重写,等价于 V