Merge "Avoid potential leaks with Runnables posted from ProgressBar"
This commit is contained in:
@@ -37,9 +37,11 @@ import android.graphics.drawable.shapes.RoundRectShape;
|
||||
import android.graphics.drawable.shapes.Shape;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.SystemClock;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Choreographer;
|
||||
import android.util.Pool;
|
||||
import android.util.Poolable;
|
||||
import android.util.PoolableManager;
|
||||
import android.util.Pools;
|
||||
import android.view.Gravity;
|
||||
import android.view.RemotableViewMethod;
|
||||
import android.view.View;
|
||||
@@ -55,6 +57,8 @@ import android.view.animation.LinearInterpolator;
|
||||
import android.view.animation.Transformation;
|
||||
import android.widget.RemoteViews.RemoteView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -218,6 +222,10 @@ public class ProgressBar extends View {
|
||||
private boolean mShouldStartAnimationDrawable;
|
||||
|
||||
private boolean mInDrawing;
|
||||
private boolean mAttached;
|
||||
private boolean mRefreshIsPosted;
|
||||
|
||||
private final ArrayList<RefreshData> mRefreshData = new ArrayList<RefreshData>();
|
||||
|
||||
private AccessibilityEventSender mAccessibilityEventSender;
|
||||
|
||||
@@ -558,29 +566,76 @@ public class ProgressBar extends View {
|
||||
}
|
||||
|
||||
private class RefreshProgressRunnable implements Runnable {
|
||||
|
||||
private int mId;
|
||||
private int mProgress;
|
||||
private boolean mFromUser;
|
||||
|
||||
RefreshProgressRunnable(int id, int progress, boolean fromUser) {
|
||||
mId = id;
|
||||
mProgress = progress;
|
||||
mFromUser = fromUser;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
doRefreshProgress(mId, mProgress, mFromUser, true);
|
||||
// Put ourselves back in the cache when we are done
|
||||
mRefreshProgressRunnable = this;
|
||||
synchronized (ProgressBar.this) {
|
||||
final int count = mRefreshData.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final RefreshData rd = mRefreshData.get(i);
|
||||
doRefreshProgress(rd.id, rd.progress, rd.fromUser, true);
|
||||
rd.recycle();
|
||||
}
|
||||
mRefreshData.clear();
|
||||
mRefreshIsPosted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class RefreshData implements Poolable<RefreshData> {
|
||||
public int id;
|
||||
public int progress;
|
||||
public boolean fromUser;
|
||||
|
||||
private RefreshData mNext;
|
||||
private boolean mIsPooled;
|
||||
|
||||
private static final int POOL_MAX = 24;
|
||||
private static final Pool<RefreshData> sPool = Pools.synchronizedPool(
|
||||
Pools.finitePool(new PoolableManager<RefreshData>() {
|
||||
@Override
|
||||
public RefreshData newInstance() {
|
||||
return new RefreshData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAcquired(RefreshData element) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReleased(RefreshData element) {
|
||||
}
|
||||
}, POOL_MAX));
|
||||
|
||||
public static RefreshData obtain(int id, int progress, boolean fromUser) {
|
||||
RefreshData rd = sPool.acquire();
|
||||
rd.id = id;
|
||||
rd.progress = progress;
|
||||
rd.fromUser = fromUser;
|
||||
return rd;
|
||||
}
|
||||
|
||||
public void setup(int id, int progress, boolean fromUser) {
|
||||
mId = id;
|
||||
mProgress = progress;
|
||||
mFromUser = fromUser;
|
||||
public void recycle() {
|
||||
sPool.release(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNextPoolable(RefreshData element) {
|
||||
mNext = element;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RefreshData getNextPoolable() {
|
||||
return mNext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPooled() {
|
||||
return mIsPooled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPooled(boolean isPooled) {
|
||||
mIsPooled = isPooled;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private synchronized void doRefreshProgress(int id, int progress, boolean fromUser,
|
||||
@@ -619,14 +674,16 @@ public class ProgressBar extends View {
|
||||
if (mRefreshProgressRunnable != null) {
|
||||
// Use cached RefreshProgressRunnable if available
|
||||
r = mRefreshProgressRunnable;
|
||||
// Uncache it
|
||||
mRefreshProgressRunnable = null;
|
||||
r.setup(id, progress, fromUser);
|
||||
} else {
|
||||
// Make a new one
|
||||
r = new RefreshProgressRunnable(id, progress, fromUser);
|
||||
r = new RefreshProgressRunnable();
|
||||
}
|
||||
final RefreshData rd = RefreshData.obtain(id, progress, fromUser);
|
||||
mRefreshData.add(rd);
|
||||
if (mAttached && !mRefreshIsPosted) {
|
||||
post(r);
|
||||
mRefreshIsPosted = true;
|
||||
}
|
||||
post(r);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1092,6 +1149,18 @@ public class ProgressBar extends View {
|
||||
if (mIndeterminate) {
|
||||
startAnimation();
|
||||
}
|
||||
if (mRefreshData != null) {
|
||||
synchronized (this) {
|
||||
final int count = mRefreshData.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final RefreshData rd = mRefreshData.get(i);
|
||||
doRefreshProgress(rd.id, rd.progress, rd.fromUser, true);
|
||||
rd.recycle();
|
||||
}
|
||||
mRefreshData.clear();
|
||||
}
|
||||
}
|
||||
mAttached = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1099,7 +1168,10 @@ public class ProgressBar extends View {
|
||||
if (mIndeterminate) {
|
||||
stopAnimation();
|
||||
}
|
||||
if(mRefreshProgressRunnable != null) {
|
||||
if (mRefreshProgressRunnable != null) {
|
||||
removeCallbacks(mRefreshProgressRunnable);
|
||||
}
|
||||
if (mRefreshProgressRunnable != null && mRefreshIsPosted) {
|
||||
removeCallbacks(mRefreshProgressRunnable);
|
||||
}
|
||||
if (mAccessibilityEventSender != null) {
|
||||
@@ -1108,6 +1180,7 @@ public class ProgressBar extends View {
|
||||
// This should come after stopAnimation(), otherwise an invalidate message remains in the
|
||||
// queue, which can prevent the entire view hierarchy from being GC'ed during a rotation
|
||||
super.onDetachedFromWindow();
|
||||
mAttached = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user