Skip to content

Commit a050663

Browse files
committed
Avoid potential leaks with Runnables posted from ProgressBar
Bug 6093695 Handle pending progress updates when a view is not attached when the view becomes attached again. Batch pending progress updates together rather than posting separate runnables for each. Change-Id: I5dea671d5b9fbe1302912ca4734a63955e77ff4d
1 parent d3ce6f5 commit a050663

File tree

1 file changed

+98
-25
lines changed

1 file changed

+98
-25
lines changed

core/java/android/widget/ProgressBar.java

Lines changed: 98 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,11 @@
3737
import android.graphics.drawable.shapes.Shape;
3838
import android.os.Parcel;
3939
import android.os.Parcelable;
40-
import android.os.SystemClock;
4140
import android.util.AttributeSet;
42-
import android.view.Choreographer;
41+
import android.util.Pool;
42+
import android.util.Poolable;
43+
import android.util.PoolableManager;
44+
import android.util.Pools;
4345
import android.view.Gravity;
4446
import android.view.RemotableViewMethod;
4547
import android.view.View;
@@ -55,6 +57,8 @@
5557
import android.view.animation.Transformation;
5658
import android.widget.RemoteViews.RemoteView;
5759

60+
import java.util.ArrayList;
61+
5862

5963
/**
6064
* <p>
@@ -218,6 +222,10 @@ public class ProgressBar extends View {
218222
private boolean mShouldStartAnimationDrawable;
219223

220224
private boolean mInDrawing;
225+
private boolean mAttached;
226+
private boolean mRefreshIsPosted;
227+
228+
private final ArrayList<RefreshData> mRefreshData = new ArrayList<RefreshData>();
221229

222230
private AccessibilityEventSender mAccessibilityEventSender;
223231

@@ -558,29 +566,76 @@ public void postInvalidate() {
558566
}
559567

560568
private class RefreshProgressRunnable implements Runnable {
569+
public void run() {
570+
synchronized (ProgressBar.this) {
571+
final int count = mRefreshData.size();
572+
for (int i = 0; i < count; i++) {
573+
final RefreshData rd = mRefreshData.get(i);
574+
doRefreshProgress(rd.id, rd.progress, rd.fromUser, true);
575+
rd.recycle();
576+
}
577+
mRefreshData.clear();
578+
mRefreshIsPosted = false;
579+
}
580+
}
581+
}
561582

562-
private int mId;
563-
private int mProgress;
564-
private boolean mFromUser;
583+
private static class RefreshData implements Poolable<RefreshData> {
584+
public int id;
585+
public int progress;
586+
public boolean fromUser;
565587

566-
RefreshProgressRunnable(int id, int progress, boolean fromUser) {
567-
mId = id;
568-
mProgress = progress;
569-
mFromUser = fromUser;
570-
}
588+
private RefreshData mNext;
589+
private boolean mIsPooled;
571590

572-
public void run() {
573-
doRefreshProgress(mId, mProgress, mFromUser, true);
574-
// Put ourselves back in the cache when we are done
575-
mRefreshProgressRunnable = this;
591+
private static final int POOL_MAX = 24;
592+
private static final Pool<RefreshData> sPool = Pools.synchronizedPool(
593+
Pools.finitePool(new PoolableManager<RefreshData>() {
594+
@Override
595+
public RefreshData newInstance() {
596+
return new RefreshData();
597+
}
598+
599+
@Override
600+
public void onAcquired(RefreshData element) {
601+
}
602+
603+
@Override
604+
public void onReleased(RefreshData element) {
605+
}
606+
}, POOL_MAX));
607+
608+
public static RefreshData obtain(int id, int progress, boolean fromUser) {
609+
RefreshData rd = sPool.acquire();
610+
rd.id = id;
611+
rd.progress = progress;
612+
rd.fromUser = fromUser;
613+
return rd;
576614
}
577615

578-
public void setup(int id, int progress, boolean fromUser) {
579-
mId = id;
580-
mProgress = progress;
581-
mFromUser = fromUser;
616+
public void recycle() {
617+
sPool.release(this);
618+
}
619+
620+
@Override
621+
public void setNextPoolable(RefreshData element) {
622+
mNext = element;
623+
}
624+
625+
@Override
626+
public RefreshData getNextPoolable() {
627+
return mNext;
628+
}
629+
630+
@Override
631+
public boolean isPooled() {
632+
return mIsPooled;
633+
}
634+
635+
@Override
636+
public void setPooled(boolean isPooled) {
637+
mIsPooled = isPooled;
582638
}
583-
584639
}
585640

586641
private synchronized void doRefreshProgress(int id, int progress, boolean fromUser,
@@ -619,14 +674,16 @@ private synchronized void refreshProgress(int id, int progress, boolean fromUser
619674
if (mRefreshProgressRunnable != null) {
620675
// Use cached RefreshProgressRunnable if available
621676
r = mRefreshProgressRunnable;
622-
// Uncache it
623-
mRefreshProgressRunnable = null;
624-
r.setup(id, progress, fromUser);
625677
} else {
626678
// Make a new one
627-
r = new RefreshProgressRunnable(id, progress, fromUser);
679+
r = new RefreshProgressRunnable();
680+
}
681+
final RefreshData rd = RefreshData.obtain(id, progress, fromUser);
682+
mRefreshData.add(rd);
683+
if (mAttached && !mRefreshIsPosted) {
684+
post(r);
685+
mRefreshIsPosted = true;
628686
}
629-
post(r);
630687
}
631688
}
632689

@@ -1092,14 +1149,29 @@ protected void onAttachedToWindow() {
10921149
if (mIndeterminate) {
10931150
startAnimation();
10941151
}
1152+
if (mRefreshData != null) {
1153+
synchronized (this) {
1154+
final int count = mRefreshData.size();
1155+
for (int i = 0; i < count; i++) {
1156+
final RefreshData rd = mRefreshData.get(i);
1157+
doRefreshProgress(rd.id, rd.progress, rd.fromUser, true);
1158+
rd.recycle();
1159+
}
1160+
mRefreshData.clear();
1161+
}
1162+
}
1163+
mAttached = true;
10951164
}
10961165

10971166
@Override
10981167
protected void onDetachedFromWindow() {
10991168
if (mIndeterminate) {
11001169
stopAnimation();
11011170
}
1102-
if(mRefreshProgressRunnable != null) {
1171+
if (mRefreshProgressRunnable != null) {
1172+
removeCallbacks(mRefreshProgressRunnable);
1173+
}
1174+
if (mRefreshProgressRunnable != null && mRefreshIsPosted) {
11031175
removeCallbacks(mRefreshProgressRunnable);
11041176
}
11051177
if (mAccessibilityEventSender != null) {
@@ -1108,6 +1180,7 @@ protected void onDetachedFromWindow() {
11081180
// This should come after stopAnimation(), otherwise an invalidate message remains in the
11091181
// queue, which can prevent the entire view hierarchy from being GC'ed during a rotation
11101182
super.onDetachedFromWindow();
1183+
mAttached = false;
11111184
}
11121185

11131186
@Override

0 commit comments

Comments
 (0)