2017-01-09 7 views
0

RecyclerView.Adapterの違いを得るには、新しいDiffUtilを試してみます。しかし、リロード上の古いカーソルは、diffが計算される前に閉じられているので、わかりません。これはCursorCallbackCallbackベースで、このAdapterは私のベースであり、ここで私の活動のコードです:DiffUtilコールバックでカーソルを早く閉じた

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で閉じられます。

+0

@ρяσѕρєяK:問題はまだ存在します。読みやすさのための変更だけです。 – Happo

+0

@Happo:Ok、user1643723答えはいいと思います。 –

+0

DiffUtilはリストのどの部分にも即座にランダムアクセスする必要があるのに対し、これらのカーソルの目的はデータベースから遅延データをロードすることであるため、ウィンドウカーソル(SQLiteCursorなど)でDiffUtilを使用することは非常に悪い考えです。すべてを最初に完全にメモリにロードする必要があります。 CursorがSQLiteCursorの場合、カーソルデータ全体をリストに最初に転送するか、DiffUtilを使用しないでください。 – BladeCoder

答えて

2

予想される動作がCursorLoaderです。現在使用しているかどうかにかかわらず、別のカーソルが到着した後に古いカーソルを閉じます。

あなたのケースでのイベントシーケンスは、このようなものです:あなたは(まだオープン)を取得

  1. カーソルと
  2. AsyncTaskスレッドプールのいくつかのバックグラウンドスレッドXに差分計算を開始する何かがどこかContentResolver.notifyChanged
  3. を呼び出します
  4. CursorLoaderは別のバックグラウンドスレッドYに新しいCursorをロードしています。その新しいCursorはonLoadFinishedにポストされており、おそらく既にリストアダプタにスワップされています。
  5. CursorLoaderは古いCursorを閉じます。
  6. バックグラウンドスレッドXはポイント2,3について認識せず、CursorLoaderによって閉じられていることがわかるまで古いCursorを使用し続けます。例外がスローされます。

バックグラウンドスレッドからCursorを使い続けるには、CursorLoaderの助けを借りずに手動でCursorを管理する必要があります。構成の変更やonDestroyが発生した場合は、それを閉じます。

また、例外を傍受して、バックグラウンド差分計算がキャンセルされていることを示す記号として扱います(とにかく別のCursorで間もなく実行されます)。

関連する問題