RecyclerView在设置适配器时会阻塞用户界面。
RecyclerView在设置适配器时会阻塞用户界面。
我有一个需求,我有一个有超过30万行的填充数据库。我已经成功地根据这个问题实现了一个基于CursorAdapter的适配器,并混合了两个最高得票的答案这里。\n我已经实现了一个用于后台服务的AsyncTask来执行对数据库的查询,速度非常快,不超过2-3秒。我的AsyncTask中的ProgressDialog有时很难检测到。\n我的问题是,当任务完成并且我检索到Cursor时,当我将适配器设置给RecyclerView时,该过程会冻结我的UI几秒钟,直到数据设置完成。当我执行搜索(新查询,与获取所有行的过程相同,但行数较少)并替换Cursor以更新数据时,也会发生这种情况。\n下面是一些相关的代码:\nAsyncTask\n
@Override protected Void doInBackground(Void... Void) { if(type==Constants.GET_ZIP_CODES) cursor = db.getAllZipCodes(); else cursor = db.searchZipCodes(text); return null; } @Override protected void onPostExecute(Void Void) { setAdapter(); mProgressDialog.dismiss(); super.onPostExecute(Void); }
\n方法\n
private void setAdapter(){ if(myAdapter == null){ myAdapter = new MyAdapter(getActivity(), cursor); search_rv.setAdapter(myAdapter); } else myAdapter.swapCursor(cursor); }
\n由于这是一个搜索,我在这里除了notifyDataSetChanged()
之外没有太多要做的,因为每次搜索时数据都会广泛更改。\n这正常吗?由于RecyclerView只渲染可见视图,为什么会冻结并花费很长时间来更新,因为Cursor已经准备好来自AsyncTask的数据?\n编辑\n我已经更改了我的适配器,避免使用CursorAdapter,因为@cricket_007指出在适配器中使用另一个适配器是不好的设计。\n这是我的适配器:\n
public class SearchListAdapter extends RecyclerView.Adapter{ private Context mContext; private Cursor mCursor; private boolean mDataValid; private int mRowIdColumn; private DataSetObserver mDataSetObserver; public SearchListAdapter(Context context, Cursor c) { mContext = context; mCursor=c; mDataValid = c != null; mRowIdColumn = mDataValid ? mCursor.getColumnIndex("_id") : -1; mDataSetObserver = new NotifyingDataSetObserver(); if (mCursor != null) { mCursor.registerDataSetObserver(mDataSetObserver); } } static class ViewHolder extends RecyclerView.ViewHolder { TextView itemTV; ViewHolder(View itemView) { super(itemView); itemTV = (TextView) itemView.findViewById(R.id.itemTV); } } @Override public void setHasStableIds(boolean hasStableIds) { super.setHasStableIds(true); } @Override public int getItemCount() { if (mDataValid && mCursor != null) { return mCursor.getCount(); } return 0; } @Override public void onBindViewHolder(ViewHolder holder, int position) { // Passing the binding operation to cursor loader mCursor.moveToPosition(position); String town = mCursor.getString(mCursor.getColumnIndex(Constants.COLUMN_TOWN)); String zipcode = mCursor.getString(mCursor.getColumnIndex(Constants.COLUMN_ZIPCODE)); String zipcode_etx = mCursor.getString(mCursor.getColumnIndex(Constants.COLUMN_ZIPCODE_EXTENSION)); holder.itemTV.setText(zipcode+"-"+zipcode_etx+", "+town); } @Override public SearchListAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.fragment_search_list_item,parent,false); // Passing the inflater job to the cursor-adapter return new SearchListAdapter.ViewHolder(itemView); } public void swapCursor(Cursor cursor) { Cursor old = changeCursor(cursor); if (old != null) { old.close(); } } private Cursor changeCursor(Cursor newCursor) { if (newCursor == mCursor) { return null; } final Cursor oldCursor = mCursor; if (oldCursor != null && mDataSetObserver != null) { oldCursor.unregisterDataSetObserver(mDataSetObserver); } mCursor = newCursor; if (mCursor != null) { if (mDataSetObserver != null) { mCursor.registerDataSetObserver(mDataSetObserver); } mRowIdColumn = newCursor.getColumnIndexOrThrow("_id"); mDataValid = true; notifyDataSetChanged(); } else { mRowIdColumn = -1; mDataValid = false; notifyDataSetChanged(); } return oldCursor; } private class NotifyingDataSetObserver extends DataSetObserver { @Override public void onChanged() { super.onChanged(); mDataValid = true; notifyDataSetChanged(); } @Override public void onInvalidated() { super.onInvalidated(); mDataValid = false; notifyDataSetChanged(); } } }
问题的原因是在设置适配器时,RecyclerView会阻塞用户界面。解决方法是检查是否有具有大字符集的数据,并尝试删除这些数据,以解决冻结问题。另外,建议为大文本添加类似于WhatsApp的“阅读更多”选项。
以下是完整的
在加载数据到RecyclerView时,我遇到了一个问题:当设置适配器时,RecyclerView会阻塞用户界面。尽管我不是通过直接将游标传递给适配器来解决这个问题的,但我认为我的经验可能对你有所帮助。
当我尝试将一个包含700-800个字符的文本加载到卡片中时,我遇到了与你类似的冻结问题。但是,当我将文本长度限制在600个字符以下时,冻结问题消失了。
所以,我建议你检查一下是否有具有大字符集的数据,如果有的话,尝试删除这些数据并重新测试一下。另外,你可以像WhatsApp那样为大文本添加一个“阅读更多”选项,以提供更好的用户体验。
谢谢你的建议,但是我没有任何长度超过20个字符的条目。
RecyclerView blocks UI when setting the adapter的问题出现的原因是数据获取的方式。在AsyncTask中,数据获取的过程中使用了Log.d来打印Cursor的大小,导致AsyncTask的执行时间变长,ProgressDialog的消失时间也变长。问题出现的原因是数据库查询操作是在后台进行的,而代码继续执行,只有在Cursor完全加载后才能继续执行。
解决方法是将打印结果的代码放在查询操作之后,即在Cursor加载完毕后再打印结果。这样可以确保查询操作完成后再继续执行其他代码,避免了RecyclerView阻塞UI的问题。
以下是修正后的代码示例:
protected Void doInBackground(Void... Void) { if(type==Constants.GET_ZIP_CODES) cursor = db.getAllZipCodes(); else cursor = db.searchZipCodes(text); // 查询操作完成后打印结果 return null; } protected void onPostExecute(Void result) { Log.d("DATABASE","SIZE "+cursor.getCount()); // 其他操作 }
通过将打印结果的代码放在查询操作之后,并在onPostExecute方法中执行,可以确保查询操作完成后再打印结果,避免了RecyclerView阻塞UI的问题。