Use a sequence number for thumbnail callback.

Change-Id: I27888cd0437b4f654cb29371b7d86b9341d84ae9
This commit is contained in:
Yao Chen
2014-07-23 15:04:48 -07:00
parent 1aa5d2d706
commit 80bb169d0f
4 changed files with 85 additions and 64 deletions

View File

@@ -18,5 +18,6 @@ oneway interface IMediaBrowserService {
void addSubscription(in Uri uri, IMediaBrowserServiceCallbacks callbacks);
void removeSubscription(in Uri uri, IMediaBrowserServiceCallbacks callbacks);
void loadThumbnail(in Uri uri, int width, int height, IMediaBrowserServiceCallbacks callbacks);
void loadThumbnail(in int seqNum, in Uri uri, int width, int height,
IMediaBrowserServiceCallbacks callbacks);
}

View File

@@ -24,5 +24,5 @@ oneway interface IMediaBrowserServiceCallbacks {
void onConnect(in Uri root, in MediaSession.Token session, in Bundle extras);
void onConnectFailed();
void onLoadChildren(in Uri uri, in ParceledListSlice list);
void onLoadThumbnail(in Uri uri, int width, int height, in Bitmap bitmap);
void onLoadThumbnail(int seqNum, in Bitmap bitmap);
}

View File

