【发布时间】:2018-02-17 23:34:00
【问题描述】:
我有一个需求,我有一个超过 300k 行的填充数据库。我已经根据这个问题成功实现了一个 CursorAdapter,其中混合了两个投票最多的答案HERE。
我已经为后台服务实现了一个 AsyncTask 来执行对数据库的查询,这非常快,不会超过 2-3 秒。我的 AsyncTask 中的 ProgressDialog 有时很难检测到。
我的问题是,当任务完成并检索光标时,当我将适配器设置为 RecyclerView 时,该过程会冻结我的 UI 几秒钟,直到设置数据。当我执行搜索(新查询,与获取所有行但行数较少的过程相同)并替换光标以更新数据时,也会发生这种情况。
以下是一些相关代码:
异步任务
@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);
}
方法
private void setAdapter(){
if(myAdapter == null){
myAdapter = new MyAdapter(getActivity(), cursor);
search_rv.setAdapter(myAdapter);
} else
myAdapter.swapCursor(cursor);
}
因为这是一个搜索,所以除了notifyDataSetChanged(),我在这里没有什么可做的,因为所有数据在每次搜索中都会发生很大变化。
这正常吗?既然 RecyclerView 只渲染可见视图,为什么在 AsyncTask 中 Cursor 已经准备好后,它会冻结并需要这么长时间才能更新?
编辑
我已经更改了我的适配器以避免使用 CursorAdapter,因为 @cricket_007 指出在适配器中包含适配器是不好的设计。
这是我的适配器:
public class SearchListAdapter extends RecyclerView.Adapter<SearchListAdapter.ViewHolder> {
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();
}
}
}
【问题讨论】:
-
Cursor cursor 是一个类变量。我只在这些方法和适配器中使用它。适配器本身与
swapCursor方法几乎相同 -
在适配器中包含适配器听起来像是糟糕的设计决策
-
进度对话框消失得非常快,但列表会在几秒钟后更新,UI 冻结
-
我已经更新了我的答案,如果你愿意看一下,我将不胜感激
标签: android android-recyclerview