“Do not place Android context classes in static fi

2019-03-03 09:05发布

问题:

There are questions with a similar title, but them all about Context that you get in constructor.

There are RecyclerView with items and some other views where play\pause button is present.

This class allows this views to play only one file at a time. If view_1 is playing and you press play at view_2 - file_2 will be played.

There is an ImageButton mPlayPauseButton in this class. It is needed to set ImageButton at view_1 to paused_state. And set ImageButton at view_2 to playing_state.

Lint Warning

Do not place Android context classes in static fields; this is a memory leak (and also breaks Instant Run) A static field will leak contexts.

public class CommentsAudioPlayer {

    private static MediaPlayer mPlayer;
    private static ImageButton mPlayPauseButton;

    private static void init(ImageButton imageButton){
        mPlayer = new MediaPlayer();
        mPlayPauseButton = imageButton;
    }

    public static void startPlaying(String dataSource, ImageButton imageButton) {
        init(imageButton);

        try {
            mPlayer.setDataSource(dataSource);
            mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                @Override
                public void onCompletion(MediaPlayer mp) {
                    stopPlaying();
                }
            });
            mPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                @Override
                public void onPrepared(MediaPlayer mp) {
                    mPlayer.start();
                }
            });
            mPlayer.prepareAsync();
            if (mPlayPauseButton != null) mPlayPauseButton.setSelected(true);
        } catch (Exception e) {
            Log.e("Player", "Error trying to start playing:\n" + e.toString());
        }
    }

    public static void stopPlaying() {
        if (mPlayPauseButton != null)
            mPlayPauseButton.setSelected(false);
        mPlayPauseButton = null;

        if (mPlayer!=null)
            mPlayer.release();
        mPlayer = null;
    }
}

回答1:

Do not put widgets in static fields.

Options include:

  1. Delete this class. Move all of this logic into the activity (or fragment), where you have direct access to the widgets.

  2. Use an event bus (LocalBroadcastManager, greenrobot's EventBus, etc.). Have your code here post messages on the bus when the state changes. Have your UI (activity or fragment) subscribe for messages on the bus and update the widgets.

  3. Have your activity/fragment hold an instance of CommentsAudioPlayer, and make the fields in CommentsAudioPlayer non-static.

Of the three, the first option would be simpler, cleaner, less memory-intensive, and faster to execute.



回答2:

You should decouple view (mPlayPauseButton) from that functionality to avoid leaks. To do this, you can implement a listener pattern. The easier way in that code could be passing a "listener" object as a parameter instead directly the view reference...