@@ -33,6 +33,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -40,6 +41,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/**
@@ -65,8 +67,8 @@ public final class MediaBrowser {
private final Handler mHandler = new Handler();
private final ArrayMap<Uri,Subscription> mSubscriptions =
new ArrayMap<Uri, MediaBrowser.Subscription>();
private final ArrayMap<ThumbnailRequest, HashSet<ThumbnailCallback>> mThumbnailCallbacks =
new ArrayMap<ThumbnailRequest, HashSet<ThumbnailCallback>>();
private final SparseArray<ThumbnailRequest> mThumbnailRequests =
new SparseArray<ThumbnailRequest>();
private int mState = CONNECT_STATE_DISCONNECTED;
private MediaServiceConnection mServiceConnection;
@@ -75,6 +77,7 @@ public final class MediaBrowser {
private Uri mRootUri;
private MediaSession.Token mMediaSessionToken;
private Bundle mExtras;
private int mNextSeq;
/**
* Creates a media browser for the specified media browse service.
@@ -367,21 +370,20 @@ public final class MediaBrowser {
mHandler.post(new Runnable() {
@Override
public void run() {
HashSet<ThumbnailCallback> callbackSet;
ThumbnailRequest request = new ThumbnailRequest(uri, width, height);
callbackSet = mThumbnailCallbacks.get(request);
if (callbackSet != null) {
callbackSet.add(callback);
mThumbnailCallbacks.put(request, callbackSet);
// same request has been sent. we will wait for the callback.
return;
for (int i = 0; i < mThumbnailRequests.size(); i++) {
ThumbnailRequest existingRequest = mThumbnailRequests.valueAt(i);
if (existingRequest.isSameRequest(uri, width, height)) {
existingRequest.addCallback(callback);
return;
}
}
callbackSet = new HashSet<ThumbnailCallback>();
callbackSet.add(callback);
mThumbnailCallbacks.put(request, callbackSet);
final int seq = mNextSeq++;
ThumbnailRequest request = new ThumbnailRequest(seq, uri, width, height);
request.addCallback(callback);
mThumbnailRequests.put(seq, request);
if (mState == CONNECT_STATE_CONNECTED) {
try {
mServiceBinder.loadThumbnail(uri, width, height, mServiceCallbacks);
mServiceBinder.loadThumbnail(seq, uri, width, height, mServiceCallbacks);
} catch (RemoteException e) {
// Process is crashing. We will disconnect, and upon reconnect we will
// automatically reload the thumbnails. So nothing to do here.
@@ -449,17 +451,17 @@ public final class MediaBrowser {
}
}
for (ThumbnailRequest request : mThumbnailCallbacks.keySet()) {
for (int i = 0; i < mThumbnailRequests.size(); i++) {
ThumbnailRequest request = mThumbnailRequests.valueAt(i);
try {
mServiceBinder.loadThumbnail(request.uri, request.width, request.height,
mServiceCallbacks);
mServiceBinder.loadThumbnail(request.mSeq, request.mUri,
request.mWidth, request.mHeight, mServiceCallbacks);
} catch (RemoteException e) {
// Process is crashing. We will disconnect, and upon reconnect we will
// automatically reload. So nothing to do here.
Log.d(TAG, "loadThumbnail failed with RemoteException uri=" + request.uri);
Log.d(TAG, "loadThumbnail failed with RemoteException request=" + request);
}
}
}
});
}
@@ -527,7 +529,7 @@ public final class MediaBrowser {
}
private final void onLoadThumbnail(final IMediaBrowserServiceCallbacks callback,
final ThumbnailRequest request, final Bitmap bitmap) {
final int seqNum, final Bitmap bitmap) {
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -537,16 +539,16 @@ public final class MediaBrowser {
return;
}
Set<ThumbnailCallback> callbackSet = mThumbnailCallbacks.get(request);
if (callbackSet == null) {
Log.d(TAG, "onLoadThumbnail called for request=" + request +
" but the callback is not registered");
ThumbnailRequest request = mThumbnailRequests.get(seqNum);
if (request == null) {
Log.d(TAG, "onLoadThumbnail called for seqNum=" + seqNum + " request="
+ request + " but the request is not registered");
return;
}
for (ThumbnailCallback thumbnailCallback : callbackSet) {
thumbnailCallback.onThumbnailLoaded(request.uri, bitmap);
mThumbnailRequests.delete(seqNum);
for (ThumbnailCallback thumbnailCallback : request.getCallbacks()) {
thumbnailCallback.onThumbnailLoaded(request.mUri, bitmap);
}
mThumbnailCallbacks.remove(request);
}
});
}
@@ -651,47 +653,66 @@ public final class MediaBrowser {
}
private static class ThumbnailRequest {
Uri uri;
int width;
int height;
ThumbnailRequest(@NonNull Uri uri, int width, int height) {
final int mSeq;
final Uri mUri;
final int mWidth;
final int mHeight;
final List<ThumbnailCallback> mCallbacks;
/**
* Constructs a thumbnail request.
* @param seq The unique sequence number assigned to the request by the media browser.
* @param uri The Uri for the thumbnail.
* @param width The width for the thumbnail.
* @param height The height for the thumbnail.
*/
ThumbnailRequest(int seq, @NonNull Uri uri, int width, int height) {
if (uri == null) {
throw new IllegalArgumentException("thumbnail uri cannot be null");
}
this.uri = uri;
this.width = width;
this.height = height;
this.mSeq = seq;
this.mUri = uri;
this.mWidth = width;
this.mHeight = height;
mCallbacks = new ArrayList<ThumbnailCallback>();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ThumbnailRequest)) return false;
ThumbnailRequest that = (ThumbnailRequest) o;
if (height != that.height) return false;
if (width != that.width) return false;
if (!uri.equals(that.uri)) return false;
return true;
/**
* Adds a callback to the thumbnail request.
* If the callback already exists, it will not be added again.
*/
public void addCallback(@NonNull ThumbnailCallback callback) {
if (callback == null) {
throw new IllegalArgumentException("callback cannot be null in ThumbnailRequest");
}
if (!mCallbacks.contains(callback)) {
mCallbacks.add(callback);
}
}
@Override
public int hashCode() {
int result = uri.hashCode();
result = 31 * result + width;
result = 31 * result + height;
return result;
/**
* Checks if the thumbnail request has the same uri, width, and height as the given values.
*/
public boolean isSameRequest(@Nullable Uri uri, int width, int height) {
return Objects.equals(mUri, uri) && mWidth == width && mHeight == height;
}
@Override
public String toString() {
return "ThumbnailRequest{" +
"uri=" + uri +
", width=" + width +
", height=" + height +
'}';
final StringBuilder sb = new StringBuilder("ThumbnailRequest{");
sb.append("uri=").append(mUri);
sb.append(", width=").append(mWidth);
sb.append(", height=").append(mHeight);
sb.append(", seq=").append(mSeq);
sb.append('}');
return sb.toString();
}
/**
* Gets an unmodifiable view of the list of callbacks associated with the request.
*/
public List<ThumbnailCallback> getCallbacks() {
return Collections.unmodifiableList(mCallbacks);
}
}
@@ -822,11 +843,10 @@ public final class MediaBrowser {
}
@Override
public void onLoadThumbnail(final Uri uri, int width, int height, final Bitmap bitmap) {
public void onLoadThumbnail(final int seqNum, final Bitmap bitmap) {
MediaBrowser mediaBrowser = mMediaBrowser.get();
if (mediaBrowser != null) {
ThumbnailRequest request = new ThumbnailRequest(uri, width, height);
mediaBrowser.onLoadThumbnail(this, request, bitmap);
mediaBrowser.onLoadThumbnail(this, seqNum, bitmap);
}
}
}

View File

@@ -266,7 +266,7 @@ public abstract class MediaBrowserService extends Service {
}
@Override
public void loadThumbnail(final Uri uri, final int width, final int height,
public void loadThumbnail(final int seq, final Uri uri, final int width, final int height,
final IMediaBrowserServiceCallbacks callbacks) {
if (uri == null) {
throw new IllegalStateException("loadThumbnail sent null list for uri " + uri);
@@ -299,7 +299,7 @@ public abstract class MediaBrowserService extends Service {
}
try {
callbacks.onLoadThumbnail(uri, width, height, bitmap);
callbacks.onLoadThumbnail(seq, bitmap);
} catch (RemoteException e) {
// The other side is in the process of crashing.
Log.w(TAG, "RemoteException in calling onLoadThumbnail", e);