带有Cursor的DiffUtil Callback关闭到早期

时间:2017-01-09 05:30:26

标签: android android-recyclerview cursor diff

我尝试新的DiffUtil来获取RecyclerView.Adapter中的差异。但是重新加载的旧光标在可以计算差异之前关闭,我不知道为什么。此CursorCallbackCallback基础,此Adapter是我的基础,这是我的活动代码:

public class RecyclerActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor>{

    private RecyclerView recyclerView;
    private ItemAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recycler);

        recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
        recyclerView.setHasFixedSize( true );
        recyclerView.setLayoutManager(new LinearLayoutManager(this) );
        recyclerView.setAdapter(adapter = new ItemAdapter(this));
        recyclerView.setItemAnimator(new ItemAnimator());

        ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {

            @Override
            public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
                return false;
            }

            @Override
            public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
                long id = recyclerView.getAdapter().getItemId( viewHolder.getAdapterPosition() );
                viewHolder.itemView.getContext().getContentResolver().delete(ContentUris.withAppendedId(CategoryContract.CONTENT_URI, id), null, null);
            }
        };
        ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
        itemTouchHelper.attachToRecyclerView(recyclerView);

        getSupportLoaderManager().initLoader(0, null, this);
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        return new CursorLoader(this, CategoryContract.CONTENT_URI, CategoryContract.PROJECTION, null, null, CategoryContract.COLUMN_ID + " DESC");
    }

    public void addItem( View button ) {
        int count = recyclerView != null ? recyclerView.getChildCount() : 0;
        ContentValues v = new ContentValues(1);
        v.put(CategoryContract.COLUMN_NAME, "Foo Nr. " + count);
        getContentResolver().insert(CategoryContract.CONTENT_URI, v);
    }

    private Task setter;

    @Override
    public void onLoadFinished( final Loader<Cursor> loader, final Cursor data) {
        if( setter != null) {
            setter.cancel(true);
        }

        setter = new Task( adapter );
        AsyncTaskCompat.executeParallel(setter, adapter.getCursor(), data );
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        adapter.changeCursor(null);
    }

    public static class Task extends AsyncTask<Cursor, Void, Pair<Cursor, DiffUtil.DiffResult>> {
        private final CursorRecyclerViewAdapter adapter;

        Task(CursorRecyclerViewAdapter adapter) {
            this.adapter = adapter;
        }

        @Override
        protected Pair<Cursor, DiffUtil.DiffResult> doInBackground(Cursor... params) {
            return Pair.create( params[1], DiffUtil.calculateDiff( new ItemCallback( params[0], params[1]) ) );
        }

        @Override
        protected void onPostExecute(Pair<Cursor, DiffUtil.DiffResult> diffResult) {
            if( isCancelled() )
                return;
            adapter.swapCursor(diffResult.first);
            diffResult.second.dispatchUpdatesTo(adapter);
        }
    }

    public static class ItemAdapter extends CursorRecyclerViewAdapter<ItemHolder>
    {
        ItemAdapter( Context context ) {
            super(context, null);
        }

        @Override
        public void onBindViewHolder(ItemHolder viewHolder, Cursor cursor) {
            CategoryModel model = CategoryModel.FACTORY.createFromCursor( cursor );
            viewHolder.textView.setText( model.getId() + " - " + model.getName() );
        }

        @Override
        public ItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            return new ItemHolder(LayoutInflater.from( parent.getContext() ).inflate( R.layout.item, parent, false ));
        }
    }

    public static class ItemHolder extends RecyclerView.ViewHolder {
        TextView textView;

        ItemHolder(View itemView) {
            super(itemView);
            textView = ( TextView ) itemView.findViewById(R.id.textView);
        }
    }

    public static class ItemCallback extends CursorCallback<Cursor> {
        public ItemCallback(Cursor newCursor, Cursor oldCursor) {
            super(newCursor, oldCursor);
        }

        @Override
        public boolean areRowContentsTheSame(Cursor oldCursor, Cursor newCursor) {
            CategoryModel oldCategory = CategoryModel.FACTORY.createFromCursor(oldCursor);
            CategoryModel newCategory = CategoryModel.FACTORY.createFromCursor(newCursor);
            return oldCategory.getName().equals( newCategory.getName() );
        }

        @Override
        public boolean areCursorRowsTheSame(Cursor oldCursor, Cursor newCursor) {
            return oldCursor.getLong(0) == newCursor.getLong(0);
        }
    }
}

欢迎任何帮助。当返回具有相同查询的新游标时,可能会关闭旧游标。我在getCursor()中呼叫onLoadFinished()但在首次使用时在CursorCallback内关闭时,光标处于打开状态。

1 个答案:

答案 0 :(得分:2)

您遇到了CursorLoader的预期行为 - 无论您当前是否使用它,它们会在另一个光标到达后关闭旧光标。

您案例中的事件序列如下:

  1. 你得到(仍然打开)Cursor并在AsyncTask线程池的某个后台线程X中启动差异计算
  2. 某处某处调用ContentResolver.notifyChanged
  3. CursorLoader正在另一个后台线程Y中加载一个新的Cursor。新的Cursor被发布到onLoadFinished,并且可能已经交换到列表适配器。
  4. CursorLoader关闭旧的Cursor。
  5. 您的后台线程X不知道点2,3并继续使用旧的Cursor,直到它发现它被CursorLoader关闭。抛出异常。
  6. 为了继续使用后台线程中的Cursor,你必须手动管理你的Cursor(没有CursorLoader的帮助):如果发生配置更改或onDestroy,你自己关闭它。

    或者,只是截取异常并将其视为您的背景差异计算被取消的标志(无论如何不久将对另一个Cursor执行)。