Skip to content

Commit

Permalink
Attempt to fix frequent MiniPlayerFragment ANRs seen in production (#96)
Browse files Browse the repository at this point in the history
For background on the issue, and why this change may help, see
androidx/media#890. I had hoped that upgrading
the Media3 library version back in #92 would completely solve the
problems we had been seeing in production. While it did help in some
ways, by eliminating `ForegroundServiceDidNotStartInTimeException`s (see
androidx/media#167), it also seems to have
morphed a lot of problems into a new form, namely App Not Responding
(ANR) problems that arise in `MiniPlayerFragment`.

The basic idea here is to reduce the rate at which we create and release
`MediaController`s in `MiniPlayerFragment`, by moving the creation and
releasing to fragment lifecycle callbacks that are invoked less
frequently than `onResume()` and `onPause()`. My hypothesis is that
rapid creation and releasing of `MediaController`s results in some kind
of race condition that is very hard to reproduce locally but will occur
in production with enough use of the app.

It's not completely clear that this PR will definitely solve the
problems we're seeing in production, but it seems like a worthwhile
experiment to try. Other than potentially decreasing the rate of ANRs,
it should not have any other noticeable effects on the behavior of the
app.
  • Loading branch information
bb4242 committed Dec 19, 2023
2 parents 5097679 + 749909f commit eccad4c
Showing 1 changed file with 36 additions and 30 deletions.
66 changes: 36 additions & 30 deletions app/src/main/java/org/dharmaseed/android/MiniPlayerFragment.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import android.graphics.drawable.Drawable;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
Expand Down Expand Up @@ -39,38 +41,11 @@ public class MiniPlayerFragment extends Fragment {

private String LOG_TAG = "MiniPlayerFragment";

@Override
public void onResume() {
super.onResume();

// Create the media controller
Context ctx = getContext();
SessionToken sessionToken =
new SessionToken(ctx, new ComponentName(ctx, PlaybackService.class));
controllerFuture = new MediaController.Builder(ctx, sessionToken).buildAsync();
controllerFuture.addListener(() -> {
try {
mediaController = controllerFuture.get();
mediaController.addListener(playerListener);
playerListener.onMediaMetadataChanged(mediaController.getMediaMetadata());
playerListener.onPlaybackStateChanged(mediaController.getPlaybackState());
playerListener.onIsPlayingChanged(mediaController.isPlaying());
} catch (InterruptedException | ExecutionException | CancellationException e) {
Log.e(LOG_TAG, "Could not create media controller. " + e.toString());
}
}, ContextCompat.getMainExecutor(ctx));

}

@Override
public void onPause() {
super.onPause();
MediaController.releaseFuture(controllerFuture);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d(LOG_TAG, "onCreateView " + this);

// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_player, container, false);

Expand Down Expand Up @@ -98,6 +73,38 @@ public void onClick(View v) {
return view;
}

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Log.d(LOG_TAG, "onViewCreated " + this);

// Create the media controller
Context ctx = getContext();
SessionToken sessionToken =
new SessionToken(ctx, new ComponentName(ctx, PlaybackService.class));
controllerFuture = new MediaController.Builder(ctx, sessionToken).buildAsync();
controllerFuture.addListener(() -> {
try {
mediaController = controllerFuture.get();
mediaController.addListener(playerListener);
playerListener.onMediaMetadataChanged(mediaController.getMediaMetadata());
playerListener.onPlaybackStateChanged(mediaController.getPlaybackState());
playerListener.onIsPlayingChanged(mediaController.isPlaying());
} catch (InterruptedException | ExecutionException | CancellationException e) {
Log.e(LOG_TAG, "Could not create media controller. " + e.toString());
}
}, ContextCompat.getMainExecutor(ctx));

}

@Override
public void onDestroyView() {
super.onDestroyView();
Log.d(LOG_TAG, "onDestroyView " + this);

MediaController.releaseFuture(controllerFuture);
}

public void playButtonClicked(View view) {
if (mediaController != null && mediaController.getPlaybackState() != Player.STATE_IDLE) {
if (mediaController.isPlaying()) {
Expand All @@ -112,7 +119,6 @@ public void playButtonClicked(View view) {
new Player.Listener() {

private void setPPButton(String drawableName) {
Log.i(LOG_TAG, "setPPButton " + this);
ImageButton playButton = (ImageButton) getView().findViewById(R.id.mini_player_play_button);
playButton.setImageDrawable(ContextCompat.getDrawable(getContext(),
getResources().getIdentifier(drawableName, "drawable", "android")));
Expand Down

0 comments on commit eccad4c

Please sign in to comment.