Merge branch '851-refresher-memory-leak' into 'master'

Fix memory leaks caused by periodic view refreshing tasks

This branch implements @goapunk's suggested solution to #851. Credit goes to @ernir for finding the bug and the initial solution, and @goapunk for the improved solution - I just did a quick implementation so we can get this fixed as quickly as possible.

Closes #851

See merge request !473
This commit is contained in:
akwizgran
2017-01-02 14:10:32 +00:00
4 changed files with 26 additions and 20 deletions

View File

@@ -1,6 +1,8 @@
package org.briarproject.briar.android.blog; package org.briarproject.briar.android.blog;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.CallSuper; import android.support.annotation.CallSuper;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@@ -20,7 +22,7 @@ import javax.annotation.Nullable;
import static android.view.View.INVISIBLE; import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE; import static android.view.View.VISIBLE;
import static org.briarproject.briar.android.util.UiUtils.MIN_RESOLUTION; import static org.briarproject.briar.android.util.UiUtils.MIN_DATE_RESOLUTION;
@UiThread @UiThread
@MethodsNotNullByDefault @MethodsNotNullByDefault
@@ -32,8 +34,9 @@ abstract class BasePostFragment extends BaseFragment {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(BasePostFragment.class.getName()); Logger.getLogger(BasePostFragment.class.getName());
private final Handler handler = new Handler(Looper.getMainLooper());
protected MessageId postId; protected MessageId postId;
private View view;
private ProgressBar progressBar; private ProgressBar progressBar;
private BlogPostViewHolder ui; private BlogPostViewHolder ui;
private BlogPostItem post; private BlogPostItem post;
@@ -50,7 +53,7 @@ abstract class BasePostFragment extends BaseFragment {
if (p == null) throw new IllegalStateException("No post ID in args"); if (p == null) throw new IllegalStateException("No post ID in args");
postId = new MessageId(p); postId = new MessageId(p);
view = inflater.inflate(R.layout.fragment_blog_post, container, View view = inflater.inflate(R.layout.fragment_blog_post, container,
false); false);
progressBar = (ProgressBar) view.findViewById(R.id.progressBar); progressBar = (ProgressBar) view.findViewById(R.id.progressBar);
progressBar.setVisibility(VISIBLE); progressBar.setVisibility(VISIBLE);
@@ -83,21 +86,19 @@ abstract class BasePostFragment extends BaseFragment {
refresher = new Runnable() { refresher = new Runnable() {
@Override @Override
public void run() { public void run() {
if (ui == null) return;
LOG.info("Updating Content..."); LOG.info("Updating Content...");
ui.updateDate(post.getTimestamp()); ui.updateDate(post.getTimestamp());
view.postDelayed(refresher, MIN_RESOLUTION); handler.postDelayed(refresher, MIN_DATE_RESOLUTION);
} }
}; };
LOG.info("Adding Handler Callback"); LOG.info("Adding Handler Callback");
view.postDelayed(refresher, MIN_RESOLUTION); handler.postDelayed(refresher, MIN_DATE_RESOLUTION);
} }
private void stopPeriodicUpdate() { private void stopPeriodicUpdate() {
if (refresher != null && ui != null) { if (refresher != null) {
LOG.info("Removing Handler Callback"); LOG.info("Removing Handler Callback");
view.removeCallbacks(refresher); handler.removeCallbacks(refresher);
} }
} }

View File

@@ -105,6 +105,7 @@ public class FeedFragment extends BaseFragment implements
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
feedController.onStart(); feedController.onStart();
list.startPeriodicUpdate();
loadPersonalBlog(); loadPersonalBlog();
loadBlogPosts(false); loadBlogPosts(false);
} }
@@ -157,7 +158,6 @@ public class FeedFragment extends BaseFragment implements
handleDbException(exception); handleDbException(exception);
} }
}); });
list.startPeriodicUpdate();
} }
@Override @Override

View File

