Skip to content

Commit

Permalink
Fixed #568 - Possible memory leak when using Sticky Headers
Browse files Browse the repository at this point in the history
  • Loading branch information
davideas committed Mar 17, 2018
1 parent 34bf00d commit 8f310f7
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 53 deletions.
14 changes: 6 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[![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)

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

Expand Down Expand Up @@ -58,7 +58,7 @@ repositories {
```
dependencies {
// Using JCenter
compile 'eu.davidea:flexible-adapter:5.0.1'
compile 'eu.davidea:flexible-adapter:5.0.2'
compile 'eu.davidea:flexible-adapter-ui:1.0.0-b3'
compile 'eu.davidea:flexible-adapter-livedata:1.0.0-b2'
compile 'eu.davidea:flexible-adapter-databinding:1.0.0-b2'
Expand All @@ -70,12 +70,11 @@ dependencies {
#### Stay Updated
|Flexible Adapter|Live Data|Data Binding|UI|
|---|---|---|---|
|<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>
|<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>
|<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>

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

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

# Showcase of the demo App
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>
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>
This [Wiki page](https://github.com/davideas/FlexibleAdapter/wiki/5.x-%7C-Demo-App) will give you a short briefing of the demo App.

\* = Publishing to Play Store is foreseen for final release.

![Overall](/screenshots/demo20_overall.png)
![Adapter Animations](/screenshots/demo20_adapter_animations.png)
![Undo](/screenshots/demo20_undo_single_selection.png)
Expand All @@ -120,9 +117,10 @@ This [Wiki page](https://github.com/davideas/FlexibleAdapter/wiki/5.x-%7C-Demo-A

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

###### Old releases
[v5.0.1](https://github.com/davideas/FlexibleAdapter/releases/tag/5.0.1) - 2018.03.11 |
[v5.0.0](https://github.com/davideas/FlexibleAdapter/releases/tag/5.0.0) - 2018.03.04<br>
[v5.0.0-rc4](https://github.com/davideas/FlexibleAdapter/releases/tag/5.0.0-rc4) - 2017.12.17 |
[v5.0.0-rc3](https://github.com/davideas/FlexibleAdapter/releases/tag/5.0.0-rc3) - 2017.10.20 |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ public void onClick(View v) {
// Support for StaggeredGridLayoutManager
setFullSpan(true);
}

@Override
public String toString() {
return super.toString() + " " + mTitle.getText();
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@
import org.robolectric.annotation.Config;

import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
import eu.davidea.samples.flexibleadapter.items.SimpleItem;
import eu.davidea.samples.flexibleadapter.services.DatabaseService;

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

FlexibleAdapter<AbstractFlexibleItem> mAdapter;
List<AbstractFlexibleItem> mItems;
private FlexibleAdapter<AbstractFlexibleItem> mAdapter;
private List<AbstractFlexibleItem> mItems;

@Before
public void setUp() throws Exception {
DatabaseService.getInstance().createHeadersSectionsDatabase(30, 5);
mItems = DatabaseService.getInstance().getDatabaseList();
}

@Test
public void testNoDelayFilter() throws Exception {
mAdapter = new FlexibleAdapter<>(mItems);
@SuppressWarnings("unchecked")
private void createSignalAdapter(final CountDownLatch signal) {
mAdapter = new FlexibleAdapter(mItems) {
@Override
protected void onPostFilter() {
super.onPostFilter();
signal.countDown();
verifyResult();
}
};
mAdapter.showAllHeaders();
mAdapter.setSearchText("1");
}

@Test
public void testFilter() throws Throwable {
CountDownLatch signal = new CountDownLatch(1);
createSignalAdapter(signal);

mAdapter.setFilter("1"); // No delay
mAdapter.filterItems(DatabaseService.getInstance().getDatabaseList());
assertEquals(21, mAdapter.getItemCount());

signal.await(100L, TimeUnit.MILLISECONDS);
}

private void verifyResult() {
int count = 0;
for (AbstractFlexibleItem dbItem : mItems) {
SimpleItem simpleItem = (SimpleItem) dbItem;
if (simpleItem.filter("1")) {
count++;
}
}
assertEquals(count, mAdapter.getItemCount());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import org.robolectric.annotation.Config;

import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

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

FlexibleAdapter<AbstractFlexibleItem> mAdapter;
List<AbstractFlexibleItem> mItems;
private FlexibleAdapter<AbstractFlexibleItem> mAdapter;
private List<AbstractFlexibleItem> mItems;

@Before
public void setUp() throws Exception {
Expand All @@ -31,7 +33,17 @@ public void setUp() throws Exception {
mAdapter = new FlexibleAdapter<>(mItems);
}

@SuppressWarnings("Range")
@SuppressWarnings("unchecked")
private void createSignalAdapter(final CountDownLatch signal) {
mAdapter = new FlexibleAdapter(mItems) {
@Override
protected void onPostFilter() {
super.onPostFilter();
signal.countDown();
}
};
}

@Test
public void testRemoveItems() {
// Items must be deleted from bottom in a cycle for
Expand All @@ -45,7 +57,6 @@ public void testRemoveItems() {
assertEquals(7, mAdapter.getItemCount());
}

@SuppressWarnings("Range")
@Test
public void testRemoveSelectedItems() {
for (int i = 0; i < mAdapter.getItemCount(); i++) {
Expand All @@ -57,4 +68,23 @@ public void testRemoveSelectedItems() {
assertEquals(7, mAdapter.getItemCount());
}

@Test
public void testRemoveItemsWithFilter() throws InterruptedException {
CountDownLatch signal = new CountDownLatch(2);
createSignalAdapter(signal);

final int INITIAL_COUNT = mItems.size();
final int REMOVE_COUNT = 5;
mAdapter.setFilter("1"); // No delay
mAdapter.filterItems();

signal.await(100L, TimeUnit.MILLISECONDS);
mAdapter.removeRange(0, REMOVE_COUNT);
mAdapter.setFilter(""); // No delay
mAdapter.filterItems();

signal.await(100L, TimeUnit.MILLISECONDS);
assertEquals(INITIAL_COUNT - REMOVE_COUNT, mAdapter.getItemCount());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import org.robolectric.annotation.Config;

import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

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

FlexibleAdapter<AbstractFlexibleItem> mAdapter;
List<AbstractFlexibleItem> mInitialItems;
private FlexibleAdapter<AbstractFlexibleItem> mAdapter;
private List<AbstractFlexibleItem> mInitialItems;

@Before
public void setUp() throws Exception {
Expand All @@ -37,36 +39,50 @@ public void setUp() throws Exception {
FlexibleAdapter.enableLogs(Log.Level.VERBOSE);
}

@SuppressWarnings("unchecked")
private void createSignalAdapter(final CountDownLatch signal) {
mAdapter = new FlexibleAdapter(mInitialItems) {
@Override
protected void onPostUpdate() {
super.onPostUpdate();
signal.countDown();
}
};
mAdapter.showAllHeaders();
}

@Test
public void testUpdateDataSet_WithNotifyDataSetChanged() throws Exception {
mAdapter = new FlexibleAdapter<>(mInitialItems);
mAdapter.showAllHeaders();
CountDownLatch signal = new CountDownLatch(1);
createSignalAdapter(signal);

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

signal.await(300L, TimeUnit.MILLISECONDS);
assertEquals(initialItems.size(), mAdapter.getItemCount());
assertThat(initialItems, Matchers.contains(updatedItems.toArray()));
}

@Test
public void testUpdateDataSet_WithFineGrainedNotifications() throws Exception {
mAdapter = new FlexibleAdapter<>(mInitialItems);
mAdapter.showAllHeaders();
CountDownLatch signal = new CountDownLatch(1);
createSignalAdapter(signal);

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

signal.await(300L, TimeUnit.MILLISECONDS);
assertEquals(initialItems.size(), mAdapter.getItemCount());
assertThat(initialItems, Matchers.contains(updatedItems.toArray()));
}

@Test
public void testUpdateDataSet_WithWithoutNotifyChange() throws Exception {
mAdapter = new FlexibleAdapter<>(mInitialItems);
mAdapter.showAllHeaders();
CountDownLatch signal = new CountDownLatch(1);
createSignalAdapter(signal);

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

// The content of the 2 lists "with Notify" and "without Notify" must coincide
signal.await(300L, TimeUnit.MILLISECONDS);
assertEquals(updatedItems_withNotifyChange.size(), updatedItems_withoutNotifyChange.size());
assertThat(updatedItems_withNotifyChange, Matchers.contains(updatedItems_withoutNotifyChange.toArray()));
for (int i = 0; i < mAdapter.getItemCount(); i++) {
IFlexible iFlexible = updatedItems_withNotifyChange.get(i);
if (iFlexible instanceof SimpleItem) {
SimpleItem item1 = (SimpleItem) updatedItems_withNotifyChange.get(i);
SimpleItem item2 = (SimpleItem) updatedItems_withoutNotifyChange.get(i);
assertThat(item1, Matchers.samePropertyValuesAs(item2));
//assertThat(item1, Matchers.samePropertyValuesAs(item2)); // Problem with Matchers
assertEquals(item1.getId(), item2.getId());
assertEquals(item1.getTitle(), item2.getTitle());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -318,10 +318,10 @@ protected final void animateView(final RecyclerView.ViewHolder holder, final int
isForwardEnabled = false;
}
int lastVisiblePosition = getFlexibleLayoutManager().findLastVisibleItemPosition();
log.v("isForwardEnabled=%s isReverseEnabled=%s isFastScroll=%s isNotified=%s mLastAnimatedPosition=%s mMaxChildViews=%s %s",
isForwardEnabled, isReverseEnabled, isFastScroll, mAnimatorNotifierObserver.isPositionNotified(), mLastAnimatedPosition,
mMaxChildViews, (!isReverseEnabled ? " Pos>LasVisPos=" + (position > lastVisiblePosition) : "")
);
// log.v("animateView isForwardEnabled=%s isReverseEnabled=%s isFastScroll=%s isNotified=%s mLastAnimatedPosition=%s mMaxChildViews=%s %s",
// isForwardEnabled, isReverseEnabled, isFastScroll, mAnimatorNotifierObserver.isPositionNotified(), mLastAnimatedPosition,
// mMaxChildViews, (!isReverseEnabled ? " Pos>LasVisPos=" + (position > lastVisiblePosition) : "")
// );
if ((isForwardEnabled || isReverseEnabled)
&& !isFastScroll && holder instanceof FlexibleViewHolder
&& !mAnimatorNotifierObserver.isPositionNotified()
Expand Down Expand Up @@ -350,16 +350,15 @@ protected final void animateView(final RecyclerView.ViewHolder holder, final int
duration = animator.getDuration();
}
}
log.v("duration=%s", duration);
set.setDuration(duration);
set.addListener(new HelperAnimatorListener(hashCode));
if (mEntryStep) {
// Stop stepDelay when screen is filled
set.setStartDelay(calculateAnimationDelay(position));
set.setStartDelay(calculateAnimationDelay(holder, position));
}
set.start();
mAnimators.put(hashCode, set);
log.v("animateView Scroll animation on position %s", position);
//log.v("animateView Scroll animation on position %s", position);
}
mAnimatorNotifierObserver.clearNotified();
// Update last animated position
Expand All @@ -370,7 +369,7 @@ protected final void animateView(final RecyclerView.ViewHolder holder, final int
* @param position the position just bound
* @return the delay in milliseconds after which, the animation for next ItemView should start.
*/
private long calculateAnimationDelay(int position) {
private long calculateAnimationDelay(RecyclerView.ViewHolder holder, int position) {
long delay;
int firstVisiblePosition = getFlexibleLayoutManager().findFirstCompletelyVisibleItemPosition();
int lastVisiblePosition = getFlexibleLayoutManager().findLastCompletelyVisibleItemPosition();
Expand Down Expand Up @@ -412,9 +411,9 @@ private long calculateAnimationDelay(int position) {
delay = mInitialDelay + (position * mStepDelay);
}

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

return delay;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1756,8 +1756,6 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
*/
@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position, List payloads) {
String itemId = hasStableIds() ? " itemId=" + holder.getItemId() : "";
log.v("onViewBound Holder=%s position=%s%s", getClassName(holder), position, itemId);
if (!autoMap) {
// If everything has been set properly, this should never happen ;-)
throw new IllegalStateException("AutoMap is not active, this method cannot be called. You should implement the AutoMap properly.");
Expand Down Expand Up @@ -1810,7 +1808,6 @@ public void onViewRecycled(RecyclerView.ViewHolder holder) {
holder.itemView.setVisibility(View.VISIBLE);
}
int position = holder.getAdapterPosition();
//log.v("onViewRecycled Holder=%s position=%s", getClassName(holder), position);
T item = getItem(position);
if (item != null) item.unbindViewHolder(this, holder, position);
}
Expand Down
Loading

0 comments on commit 8f310f7

Please sign in to comment.