Bug 1377287 - review: Only update views if they're the same as when asyncTask began. r=mcomella
authorMichael Comella <michael.l.comella@gmail.com>
Wed, 26 Jul 2017 15:32:14 -0700
changeset 419913 e677ddb0596512ba640f64c0b35e9b5f0465c8d2
parent 419912 7a3e7c2caaf7e760ffe8222c1d5e30f8d68a0773
child 419914 efb8a580888afe0ed94af61f6a7843ecf520824d
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcomella
bugs1377287
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1377287 - review: Only update views if they're the same as when asyncTask began. r=mcomella MozReview-Commit-ID: 8XA32VFNSH7
mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/stream/HighlightItem.java
mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/topsites/TopSitesCard.java
--- a/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/stream/HighlightItem.java
+++ b/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/stream/HighlightItem.java
@@ -2,16 +2,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.activitystream.homepanel.stream;
 
 import android.content.Context;
 import android.graphics.Color;
+import android.support.annotation.UiThread;
 import android.text.TextUtils;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.TextView;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
@@ -25,16 +26,17 @@ import org.mozilla.gecko.icons.IconRespo
 import org.mozilla.gecko.icons.Icons;
 import org.mozilla.gecko.util.DrawableUtil;
 import org.mozilla.gecko.util.TouchTargetUtil;
 import org.mozilla.gecko.util.URIUtils;
 import org.mozilla.gecko.util.ViewUtil;
 import org.mozilla.gecko.widget.FaviconView;
 
 import java.lang.ref.WeakReference;
+import java.util.UUID;
 import java.util.concurrent.Future;
 
 public class HighlightItem extends StreamItem implements IconCallback {
     private static final String LOGTAG = "GeckoHighlightItem";
 
     public static final int LAYOUT_ID = R.layout.activity_stream_card_history_item;
 
     private Highlight highlight;
@@ -153,29 +155,46 @@ public class HighlightItem extends Strea
 
     @Override
     public void onIconResponse(IconResponse response) {
         pageIconView.updateImage(response);
     }
 
     /** Updates the text of the given view to the host second level domain. */
     private static class UpdatePageDomainAsyncTask extends URIUtils.GetHostSecondLevelDomainAsyncTask {
+        private static final int VIEW_TAG_ID = R.id.page; // same as the view.
+
         private final WeakReference<TextView> pageDomainViewWeakReference;
+        private final UUID viewTagAtStart;
 
         UpdatePageDomainAsyncTask(final Context contextReference, final String uriString, final TextView pageDomainView) {
             super(contextReference, uriString);
             this.pageDomainViewWeakReference = new WeakReference<>(pageDomainView);
+
+            // See isTagSameAsStartTag for details.
+            viewTagAtStart = UUID.randomUUID();
+            pageDomainView.setTag(VIEW_TAG_ID, viewTagAtStart);
         }
 
         @Override
         protected void onPostExecute(final String hostSLD) {
             super.onPostExecute(hostSLD);
             final TextView viewToUpdate = pageDomainViewWeakReference.get();
-            if (viewToUpdate == null) {
+
+            if (viewToUpdate == null || !isTagSameAsStartTag(viewToUpdate)) {
                 return;
             }
 
             // hostSLD will be the empty String if the host cannot be determined. This is fine: it's very unlikely
             // and the page title will be URL if there's an error there so we wouldn't want to write the URL again here.
             viewToUpdate.setText(hostSLD);
         }
+
+        /**
+         * Returns true if the tag on the given view matches the tag from the constructor. We do this to ensure
+         * the View we're making this request for hasn't been re-used by the time this request completes.
+         */
+        @UiThread
+        private boolean isTagSameAsStartTag(final View viewToCheck) {
+            return viewTagAtStart.equals(viewToCheck.getTag(VIEW_TAG_ID));
+        }
     }
 }
--- a/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/topsites/TopSitesCard.java
+++ b/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/topsites/TopSitesCard.java
@@ -2,16 +2,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 package org.mozilla.gecko.activitystream.homepanel.topsites;
 
 import android.content.Context;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.UiThread;
 import android.support.v4.widget.TextViewCompat;
 import android.support.v7.widget.RecyclerView;
 import android.text.TextUtils;
 import android.view.View;
 import android.widget.FrameLayout;
 import android.widget.TextView;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.Telemetry;
@@ -25,16 +26,17 @@ import org.mozilla.gecko.icons.IconRespo
 import org.mozilla.gecko.icons.Icons;
 import org.mozilla.gecko.util.DrawableUtil;
 import org.mozilla.gecko.util.TouchTargetUtil;
 import org.mozilla.gecko.util.URIUtils;
 import org.mozilla.gecko.util.ViewUtil;
 import org.mozilla.gecko.widget.FaviconView;
 
 import java.lang.ref.WeakReference;
+import java.util.UUID;
 import java.util.concurrent.Future;
 
 /* package-local */ class TopSitesCard extends RecyclerView.ViewHolder
         implements IconCallback {
     private final FaviconView faviconView;
 
     private final TextView title;
     private Future<IconResponse> ongoingIconLoad;
@@ -123,31 +125,47 @@ import java.util.concurrent.Future;
 
     @Override
     public void onIconResponse(IconResponse response) {
         faviconView.updateImage(response);
     }
 
     /** Updates the text of the given view to the page domain. */
     private static class UpdateCardTitleAsyncTask extends URIUtils.GetHostSecondLevelDomainAsyncTask {
+        private static final int VIEW_TAG_ID = R.id.title; // same as the view.
+
         private final WeakReference<TextView> titleViewWeakReference;
+        private final UUID viewTagAtStart;
 
         UpdateCardTitleAsyncTask(final Context contextReference, final String uriString, final TextView titleView) {
             super(contextReference, uriString);
             this.titleViewWeakReference = new WeakReference<>(titleView);
+
+            // See isTagSameAsStartTag for details.
+            viewTagAtStart = UUID.randomUUID();
+            titleView.setTag(VIEW_TAG_ID, viewTagAtStart);
         }
 
         @Override
         protected void onPostExecute(final String hostSLD) {
             super.onPostExecute(hostSLD);
             final TextView titleView = titleViewWeakReference.get();
-            if (titleView == null) {
+            if (titleView == null || !isTagSameAsStartTag(titleView)) {
                 return;
             }
 
             final String updateText = !TextUtils.isEmpty(hostSLD) ? hostSLD : uriString;
 
             // We use consistent padding all around the title, and the top padding is never modified,
             // so we can pass that in as the default padding:
             ViewUtil.setCenteredText(titleView, updateText, titleView.getPaddingTop());
         }
+
+        /**
+         * Returns true if the tag on the given view matches the tag from the constructor. We do this to ensure
+         * the View we're making this request for hasn't been re-used by the time this request completes.
+         */
+        @UiThread
+        private boolean isTagSameAsStartTag(final View viewToCheck) {
+            return viewTagAtStart.equals(viewToCheck.getTag(VIEW_TAG_ID));
+        }
     }
 }