Fix paging issue where DataStatus was not updated on insert.
This commit is contained in:
parent
54eb579558
commit
e7a370a549
3 changed files with 125 additions and 31 deletions
|
@ -13,7 +13,8 @@ class DataStatus {
|
||||||
private static final Pools.Pool<BitSet> POOL = new Pools.SynchronizedPool<>(1);
|
private static final Pools.Pool<BitSet> POOL = new Pools.SynchronizedPool<>(1);
|
||||||
|
|
||||||
private final BitSet state;
|
private final BitSet state;
|
||||||
private final int size;
|
|
||||||
|
private int size;
|
||||||
|
|
||||||
public static DataStatus obtain(int size) {
|
public static DataStatus obtain(int size) {
|
||||||
BitSet bitset = POOL.acquire();
|
BitSet bitset = POOL.acquire();
|
||||||
|
@ -31,6 +32,10 @@ class DataStatus {
|
||||||
this.state = bitset;
|
this.state = bitset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mark(int position) {
|
||||||
|
state.set(position, true);
|
||||||
|
}
|
||||||
|
|
||||||
void markRange(int startInclusive, int endExclusive) {
|
void markRange(int startInclusive, int endExclusive) {
|
||||||
state.set(startInclusive, endExclusive, true);
|
state.set(startInclusive, endExclusive, true);
|
||||||
}
|
}
|
||||||
|
@ -53,6 +58,24 @@ class DataStatus {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean get(int position) {
|
||||||
|
return state.get(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
void insertState(int position, boolean value) {
|
||||||
|
if (position < 0 || position > size + 1) {
|
||||||
|
throw new IndexOutOfBoundsException();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = size; i > position; i--) {
|
||||||
|
state.set(i, state.get(i - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
state.set(position, value);
|
||||||
|
|
||||||
|
this.size = size + 1;
|
||||||
|
}
|
||||||
|
|
||||||
int size() {
|
int size() {
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,10 @@ class FixedSizePagingController<Key, Data> implements PagingController<Key> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final int loadStart;
|
||||||
|
final int loadEnd;
|
||||||
|
|
||||||
|
synchronized (loadState) {
|
||||||
if (loadState.size() == 0) {
|
if (loadState.size() == 0) {
|
||||||
liveData.postValue(Collections.emptyList());
|
liveData.postValue(Collections.emptyList());
|
||||||
return;
|
return;
|
||||||
|
@ -74,14 +78,14 @@ class FixedSizePagingController<Key, Data> implements PagingController<Key> {
|
||||||
int leftLoadBoundary = Math.max(0, leftPageBoundary - buffer);
|
int leftLoadBoundary = Math.max(0, leftPageBoundary - buffer);
|
||||||
int rightLoadBoundary = Math.min(loadState.size(), rightPageBoundary + buffer);
|
int rightLoadBoundary = Math.min(loadState.size(), rightPageBoundary + buffer);
|
||||||
|
|
||||||
int loadStart = loadState.getEarliestUnmarkedIndexInRange(leftLoadBoundary, rightLoadBoundary);
|
loadStart = loadState.getEarliestUnmarkedIndexInRange(leftLoadBoundary, rightLoadBoundary);
|
||||||
|
|
||||||
if (loadStart < 0) {
|
if (loadStart < 0) {
|
||||||
if (DEBUG) Log.i(TAG, buildLog(aroundIndex, "loadStart < 0"));
|
if (DEBUG) Log.i(TAG, buildLog(aroundIndex, "loadStart < 0"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int loadEnd = loadState.getLatestUnmarkedIndexInRange(Math.max(leftLoadBoundary, loadStart), rightLoadBoundary) + 1;
|
loadEnd = loadState.getLatestUnmarkedIndexInRange(Math.max(leftLoadBoundary, loadStart), rightLoadBoundary) + 1;
|
||||||
|
|
||||||
if (loadEnd <= loadStart) {
|
if (loadEnd <= loadStart) {
|
||||||
if (DEBUG) Log.i(TAG, buildLog(aroundIndex, "loadEnd <= loadStart, loadEnd: " + loadEnd + ", loadStart: " + loadStart));
|
if (DEBUG) Log.i(TAG, buildLog(aroundIndex, "loadEnd <= loadStart, loadEnd: " + loadEnd + ", loadStart: " + loadStart));
|
||||||
|
@ -93,6 +97,7 @@ class FixedSizePagingController<Key, Data> implements PagingController<Key> {
|
||||||
loadState.markRange(loadStart, loadEnd);
|
loadState.markRange(loadStart, loadEnd);
|
||||||
|
|
||||||
if (DEBUG) Log.i(TAG, buildLog(aroundIndex, "start: " + loadStart + ", end: " + loadEnd + ", totalSize: " + totalSize));
|
if (DEBUG) Log.i(TAG, buildLog(aroundIndex, "start: " + loadStart + ", end: " + loadEnd + ", totalSize: " + totalSize));
|
||||||
|
}
|
||||||
|
|
||||||
FETCH_EXECUTOR.execute(() -> {
|
FETCH_EXECUTOR.execute(() -> {
|
||||||
if (invalidated) {
|
if (invalidated) {
|
||||||
|
@ -147,6 +152,10 @@ class FixedSizePagingController<Key, Data> implements PagingController<Key> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
synchronized (loadState) {
|
||||||
|
loadState.mark(position);
|
||||||
|
}
|
||||||
|
|
||||||
Data item = dataSource.load(key);
|
Data item = dataSource.load(key);
|
||||||
|
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
|
@ -180,6 +189,10 @@ class FixedSizePagingController<Key, Data> implements PagingController<Key> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
synchronized (loadState) {
|
||||||
|
loadState.insertState(position, true);
|
||||||
|
}
|
||||||
|
|
||||||
Data item = dataSource.load(key);
|
Data item = dataSource.load(key);
|
||||||
|
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
package org.signal.paging;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
public class DataStatusTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void insertState_initiallyEmpty_InsertAtZero() {
|
||||||
|
DataStatus subject = DataStatus.obtain(0);
|
||||||
|
subject.insertState(0, true);
|
||||||
|
|
||||||
|
assertEquals(1, subject.size());
|
||||||
|
assertTrue(subject.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void insertState_someData_InsertAtZero() {
|
||||||
|
DataStatus subject = DataStatus.obtain(2);
|
||||||
|
subject.mark(1);
|
||||||
|
|
||||||
|
subject.insertState(0, true);
|
||||||
|
|
||||||
|
assertEquals(3, subject.size());
|
||||||
|
assertTrue(subject.get(0));
|
||||||
|
assertFalse(subject.get(1));
|
||||||
|
assertTrue(subject.get(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void insertState_someData_InsertAtOne() {
|
||||||
|
DataStatus subject = DataStatus.obtain(3);
|
||||||
|
subject.mark(1);
|
||||||
|
|
||||||
|
subject.insertState(1, true);
|
||||||
|
|
||||||
|
assertEquals(4, subject.size());
|
||||||
|
assertFalse(subject.get(0));
|
||||||
|
assertTrue(subject.get(1));
|
||||||
|
assertTrue(subject.get(2));
|
||||||
|
assertFalse(subject.get(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IndexOutOfBoundsException.class)
|
||||||
|
public void insertState_negativeThrows() {
|
||||||
|
DataStatus subject = DataStatus.obtain(0);
|
||||||
|
subject.insertState(-1, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IndexOutOfBoundsException.class)
|
||||||
|
public void insertState_largerThanSizePlusOneThrows() {
|
||||||
|
DataStatus subject = DataStatus.obtain(0);
|
||||||
|
subject.insertState(2, true);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue