[Crash分析] java.util.IllegalStateException

【案例分析】

  最近开发项目中遇到了java.util.IllegalStateException这种类型的crash,由于实现的场景比较复杂,这里讲出现问题的场景进行了简化模拟:自定义一个ListAdapter,每次点击ListView中的一个Item时,整个ListView中的数据膨胀一倍(即原来ListView有10个元素,item0~item9,点击其中任何一个item后,整个ListView会展示20个元素)。
  错误Log展示:
  错误Log
  

【类型定位】

  java.util.IllegalStateException这种类型的crash在app应用中也是会经常遇到,它代表了我们在使用listView或者gridView等控件时绑定的数据发生更新但是UI没有做出反馈的情形。这种crash更多的出现在我们自己定义了自己的ListViewAdapter,然后在写代码逻辑时更改了Adapter中对应的数据,但是没有及时通知到UI让其作出正确的更新操作。
  

【解决方案】

  查找ListView绑定的那个adapter,找到关联这个adapter的数据源,找到所有对这个数据源进行改动的地点后(找到所有的数据源改动可以通过快捷键alt+F7, 参考提升Android Studio开发效率的小技巧),手动调用adapter的notifyDataSetChanged方法,让UI能够及时作出更新。

【代码展示】

首先是会引起java.util.IllegalStateException的crash:

public class MainActivity extends AppCompatActivity {

    private ListView mListView = null;
    private ListAdapter adapter = null;
    private List<String> data = new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        for (int i = 0; i < 10; ++i) {
            data.add(i, "temp "+i);
        }

        mListView = (ListView) findViewById(R.id.text_list_view);
        adapter = new ListAdapter(MainActivity.this, R.layout.list_item, data);
        mListView.setAdapter(adapter);
        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                updateListData();
            }
        });
    }

    private void updateListData() {
        for (int i = 0; i < 10; ++i) {
            data.add("temp "+i);
        }
    }

    private class ListAdapter extends ArrayAdapter<String> {

        private List<String> mDatas;
        private int resourceId;

        public ListAdapter(Context context, int resourceId, List<String> data) {
            super(context, resourceId, data);
            mDatas = data;
            this.resourceId = resourceId;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {

            String strItem = getItem(position);
            View view;
            if (convertView == null) {
                view = LayoutInflater.from(getContext()).inflate(resourceId, null);
            } else {
                view = convertView;
            }

            TextView tv = (TextView) view.findViewById(R.id.item_text);
            tv.setText(strItem);
            return view;
        }

        @Override
        public String getItem(int position) {
            return super.getItem(position);
        }

        @Override
        public int getCount() {
            return mDatas != null ? mDatas.size() : 0;
        }
    }

下面在updateListData这个函数中添加了adapter的更新操作,可以解决IllegalStateException:

    private void updateListData() {
        for (int i = 0; i < 10; ++i) {
            data.add("temp "+i);
        }

        /**
         * 作者:lexli
         * 说明:
         *      动态更新完ListView关联的数据后,一定要通知到adapter notifyDataSetChanged
         *      好让UI能够做出正确的反应,否则就会出现crash:java.lang.IllegalStateException:
         *          The content of the adapter has changed but ListView did not receive a notification.
         *          Make sure the content of your adapter is not modified from a background thread,
         *          but only from the UI thread.
         *
         *  注意:1. 任何修改原始ListView关联的数据后都要及时通知adapter
         *       2. adapter的notifyDataSetChanged 需要保证在UI线程中处理
         *  博客:http://blog.csdn.net/csdn_lexli/article
         */
        if (adapter != null) {
            adapter.notifyDataSetChanged();
        }
    }

【个人总结】

  1. java.util.IllegalStateException对应着Adapter数据和UI展示数据不匹配问题
  2. 解决方案是在每次Adapter数据更改后,调用adapter的notifyDataSetChanged方法
  3. 定位项目所有的Adapter数据的修改:选中相关数据-> 快捷键alt+F7
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值