Skip to content

Commit 8f310f7

Browse files
committed
Fixed #568 - Possible memory leak when using Sticky Headers
1 parent 34bf00d commit 8f310f7

File tree

9 files changed

+144
-53
lines changed

9 files changed

+144
-53
lines changed

README.md

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[![Methods and Size](https://img.shields.io/badge/Methods%20and%20size-core:%201099%20|%20119%20KB-e91e63.svg)](http://www.methodscount.com/?lib=eu.davidea%3Aflexible-adapter%3A5.0.0%2B)
66

77
# FlexibleAdapter
8-
- [v5.0.1](https://github.com/davideas/FlexibleAdapter/releases/tag/5.0.1) built on 2018.03.11
8+
- [v5.0.2](https://github.com/davideas/FlexibleAdapter/releases/tag/5.0.2) built on 2018.03.17
99
- If you come from previous versions, update your code following the Wiki page [Migrations](https://github.com/davideas/FlexibleAdapter/wiki/Migrations).
1010
- Please read also [issues](https://github.com/davideas/FlexibleAdapter/issues) and [releases](https://github.com/davideas/FlexibleAdapter/releases).
1111

@@ -58,7 +58,7 @@ repositories {
5858
```
5959
dependencies {
6060
// Using JCenter
61-
compile 'eu.davidea:flexible-adapter:5.0.1'
61+
compile 'eu.davidea:flexible-adapter:5.0.2'
6262
compile 'eu.davidea:flexible-adapter-ui:1.0.0-b3'
6363
compile 'eu.davidea:flexible-adapter-livedata:1.0.0-b2'
6464
compile 'eu.davidea:flexible-adapter-databinding:1.0.0-b2'
@@ -70,12 +70,11 @@ dependencies {
7070
#### Stay Updated
7171
|Flexible Adapter|Live Data|Data Binding|UI|
7272
|---|---|---|---|
73-
|<div align="center">5.0.1</div>|<div align="center">1.0.0-b2</div>|<div align="center">1.0.0-b2</div>|<div align="center">1.0.0-b3</div>
73+
|<div align="center">5.0.2</div>|<div align="center">1.0.0-b2</div>|<div align="center">1.0.0-b2</div>|<div align="center">1.0.0-b3</div>
7474
|<a href='https://bintray.com/davideas/maven/flexible-adapter?source=watch' alt='Get automatic notifications about new "flexible-adapter" versions'><img src='https://www.bintray.com/docs/images/bintray_badge_color.png'></a>|<a href='https://bintray.com/davideas/maven/flexible-adapter-livedata?source=watch' alt='Get automatic notifications about new "flexible-adapter-livedata" versions'><img src='https://www.bintray.com/docs/images/bintray_badge_bw.png'></a>|<a href='https://bintray.com/davideas/maven/flexible-adapter-databinding?source=watch' alt='Get automatic notifications about new "flexible-adapter-databinding" versions'><img src='https://www.bintray.com/docs/images/bintray_badge_bw.png'></a>|<a href='https://bintray.com/davideas/maven/flexible-adapter-ui?source=watch' alt='Get automatic notifications about new "flexible-adapter-ui" versions'><img src='https://www.bintray.com/docs/images/bintray_badge_bw.png'></a>
7575

7676
# Wiki!
7777
I strongly recommend to read the **new [Wiki](https://github.com/davideas/FlexibleAdapter/wiki) pages**, where you can find a comprehensive Tutorial.<br/>
78-
Wiki pages have been completely reviewed to support all the coming features of version 5.0.0.
7978

8079
### Pull requests / Issues / Improvement requests
8180
Feel free to contribute and ask!<br/>
@@ -94,11 +93,9 @@ Some simple features have been implemented, thanks to some Blogs (see at the bot
9493
* Item interfaces and predefined ViewHolders complete the whole library giving more actions to the items and configuration options to developers.
9594

9695
# Showcase of the demo App
97-
You can [download](https://github.com/davideas/FlexibleAdapter/releases)* the latest demo App from the latest release page OR run it with the emulator.<br>
96+
You can [download](https://github.com/davideas/FlexibleAdapter/releases) the latest demo App from the latest release page OR run it with the emulator.<br>
9897
This [Wiki page](https://github.com/davideas/FlexibleAdapter/wiki/5.x-%7C-Demo-App) will give you a short briefing of the demo App.
9998

100-
\* = Publishing to Play Store is foreseen for final release.
101-
10299
![Overall](/screenshots/demo20_overall.png)
103100
![Adapter Animations](/screenshots/demo20_adapter_animations.png)
104101
![Undo](/screenshots/demo20_undo_single_selection.png)
@@ -120,9 +117,10 @@ This [Wiki page](https://github.com/davideas/FlexibleAdapter/wiki/5.x-%7C-Demo-A
120117

121118
# Change Log
122119
###### Latest release
123-
[v5.0.1](https://github.com/davideas/FlexibleAdapter/releases/tag/5.0.1) - 2018.03.11
120+
[v5.0.2](https://github.com/davideas/FlexibleAdapter/releases/tag/5.0.2) - 2018.03.17
124121

125122
###### Old releases
123+
[v5.0.1](https://github.com/davideas/FlexibleAdapter/releases/tag/5.0.1) - 2018.03.11 |
126124
[v5.0.0](https://github.com/davideas/FlexibleAdapter/releases/tag/5.0.0) - 2018.03.04<br>
127125
[v5.0.0-rc4](https://github.com/davideas/FlexibleAdapter/releases/tag/5.0.0-rc4) - 2017.12.17 |
128126
[v5.0.0-rc3](https://github.com/davideas/FlexibleAdapter/releases/tag/5.0.0-rc3) - 2017.10.20 |

flexible-adapter-app/src/main/java/eu/davidea/samples/flexibleadapter/items/HeaderItem.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,11 @@ public void onClick(View v) {
142142
// Support for StaggeredGridLayoutManager
143143
setFullSpan(true);
144144
}
145+
146+
@Override
147+
public String toString() {
148+
return super.toString() + " " + mTitle.getText();
149+
}
145150
}
146151

147152
@Override

flexible-adapter-app/src/test/java/eu/davidea/flexibleadapter/FilterTest.java

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@
77
import org.robolectric.annotation.Config;
88

99
import java.util.List;
10+
import java.util.concurrent.CountDownLatch;
11+
import java.util.concurrent.TimeUnit;
1012

1113
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
14+
import eu.davidea.samples.flexibleadapter.items.SimpleItem;
1215
import eu.davidea.samples.flexibleadapter.services.DatabaseService;
1316

1417
import static org.junit.Assert.assertEquals;
@@ -21,22 +24,48 @@
2124
@Config(constants = BuildConfig.class, sdk = 25)
2225
public class FilterTest {
2326

24-
FlexibleAdapter<AbstractFlexibleItem> mAdapter;
25-
List<AbstractFlexibleItem> mItems;
27+
private FlexibleAdapter<AbstractFlexibleItem> mAdapter;
28+
private List<AbstractFlexibleItem> mItems;
2629

2730
@Before
2831
public void setUp() throws Exception {
2932
DatabaseService.getInstance().createHeadersSectionsDatabase(30, 5);
3033
mItems = DatabaseService.getInstance().getDatabaseList();
3134
}
3235

33-
@Test
34-
public void testNoDelayFilter() throws Exception {
35-
mAdapter = new FlexibleAdapter<>(mItems);
36+
@SuppressWarnings("unchecked")
37+
private void createSignalAdapter(final CountDownLatch signal) {
38+
mAdapter = new FlexibleAdapter(mItems) {
39+
@Override
40+
protected void onPostFilter() {
41+
super.onPostFilter();
42+
signal.countDown();
43+
verifyResult();
44+
}
45+
};
3646
mAdapter.showAllHeaders();
37-
mAdapter.setSearchText("1");
47+
}
48+
49+
@Test
50+
public void testFilter() throws Throwable {
51+
CountDownLatch signal = new CountDownLatch(1);
52+
createSignalAdapter(signal);
53+
54+
mAdapter.setFilter("1"); // No delay
3855
mAdapter.filterItems(DatabaseService.getInstance().getDatabaseList());
39-
assertEquals(21, mAdapter.getItemCount());
56+
57+
signal.await(100L, TimeUnit.MILLISECONDS);
58+
}
59+
60+
private void verifyResult() {
61+
int count = 0;
62+
for (AbstractFlexibleItem dbItem : mItems) {
63+
SimpleItem simpleItem = (SimpleItem) dbItem;
64+
if (simpleItem.filter("1")) {
65+
count++;
66+
}
67+
}
68+
assertEquals(count, mAdapter.getItemCount());
4069
}
4170

4271
}

flexible-adapter-app/src/test/java/eu/davidea/flexibleadapter/RemoveItemsTest.java

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import org.robolectric.annotation.Config;
88

99
import java.util.List;
10+
import java.util.concurrent.CountDownLatch;
11+
import java.util.concurrent.TimeUnit;
1012

1113
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
1214
import eu.davidea.samples.flexibleadapter.services.DatabaseService;
@@ -21,8 +23,8 @@
2123
@Config(constants = BuildConfig.class, sdk = 25)
2224
public class RemoveItemsTest {
2325

24-
FlexibleAdapter<AbstractFlexibleItem> mAdapter;
25-
List<AbstractFlexibleItem> mItems;
26+
private FlexibleAdapter<AbstractFlexibleItem> mAdapter;
27+
private List<AbstractFlexibleItem> mItems;
2628

2729
@Before
2830
public void setUp() throws Exception {
@@ -31,7 +33,17 @@ public void setUp() throws Exception {
3133
mAdapter = new FlexibleAdapter<>(mItems);
3234
}
3335

34-
@SuppressWarnings("Range")
36+
@SuppressWarnings("unchecked")
37+
private void createSignalAdapter(final CountDownLatch signal) {
38+
mAdapter = new FlexibleAdapter(mItems) {
39+
@Override
40+
protected void onPostFilter() {
41+
super.onPostFilter();
42+
signal.countDown();
43+
}
44+
};
45+
}
46+
3547
@Test
3648
public void testRemoveItems() {
3749
// Items must be deleted from bottom in a cycle for
@@ -45,7 +57,6 @@ public void testRemoveItems() {
4557
assertEquals(7, mAdapter.getItemCount());
4658
}
4759

48-
@SuppressWarnings("Range")
4960
@Test
5061
public void testRemoveSelectedItems() {
5162
for (int i = 0; i < mAdapter.getItemCount(); i++) {
@@ -57,4 +68,23 @@ public void testRemoveSelectedItems() {
5768
assertEquals(7, mAdapter.getItemCount());
5869
}
5970

71+
@Test
72+
public void testRemoveItemsWithFilter() throws InterruptedException {
73+
CountDownLatch signal = new CountDownLatch(2);
74+
createSignalAdapter(signal);
75+
76+
final int INITIAL_COUNT = mItems.size();
77+
final int REMOVE_COUNT = 5;
78+
mAdapter.setFilter("1"); // No delay
79+
mAdapter.filterItems();
80+
81+
signal.await(100L, TimeUnit.MILLISECONDS);
82+
mAdapter.removeRange(0, REMOVE_COUNT);
83+
mAdapter.setFilter(""); // No delay
84+
mAdapter.filterItems();
85+
86+
signal.await(100L, TimeUnit.MILLISECONDS);
87+
assertEquals(INITIAL_COUNT - REMOVE_COUNT, mAdapter.getItemCount());
88+
}
89+
6090
}

flexible-adapter-app/src/test/java/eu/davidea/flexibleadapter/UpdateDataSetTest.java

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import org.robolectric.annotation.Config;
99

1010
import java.util.List;
11+
import java.util.concurrent.CountDownLatch;
12+
import java.util.concurrent.TimeUnit;
1113

1214
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
1315
import eu.davidea.flexibleadapter.items.IFlexible;
@@ -27,8 +29,8 @@
2729
@Config(constants = BuildConfig.class, sdk = 25)
2830
public class UpdateDataSetTest {
2931

30-
FlexibleAdapter<AbstractFlexibleItem> mAdapter;
31-
List<AbstractFlexibleItem> mInitialItems;
32+
private FlexibleAdapter<AbstractFlexibleItem> mAdapter;
33+
private List<AbstractFlexibleItem> mInitialItems;
3234

3335
@Before
3436
public void setUp() throws Exception {
@@ -37,36 +39,50 @@ public void setUp() throws Exception {
3739
FlexibleAdapter.enableLogs(Log.Level.VERBOSE);
3840
}
3941

42+
@SuppressWarnings("unchecked")
43+
private void createSignalAdapter(final CountDownLatch signal) {
44+
mAdapter = new FlexibleAdapter(mInitialItems) {
45+
@Override
46+
protected void onPostUpdate() {
47+
super.onPostUpdate();
48+
signal.countDown();
49+
}
50+
};
51+
mAdapter.showAllHeaders();
52+
}
53+
4054
@Test
4155
public void testUpdateDataSet_WithNotifyDataSetChanged() throws Exception {
42-
mAdapter = new FlexibleAdapter<>(mInitialItems);
43-
mAdapter.showAllHeaders();
56+
CountDownLatch signal = new CountDownLatch(1);
57+
createSignalAdapter(signal);
4458

4559
List<AbstractFlexibleItem> initialItems = mAdapter.getCurrentItems();
4660
mAdapter.updateDataSet(DatabaseService.getInstance().getDatabaseList());
4761
List<AbstractFlexibleItem> updatedItems = mAdapter.getCurrentItems();
4862

63+
signal.await(300L, TimeUnit.MILLISECONDS);
4964
assertEquals(initialItems.size(), mAdapter.getItemCount());
5065
assertThat(initialItems, Matchers.contains(updatedItems.toArray()));
5166
}
5267

5368
@Test
5469
public void testUpdateDataSet_WithFineGrainedNotifications() throws Exception {
55-
mAdapter = new FlexibleAdapter<>(mInitialItems);
56-
mAdapter.showAllHeaders();
70+
CountDownLatch signal = new CountDownLatch(1);
71+
createSignalAdapter(signal);
5772

5873
List<AbstractFlexibleItem> initialItems = mAdapter.getCurrentItems();
5974
mAdapter.updateDataSet(DatabaseService.getInstance().getDatabaseList(), true);
6075
List<AbstractFlexibleItem> updatedItems = mAdapter.getCurrentItems();
6176

77+
signal.await(300L, TimeUnit.MILLISECONDS);
6278
assertEquals(initialItems.size(), mAdapter.getItemCount());
6379
assertThat(initialItems, Matchers.contains(updatedItems.toArray()));
6480
}
6581

6682
@Test
6783
public void testUpdateDataSet_WithWithoutNotifyChange() throws Exception {
68-
mAdapter = new FlexibleAdapter<>(mInitialItems);
69-
mAdapter.showAllHeaders();
84+
CountDownLatch signal = new CountDownLatch(1);
85+
createSignalAdapter(signal);
7086

7187
// Let's change the DB
7288
changeDatabaseContent();
@@ -92,14 +108,17 @@ public void testUpdateDataSet_WithWithoutNotifyChange() throws Exception {
92108
List<AbstractFlexibleItem> updatedItems_withoutNotifyChange = mAdapter.getCurrentItems();
93109

94110
// The content of the 2 lists "with Notify" and "without Notify" must coincide
111+
signal.await(300L, TimeUnit.MILLISECONDS);
95112
assertEquals(updatedItems_withNotifyChange.size(), updatedItems_withoutNotifyChange.size());
96113
assertThat(updatedItems_withNotifyChange, Matchers.contains(updatedItems_withoutNotifyChange.toArray()));
97114
for (int i = 0; i < mAdapter.getItemCount(); i++) {
98115
IFlexible iFlexible = updatedItems_withNotifyChange.get(i);
99116
if (iFlexible instanceof SimpleItem) {
100117
SimpleItem item1 = (SimpleItem) updatedItems_withNotifyChange.get(i);
101118
SimpleItem item2 = (SimpleItem) updatedItems_withoutNotifyChange.get(i);
102-
assertThat(item1, Matchers.samePropertyValuesAs(item2));
119+
//assertThat(item1, Matchers.samePropertyValuesAs(item2)); // Problem with Matchers
120+
assertEquals(item1.getId(), item2.getId());
121+
assertEquals(item1.getTitle(), item2.getTitle());
103122
}
104123
}
105124
}

flexible-adapter/src/main/java/eu/davidea/flexibleadapter/AnimatorAdapter.java

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -318,10 +318,10 @@ protected final void animateView(final RecyclerView.ViewHolder holder, final int
318318
isForwardEnabled = false;
319319
}
320320
int lastVisiblePosition = getFlexibleLayoutManager().findLastVisibleItemPosition();
321-
log.v("isForwardEnabled=%s isReverseEnabled=%s isFastScroll=%s isNotified=%s mLastAnimatedPosition=%s mMaxChildViews=%s %s",
322-
isForwardEnabled, isReverseEnabled, isFastScroll, mAnimatorNotifierObserver.isPositionNotified(), mLastAnimatedPosition,
323-
mMaxChildViews, (!isReverseEnabled ? " Pos>LasVisPos=" + (position > lastVisiblePosition) : "")
324-
);
321+
// log.v("animateView isForwardEnabled=%s isReverseEnabled=%s isFastScroll=%s isNotified=%s mLastAnimatedPosition=%s mMaxChildViews=%s %s",
322+
// isForwardEnabled, isReverseEnabled, isFastScroll, mAnimatorNotifierObserver.isPositionNotified(), mLastAnimatedPosition,
323+
// mMaxChildViews, (!isReverseEnabled ? " Pos>LasVisPos=" + (position > lastVisiblePosition) : "")
324+
// );
325325
if ((isForwardEnabled || isReverseEnabled)
326326
&& !isFastScroll && holder instanceof FlexibleViewHolder
327327
&& !mAnimatorNotifierObserver.isPositionNotified()
@@ -350,16 +350,15 @@ protected final void animateView(final RecyclerView.ViewHolder holder, final int
350350
duration = animator.getDuration();
351351
}
352352
}
353-
log.v("duration=%s", duration);
354353
set.setDuration(duration);
355354
set.addListener(new HelperAnimatorListener(hashCode));
356355
if (mEntryStep) {
357356
// Stop stepDelay when screen is filled
358-
set.setStartDelay(calculateAnimationDelay(position));
357+
set.setStartDelay(calculateAnimationDelay(holder, position));
359358
}
360359
set.start();
361360
mAnimators.put(hashCode, set);
362-
log.v("animateView Scroll animation on position %s", position);
361+
//log.v("animateView Scroll animation on position %s", position);
363362
}
364363
mAnimatorNotifierObserver.clearNotified();
365364
// Update last animated position
@@ -370,7 +369,7 @@ protected final void animateView(final RecyclerView.ViewHolder holder, final int
370369
* @param position the position just bound
371370
* @return the delay in milliseconds after which, the animation for next ItemView should start.
372371
*/
373-
private long calculateAnimationDelay(int position) {
372+
private long calculateAnimationDelay(RecyclerView.ViewHolder holder, int position) {
374373
long delay;
375374
int firstVisiblePosition = getFlexibleLayoutManager().findFirstCompletelyVisibleItemPosition();
376375
int lastVisiblePosition = getFlexibleLayoutManager().findLastCompletelyVisibleItemPosition();
@@ -412,9 +411,9 @@ private long calculateAnimationDelay(int position) {
412411
delay = mInitialDelay + (position * mStepDelay);
413412
}
414413

415-
log.v("Delay[%s]=%s FirstVisible=%s LastVisible=%s LastAnimated=%s VisibleItems=%s ChildCount=%s MaxChildCount=%s",
416-
position, delay, firstVisiblePosition, lastVisiblePosition, numberOfAnimatedItems,
417-
visibleItems, mRecyclerView.getChildCount(), mMaxChildViews);
414+
// log.v("animateView %s delay[%s]=%s firstVisible=%s lastVisible=%s lastAnimated=%s visibleItems=%s childCount=%s maxChildCount=%s",
415+
// getClassName(holder), position, delay, firstVisiblePosition, lastVisiblePosition,
416+
// numberOfAnimatedItems, visibleItems, mRecyclerView.getChildCount(), mMaxChildViews);
418417

419418
return delay;
420419
}

flexible-adapter/src/main/java/eu/davidea/flexibleadapter/FlexibleAdapter.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1756,8 +1756,6 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
17561756
*/
17571757
@Override
17581758
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position, List payloads) {
1759-
String itemId = hasStableIds() ? " itemId=" + holder.getItemId() : "";
1760-
log.v("onViewBound Holder=%s position=%s%s", getClassName(holder), position, itemId);
17611759
if (!autoMap) {
17621760
// If everything has been set properly, this should never happen ;-)
17631761
throw new IllegalStateException("AutoMap is not active, this method cannot be called. You should implement the AutoMap properly.");
@@ -1810,7 +1808,6 @@ public void onViewRecycled(RecyclerView.ViewHolder holder) {
18101808
holder.itemView.setVisibility(View.VISIBLE);
18111809
}
18121810
int position = holder.getAdapterPosition();
1813-
//log.v("onViewRecycled Holder=%s position=%s", getClassName(holder), position);
18141811
T item = getItem(position);
18151812
if (item != null) item.unbindViewHolder(this, holder, position);
18161813
}

0 commit comments

Comments
 (0)