@@ -33,7 +33,7 @@ import static android.text.format.DateUtils.WEEK_IN_MILLIS;
public class UiUtils { public class UiUtils {
public static final long MIN_RESOLUTION = MINUTE_IN_MILLIS; public static final long MIN_DATE_RESOLUTION = MINUTE_IN_MILLIS;
public static final int TEASER_LENGTH = 320; public static final int TEASER_LENGTH = 320;
public static final float GREY_OUT = 0.5f; public static final float GREY_OUT = 0.5f;
@@ -51,15 +51,16 @@ public class UiUtils {
FORMAT_SHOW_DATE | FORMAT_ABBREV_TIME | FORMAT_ABBREV_MONTH; FORMAT_SHOW_DATE | FORMAT_ABBREV_TIME | FORMAT_ABBREV_MONTH;
long diff = System.currentTimeMillis() - time; long diff = System.currentTimeMillis() - time;
if (diff < MIN_RESOLUTION) return ctx.getString(R.string.now); if (diff < MIN_DATE_RESOLUTION) return ctx.getString(R.string.now);
if (diff >= DAY_IN_MILLIS && diff < WEEK_IN_MILLIS) { if (diff >= DAY_IN_MILLIS && diff < WEEK_IN_MILLIS) {
// also show time when older than a day, but newer than a week // also show time when older than a day, but newer than a week
return DateUtils.getRelativeDateTimeString(ctx, time, return DateUtils.getRelativeDateTimeString(ctx, time,
MIN_RESOLUTION, WEEK_IN_MILLIS, flags).toString(); MIN_DATE_RESOLUTION, WEEK_IN_MILLIS, flags).toString();
} }
// otherwise just show "...ago" or date string // otherwise just show "...ago" or date string
return DateUtils.getRelativeTimeSpanString(time, return DateUtils.getRelativeTimeSpanString(time,
System.currentTimeMillis(), MIN_RESOLUTION, flags).toString(); System.currentTimeMillis(),
MIN_DATE_RESOLUTION, flags).toString();
} }
public static SpannableStringBuilder getTeaser(Context ctx, Spanned body) { public static SpannableStringBuilder getTeaser(Context ctx, Spanned body) {

View File

@@ -2,6 +2,8 @@ package org.briarproject.briar.android.view;
import android.content.Context; import android.content.Context;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.os.Handler;
import android.os.Looper;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.Adapter; import android.support.v7.widget.RecyclerView.Adapter;
import android.util.AttributeSet; import android.util.AttributeSet;
@@ -17,14 +19,15 @@ import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static org.briarproject.briar.android.util.UiUtils.MIN_RESOLUTION; import static org.briarproject.briar.android.util.UiUtils.MIN_DATE_RESOLUTION;
public class BriarRecyclerView extends FrameLayout { public class BriarRecyclerView extends FrameLayout {
private static final long DEFAULT_REFRESH_INTERVAL = MIN_RESOLUTION;
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(BriarRecyclerView.class.getName()); Logger.getLogger(BriarRecyclerView.class.getName());
private final Handler handler = new Handler(Looper.getMainLooper());
private RecyclerView recyclerView; private RecyclerView recyclerView;
private TextView emptyView; private TextView emptyView;
private ProgressBar progressBar; private ProgressBar progressBar;
@@ -192,18 +195,19 @@ public class BriarRecyclerView extends FrameLayout {
@Override @Override
public void run() { public void run() {
LOG.info("Updating Content..."); LOG.info("Updating Content...");
recyclerView.getAdapter().notifyDataSetChanged(); Adapter adapter = recyclerView.getAdapter();
postDelayed(refresher, DEFAULT_REFRESH_INTERVAL); adapter.notifyItemRangeChanged(0, adapter.getItemCount());
handler.postDelayed(refresher, MIN_DATE_RESOLUTION);
} }
}; };
LOG.info("Adding Handler Callback"); LOG.info("Adding Handler Callback");
postDelayed(refresher, DEFAULT_REFRESH_INTERVAL); handler.postDelayed(refresher, MIN_DATE_RESOLUTION);
} }
public void stopPeriodicUpdate() { public void stopPeriodicUpdate() {
if (refresher != null) { if (refresher != null) {
LOG.info("Removing Handler Callback"); LOG.info("Removing Handler Callback");
removeCallbacks(refresher); handler.removeCallbacks(refresher);
} }
} }