@@ -174,6 +174,7 @@ public final class Player implements PlaybackListener, Listener {
174174 //////////////////////////////////////////////////////////////////////////*/
175175
176176 public static final int RENDERER_UNAVAILABLE = -1 ;
177+ private static final String PICASSO_PLAYER_THUMBNAIL_TAG = "PICASSO_PLAYER_THUMBNAIL_TAG" ;
177178
178179 /*//////////////////////////////////////////////////////////////////////////
179180 // Playback
@@ -232,6 +233,11 @@ public final class Player implements PlaybackListener, Listener {
232233 @ NonNull private final SerialDisposable progressUpdateDisposable = new SerialDisposable ();
233234 @ NonNull private final CompositeDisposable databaseUpdateDisposable = new CompositeDisposable ();
234235
236+ // This is the only listener we need for thumbnail loading, since there is always at most only
237+ // one thumbnail being loaded at a time. This field is also here to maintain a strong reference,
238+ // which would otherwise be garbage collected since Picasso holds weak references to targets.
239+ @ NonNull private final Target currentThumbnailTarget ;
240+
235241 /*//////////////////////////////////////////////////////////////////////////
236242 // Utils
237243 //////////////////////////////////////////////////////////////////////////*/
@@ -263,6 +269,8 @@ public Player(@NonNull final PlayerService service) {
263269 videoResolver = new VideoPlaybackResolver (context , dataSource , getQualityResolver ());
264270 audioResolver = new AudioPlaybackResolver (context , dataSource );
265271
272+ currentThumbnailTarget = getCurrentThumbnailTarget ();
273+
266274 // The UIs added here should always be present. They will be initialized when the player
267275 // reaches the initialization step. Make sure the media session ui is before the
268276 // notification ui in the UIs list, since the notification depends on the media session in
@@ -573,7 +581,7 @@ public void destroy() {
573581
574582 databaseUpdateDisposable .clear ();
575583 progressUpdateDisposable .set (null );
576- PicassoHelper . cancelTag ( PicassoHelper . PLAYER_THUMBNAIL_TAG ); // cancel thumbnail loading
584+ cancelLoadingCurrentThumbnail ();
577585
578586 UIs .destroyAll (Object .class ); // destroy every UI: obviously every UI extends Object
579587 }
@@ -747,48 +755,63 @@ private void unregisterBroadcastReceiver() {
747755 //////////////////////////////////////////////////////////////////////////*/
748756 //region Thumbnail loading
749757
750- private void loadCurrentThumbnail (final String url ) {
751- if (DEBUG ) {
752- Log .d (TAG , "Thumbnail - loadCurrentThumbnail() called with url = ["
753- + (url == null ? "null" : url ) + "]" );
754- }
755-
756- // Unset currentThumbnail, since it is now outdated. This ensures it is not used in media
757- // session metadata while the new thumbnail is being loaded by Picasso.
758- currentThumbnail = null ;
759- if (isNullOrEmpty (url )) {
760- return ;
761- }
762-
763- // scale down the notification thumbnail for performance
764- PicassoHelper .loadScaledDownThumbnail (context , url ).into (new Target () {
758+ private Target getCurrentThumbnailTarget () {
759+ // a Picasso target is just a listener for thumbnail loading events
760+ return new Target () {
765761 @ Override
766762 public void onBitmapLoaded (final Bitmap bitmap , final Picasso .LoadedFrom from ) {
767763 if (DEBUG ) {
768- Log .d (TAG , "Thumbnail - onBitmapLoaded() called with: url = [" + url
769- + "], " + "bitmap = [" + bitmap + " -> " + bitmap .getWidth () + "x "
770- + bitmap . getHeight () + "], from = [" + from + "]" );
764+ Log .d (TAG , "Thumbnail - onBitmapLoaded() called with: bitmap = [" + bitmap
765+ + " -> " + bitmap . getWidth () + "x " + bitmap .getHeight () + "], from = [ "
766+ + from + "]" );
771767 }
772-
773768 currentThumbnail = bitmap ;
774- // there is a new thumbnail, so changed the end screen thumbnail, too.
769+ // there is a new thumbnail, so e.g. the end screen thumbnail needs to change , too.
775770 UIs .call (playerUi -> playerUi .onThumbnailLoaded (bitmap ));
776771 }
777772
778773 @ Override
779774 public void onBitmapFailed (final Exception e , final Drawable errorDrawable ) {
780- Log .e (TAG , "Thumbnail - onBitmapFailed() called: url = [" + url + "] " , e );
775+ Log .e (TAG , "Thumbnail - onBitmapFailed() called" , e );
781776 currentThumbnail = null ;
777+ // there is a new thumbnail, so e.g. the end screen thumbnail needs to change, too.
782778 UIs .call (playerUi -> playerUi .onThumbnailLoaded (null ));
783779 }
784780
785781 @ Override
786782 public void onPrepareLoad (final Drawable placeHolderDrawable ) {
787783 if (DEBUG ) {
788- Log .d (TAG , "Thumbnail - onPrepareLoad() called: url = [" + url + "] " );
784+ Log .d (TAG , "Thumbnail - onPrepareLoad() called" );
789785 }
790786 }
791- });
787+ };
788+ }
789+
790+ private void loadCurrentThumbnail (final String url ) {
791+ if (DEBUG ) {
792+ Log .d (TAG , "Thumbnail - loadCurrentThumbnail() called with url = ["
793+ + (url == null ? "null" : url ) + "]" );
794+ }
795+
796+ // first cancel any previous loading
797+ cancelLoadingCurrentThumbnail ();
798+
799+ // Unset currentThumbnail, since it is now outdated. This ensures it is not used in media
800+ // session metadata while the new thumbnail is being loaded by Picasso.
801+ currentThumbnail = null ;
802+ if (isNullOrEmpty (url )) {
803+ return ;
804+ }
805+
806+ // scale down the notification thumbnail for performance
807+ PicassoHelper .loadScaledDownThumbnail (context , url )
808+ .tag (PICASSO_PLAYER_THUMBNAIL_TAG )
809+ .into (currentThumbnailTarget );
810+ }
811+
812+ private void cancelLoadingCurrentThumbnail () {
813+ // cancel the Picasso job associated with the player thumbnail, if any
814+ PicassoHelper .cancelTag (PICASSO_PLAYER_THUMBNAIL_TAG );
792815 }
793816 //endregion
794817
0 commit comments