Android MediaPlayer在多次播放后出现严重问题,只能通过重新启动手机来修复

Android MediaPlayer在多次播放后出现严重问题,只能通过重新启动手机来修复,android,android-mediaplayer,Android,Android Mediaplayer,我的目标是使用常用的媒体控件无限期地循环浏览用户选择的播放列表。目前,转发功能已被破坏。我正在使用带视频视图的MediaPlayer。我在MediaPlayer完成后释放它们并切换到新的播放器。我希望MediaPlayer类能够使用此设置。这可以工作一段时间,但在加载了不确定数量的MediaPlayer后,应用程序开始滞后。当我查看堆时,当这种情况开始发生时,大约有15个MediaPlayer,因此我认为某个地方存在泄漏,但是默认的LeakCanary设置显示没有泄漏。注意,这发生在15个Med

我的目标是使用常用的媒体控件无限期地循环浏览用户选择的播放列表。目前,转发功能已被破坏。我正在使用带视频视图的MediaPlayer。我在MediaPlayer完成后释放它们并切换到新的播放器。我希望MediaPlayer类能够使用此设置。这可以工作一段时间,但在加载了不确定数量的MediaPlayer后,应用程序开始滞后。当我查看堆时,当这种情况开始发生时,大约有15个MediaPlayer,因此我认为某个地方存在泄漏,但是默认的LeakCanary设置显示没有泄漏。注意,这发生在15个MediaPlayer加载之后,所以我认为这是一个间歇性的问题,一些MediaPlayer无法正确释放。奇怪的是,关闭应用程序并从开发者设置中删除它并不能解决滞后问题。我必须重新启动手机以消除延迟。RAM和CPU使用率不高

这个问题似乎与创建MediaPlayer有关,因为我从下面的代码块中得到错误代码

Log.i(TAG, "creating next MediaPlayer mediaPlayerLock");
nextMediaPlayer = MediaPlayer.create(getApplicationContext(), nextSong.getUri());
nextMediaPlayer.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
nextMediaPlayer.setOnCompletionListener(onCompletionListener);
Log.i(TAG, "next MediaPlayer created mediaPlayerLock");
此错误在每次创建期间打印两次:

E/ExtMediaPlayer-JNI: env->IsInstanceOf fails
E/MediaPlayer-JNI: JNIMediaPlayerFactory: bIsQCMediaPlayerPresent 0
错误会在稍后出现。JNI是否未能释放系统资源

以下是大部分代码:

public class FullscreenActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fullscreen);
    }

    @Override
    protected void onResume() {
        super.onResume();
        findViewById(R.id.fullscreen_content).setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_LOW_PROFILE
                        | View.SYSTEM_UI_FLAG_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                        | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
        hideActionBar();
    }

    private void hideActionBar() {
        ActionBar actionBar = getSupportActionBar();
        if (actionBar != null) {
            actionBar.hide();
        }
    }

    // Settings
    private static final int REQUEST_PERMISSION = 245083964;
    private static final String FILE_SAVE = "playlists";
    private static final String MASTER_PLAYLIST_NAME = "MASTER_PLAYLIST_NAME";

    private final HashMap<Uri, VideoUri> uriMap = new HashMap<>();
    private final HashMap<Uri, MediaPlayer> videosMap = new HashMap<>();

    private Playlist masterPlaylist;
    private boolean videoFilesLoaded = false;
    public boolean surfaceCreated = false;

    private VideoUri currentVideo;
    private VideoUri nextVideo;
    private MediaPlayer mediaPlayer;
    private MediaPlayer nextMediaPlayer;
    SurfaceView surfaceView;

    private final Object mediaPlayerLock = new Object();
    private final Object uiLock = new Object();

    private View.OnClickListener onClickListener;
    private MediaPlayer.OnCompletionListener onCompletionListener;

    private ExecutorService executorServiceMediaPlayers;
    private ExecutorService executorServiceSaveFile;
    private Runnable runnableNextMediaPlayer;
    private Runnable runnableSaveFile;
    private volatile boolean saving = false;

    private SurfaceHolder.Callback surfaceHolderCallback;

    private void mediaPlayerCompleted() {
        currentVideo = nextVideo;
        destroyMediaPlayer();
        updateMediaPlayer();
        setUpNextMediaPlayer();
    }

    private void destroyMediaPlayer() {
        synchronized (mediaPlayerLock) {
            if(mediaPlayer != null) {
                mediaPlayer.setDisplay(null);
                mediaPlayer.reset();
                mediaPlayer.release();
                mediaPlayer = null;
            }
        }
    }

    private void updateMediaPlayer() {
        synchronized (mediaPlayerLock) {
            mediaPlayer = nextMediaPlayer;
            if (surfaceCreated) {
                if(mediaPlayer != null) {
                    mediaPlayer.setDisplay(surfaceView.getHolder());
                    mediaPlayer.start();
                }
            }
        }
    }

    private void setUpNextMediaPlayer() {
        nextVideo = masterPlaylist.next();
        executorServiceMediaPlayers.submit(runnableNextMediaPlayer);
    }

    @Override
    protected void onStart() {
        super.onStart();
        onStartHelper();
    }

    public void onStartHelper() {
        executorServiceMediaPlayers = Executors.newSingleThreadExecutor();
        executorServiceSaveFile = Executors.newSingleThreadExecutor();
        setUpRunnables();
        setUpListeners();
        loadSaveFile();
        getExternalStoragePermissionAndFetchMediaFiles();
        setUpMediaPlayersOnCreate();
        setUpSurfaceView();
        setUpFAB();
    }

    @Override
    protected void onStop() {
        super.onStop();
        FloatingActionButton floatingActionButton = findViewById(R.id.fab);
        floatingActionButton.setOnClickListener(null);
        onClickListener = null;
        onCompletionListener = null;
        synchronized (mediaPlayerLock) {
            if(mediaPlayer != null) {
                mediaPlayer.setOnCompletionListener(null);
                mediaPlayer.reset();
                mediaPlayer.release();
            }
            if(nextMediaPlayer != null) {
                nextMediaPlayer.setOnCompletionListener(null);
                nextMediaPlayer.reset();
                nextMediaPlayer.release();
            }
        }
        executorServiceMediaPlayers.shutdown();
        executorServiceMediaPlayers = null;
        executorServiceSaveFile.shutdown();
        executorServiceSaveFile = null;
        runnableNextMediaPlayer = null;
        runnableSaveFile = null;
        surfaceHolderCallback = null;
        surfaceView = findViewById(R.id.surface_view);
        surfaceView.getHolder().removeCallback(surfaceHolderCallback);

    }

    private void setUpSurfaceHolderCallback() {
        surfaceHolderCallback = new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
                synchronized (mediaPlayerLock) {
                    surfaceCreated = true;
                    if(mediaPlayer != null) {
                        mediaPlayer.setDisplay(surfaceHolder);
                        mediaPlayer.start();
                    }
                }
            }

            @Override
            public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int i1, int i2) {

            }

            @Override
            public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {
                synchronized (mediaPlayerLock) {
                    surfaceCreated = false;
                }
                saveFile();
            }
        };
    }

    private void setUpListeners() {
        onClickListener = new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                synchronized (uiLock) {
                    mediaPlayerCompleted();
                }
                if(!saving){
                    executorServiceSaveFile.submit(runnableSaveFile);
                }
            }
        };
        onCompletionListener = new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mediaPlayer2) {
                synchronized (uiLock) {
                    mediaPlayerCompleted();
                }
            }
        };
    }

    private void setUpRunnables() {
        runnableNextMediaPlayer = new Runnable() {
            @Override
            public void run() {
                synchronized (uiLock) {
                    synchronized (mediaPlayerLock) {
                        nextMediaPlayer = MediaPlayer.create(getApplicationContext(), nextVideo.getUri());
                        nextMediaPlayer.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
                        nextMediaPlayer.setOnCompletionListener(onCompletionListener);
                    }
                }
            }
        };
        runnableSaveFile = new Runnable() {
            @Override
            public void run() {
                saving = true;
                saveFile();
                saving = false;
            }
        };
    }

    private void setUpFAB() {
        FloatingActionButton floatingActionButton = findViewById(R.id.fab);
        floatingActionButton.setOnClickListener(onClickListener);
    }

    private void setUpSurfaceView() {
            setUpSurfaceHolderCallback();
            surfaceView = findViewById(R.id.surface_view);
            surfaceView.getHolder().addCallback(surfaceHolderCallback);
    }

    private void setUpMediaPlayersOnCreate() {
        synchronized (mediaPlayerLock) {
            if(masterPlaylist != null) {
                currentVideo = masterPlaylist.next();
                mediaPlayer = MediaPlayer.create(getApplicationContext(), currentVideo.getUri());
                if (mediaPlayer != null) {
                    mediaPlayer.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);         mediaPlayer.setOnCompletionListener(onCompletionListener);
                }
                nextVideo = masterPlaylist.next();
                executorServiceMediaPlayers.submit(runnableNextMediaPlayer);
            }
        }
    }


    private void getVideoFiles() {
        String[] projection = new String[]{
                MediaStore.Video.Media._ID, MediaStore.Video.Media.DISPLAY_NAME,
                MediaStore.Video.Media.TITLE, MediaStore.Images.Media.DATA
        };
        String selection = MediaStore.Video.Media.DISPLAY_NAME + " != ?";
        String[] selectionArgs = new String[]{""};
        String sortOrder = MediaStore.Video.Media.DISPLAY_NAME + " ASC";
        try (Cursor cursor = getApplicationContext().getContentResolver().query(
                MediaStore.Video.Media.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, sortOrder)) {
            if (cursor != null) {
                List<Uri> newURIs = getURIs(cursor);
                if (!uriMap.isEmpty()) {
                    if (masterPlaylist != null) {
                        // Add new videos to the masterPlaylist
                        for (VideoUri videoURIFromUriMap : uriMap.values()) {
                            if (videoURIFromUriMap != null) {
                                if (!masterPlaylist.contains(videoURIFromUriMap)) {
                                    masterPlaylist.add(videoURIFromUriMap);
                                }
                            }
                        }
                        // Remove missing videos
                        ArrayList<VideoUri> a = new ArrayList<>(masterPlaylist.getProbMap().keySet());
                        for (VideoUri videoURI : a) {
                            if (!newURIs.contains(videoURI.getUri())) {
                                masterPlaylist.remove(videoURI);
                                uriMap.remove(videoURI.getUri());
                                videosMap.remove(videoURI.getUri());
                            }
                        }
                    } else {
                        masterPlaylist = new Playlist(
                                new ArrayList<>(uriMap.values()), MASTER_PLAYLIST_NAME);
                    }
                }
            }
        }
        videoFilesLoaded = true;
    }

    private List<Uri> getURIs(Cursor cursor) {
        ArrayList<Uri> newURIs = new ArrayList<>();
        int idCol = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID);
        int nameCol = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME);
        int titleCol = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.TITLE);
        int dataCol = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA);
        while (cursor.moveToNext()) {
            long id = cursor.getLong(idCol);
            String displayName = cursor.getString(nameCol);
            String title = cursor.getString(titleCol);
            String artist = "";
            String data = cursor.getString(dataCol);
            Uri uri = ContentUris.withAppendedId(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, id);
            if (!uriMap.containsKey(uri)) {
                VideoUri videoURI = new VideoUri(uri, data, displayName, artist, title, id);
                uriMap.put(uri, videoURI);
            }
            newURIs.add(uri);
        }
        return newURIs;
    }

    private void getExternalStoragePermissionAndFetchMediaFiles() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
                    != PackageManager.PERMISSION_GRANTED) {
                requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_PERMISSION);
            } else {
                if (!videoFilesLoaded) {
                    getVideoFiles();
                }
            }
        } else {
            if (!videoFilesLoaded) {
                getVideoFiles();
            }
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_PERMISSION && grantResults.length > 0 &&
                grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            getVideoFiles();
            setUpMediaPlayersOnCreate();
            setUpSurfaceView();
        } else {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_PERMISSION);
            }
        }
    }

    private void loadSaveFile() {
        String string = "Loading the save file";
        File file = new File(getBaseContext().getFilesDir(), FILE_SAVE);
        if (file.exists()) {
            try (FileInputStream fileInputStream = getApplicationContext().openFileInput(FILE_SAVE);
                 ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)) {
                masterPlaylist = (Playlist) objectInputStream.readObject();
            } catch (ClassNotFoundException | IOException e) {
                e.printStackTrace();
                string = "Error encountered while loading the save file";
            }
        }
        string = "Save file loaded";
    }

    void saveFile() {
        File file = new File(getBaseContext().getFilesDir(), FILE_SAVE);
        //noinspection ResultOfMethodCallIgnored
        file.delete();
        try (FileOutputStream fos = getApplicationContext().openFileOutput(FILE_SAVE, Context.MODE_PRIVATE);
             ObjectOutputStream objectOutputStream = new ObjectOutputStream(fos)) {
            objectOutputStream.writeObject(masterPlaylist);
            objectOutputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
公共类FullscreenActivity扩展了AppCompatActivity{
@凌驾
创建时受保护的void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_全屏);
}
@凌驾
受保护的void onResume(){
super.onResume();
findViewById(R.id.全屏内容)。设置系统兼容性(
View.SYSTEM\u UI\u FLAG\u LOW\u PROFILE
|View.SYSTEM\u UI\u FLAG\u全屏显示
|View.SYSTEM\u UI\u FLAG\u布局\u稳定
|View.SYSTEM\u UI\u FLAG\u沉浸式\u粘性
|View.SYSTEM\u UI\u FLAG\u布局\u隐藏\u导航
|视图。系统(用户界面、标志、隐藏、导航);
hideActionBar();
}
私有void hideActionBar(){
ActionBar ActionBar=getSupportActionBar();
if(actionBar!=null){
actionBar.hide();
}
}
//背景
私有静态最终int请求\ u权限=245083964;
私有静态最终字符串文件\u SAVE=“playlists”;
私有静态最终字符串MASTER\u PLAYLIST\u NAME=“MASTER\u PLAYLIST\u NAME”;
private final HashMap uriMap=new HashMap();
private final HashMap videosMap=new HashMap();
私人播放列表主播放列表;
私有布尔videoFilesLoaded=false;
公共布尔值surfaceCreated=false;
私人视频;
私有视频用户接口nextVideo;
私人媒体播放器;
私有媒体播放器nextMediaPlayer;
表面视图表面视图;
私有最终对象mediaPlayerLock=新对象();
私有最终对象uiLock=新对象();
private View.OnClickListener OnClickListener;
private MediaPlayer.OnCompletionListener OnCompletionListener;
私人执行者服务执行者服务媒体玩家;
私有ExecutorService executorServiceSaveFile;
私有runnableNextMediaPlayer;
私有可运行的可运行文件;
私有易变布尔保存=false;
私有SurfaceHolder.Callback surfaceHolderCallback;
私有void mediaPlayerCompleted(){
currentVideo=nextVideo;
媒体播放器();
更新层();
setUpNextMediaPlayer();
}
私有媒体播放器(){
已同步(mediaPlayerLock){
如果(mediaPlayer!=null){
mediaPlayer.setDisplay(空);
mediaPlayer.reset();
mediaPlayer.release();
mediaPlayer=null;
}
}
}
私有void更新层(){
已同步(mediaPlayerLock){
mediaPlayer=nextMediaPlayer;
如果(表面处理){
如果(mediaPlayer!=null){
setDisplay(surfaceView.getHolder());
mediaPlayer.start();
}
}
}
}
私有void setUpNextMediaPlayer(){
nextVideo=masterPlaylist.next();
ExecutorServiceMediaPlayer.submit(runnableNextMediaPlayer);
}
@凌驾
受保护的void onStart(){
super.onStart();
onStartHelper();
}
公共void onStartHelper(){
ExecutorServiceMediaPlayer=Executors.newSingleThreadExecutor();
executorServiceSaveFile=Executors.newSingleThreadExecutor();
setUpRunnables();
setUpListeners();
loadSaveFile();
getExternalStoragePermissionAndFetchMediaFiles();
setUpMediaPlayersOnCreate();
setUpSurfaceView();
setUpFAB();
}
@凌驾
受保护的void onStop(){
super.onStop();
FloatingActionButton FloatingActionButton=findViewById(R.id.fab);
floatingActionButton.setOnClickListener(null);
onClickListener=null;
onCompletionListener=null;
已同步(mediaPlayerLock){
如果(mediaPlayer!=null){
mediaPlayer.setOnCompletionListener(空);
mediaPlayer.reset();
mediaPlayer.release();
}
if(nextMediaPlayer!=null){
nextMediaPlayer.setOnCompletionListener(空);
nextMediaPlayer.reset();
nextMediaPlayer.release();
}
}
ExecutorServiceMediaPlayer.shutdown();
ExecutorServiceMediaPlayer=null;
executorServiceSaveFile.shutdown();
executorServiceSaveFile=null;
runnableNextMediaPlayer=null;
runnableSaveFile=null;
surfaceHolderCallback=null;
表面视图=findViewById(R.id.表面视图);
public class FullscreenActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fullscreen);
    }

    @Override
    protected void onResume() {
        super.onResume();
        findViewById(R.id.fullscreen_content).setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_LOW_PROFILE
                        | View.SYSTEM_UI_FLAG_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                        | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
        hideActionBar();
    }

    private void hideActionBar() {
        ActionBar actionBar = getSupportActionBar();
        if (actionBar != null) {
            actionBar.hide();
        }
    }

    // Settings
    private static final int REQUEST_PERMISSION = 245083964;
    private static final String FILE_SAVE = "playlists";
    private static final String MASTER_PLAYLIST_NAME = "MASTER_PLAYLIST_NAME";

    private final HashMap<Uri, VideoUri> uriMap = new HashMap<>();
    private final HashMap<Uri, MediaPlayer> videosMap = new HashMap<>();

    private Playlist masterPlaylist;
    private boolean videoFilesLoaded = false;
    public boolean surfaceCreated = false;

    private VideoUri currentVideo;
    private VideoUri nextVideo;
    private MediaPlayer mediaPlayer;
    private MediaPlayer nextMediaPlayer;
    SurfaceView surfaceView;

    private final Object mediaPlayerLock = new Object();
    private final Object uiLock = new Object();

    private View.OnClickListener onClickListener;
    private MediaPlayer.OnCompletionListener onCompletionListener;

    private ExecutorService executorServiceMediaPlayers;
    private ExecutorService executorServiceSaveFile;
    private Runnable runnableNextMediaPlayer;
    private Runnable runnableSaveFile;
    private volatile boolean saving = false;

    private SurfaceHolder.Callback surfaceHolderCallback;

    private void mediaPlayerCompleted() {
        currentVideo = nextVideo;
        destroyMediaPlayer();
        updateMediaPlayer();
        setUpNextMediaPlayer();
    }

    private void destroyMediaPlayer() {
        synchronized (mediaPlayerLock) {
            if(mediaPlayer != null) {
                mediaPlayer.setDisplay(null);
                mediaPlayer.reset();
                mediaPlayer.release();
                mediaPlayer = null;
            }
        }
    }

    private void updateMediaPlayer() {
        synchronized (mediaPlayerLock) {
            mediaPlayer = nextMediaPlayer;
            if (surfaceCreated) {
                if(mediaPlayer != null) {
                    mediaPlayer.setDisplay(surfaceView.getHolder());
                    mediaPlayer.start();
                }
            }
        }
    }

    private void setUpNextMediaPlayer() {
        nextVideo = masterPlaylist.next();
        executorServiceMediaPlayers.submit(runnableNextMediaPlayer);
    }

    @Override
    protected void onStart() {
        super.onStart();
        onStartHelper();
    }

    public void onStartHelper() {
        executorServiceMediaPlayers = Executors.newSingleThreadExecutor();
        executorServiceSaveFile = Executors.newSingleThreadExecutor();
        setUpRunnables();
        setUpListeners();
        loadSaveFile();
        getExternalStoragePermissionAndFetchMediaFiles();
        setUpMediaPlayersOnCreate();
        setUpSurfaceView();
        setUpFAB();
    }

    @Override
    protected void onStop() {
        super.onStop();
        FloatingActionButton floatingActionButton = findViewById(R.id.fab);
        floatingActionButton.setOnClickListener(null);
        onClickListener = null;
        onCompletionListener = null;
        synchronized (mediaPlayerLock) {
            if(mediaPlayer != null) {
                mediaPlayer.setOnCompletionListener(null);
                mediaPlayer.reset();
                mediaPlayer.release();
            }
            if(nextMediaPlayer != null) {
                nextMediaPlayer.setOnCompletionListener(null);
                nextMediaPlayer.reset();
                nextMediaPlayer.release();
            }
        }
        executorServiceMediaPlayers.shutdown();
        executorServiceMediaPlayers = null;
        executorServiceSaveFile.shutdown();
        executorServiceSaveFile = null;
        runnableNextMediaPlayer = null;
        runnableSaveFile = null;
        surfaceHolderCallback = null;
        surfaceView = findViewById(R.id.surface_view);
        surfaceView.getHolder().removeCallback(surfaceHolderCallback);

    }

    private void setUpSurfaceHolderCallback() {
        surfaceHolderCallback = new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
                synchronized (mediaPlayerLock) {
                    surfaceCreated = true;
                    if(mediaPlayer != null) {
                        mediaPlayer.setDisplay(surfaceHolder);
                        mediaPlayer.start();
                    }
                }
            }

            @Override
            public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int i1, int i2) {

            }

            @Override
            public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {
                synchronized (mediaPlayerLock) {
                    surfaceCreated = false;
                }
                saveFile();
            }
        };
    }

    private void setUpListeners() {
        onClickListener = new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                synchronized (uiLock) {
                    mediaPlayerCompleted();
                }
                if(!saving){
                    executorServiceSaveFile.submit(runnableSaveFile);
                }
            }
        };
        onCompletionListener = new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mediaPlayer2) {
                synchronized (uiLock) {
                    mediaPlayerCompleted();
                }
            }
        };
    }

    private void setUpRunnables() {
        runnableNextMediaPlayer = new Runnable() {
            @Override
            public void run() {
                synchronized (uiLock) {
                    synchronized (mediaPlayerLock) {
                        nextMediaPlayer = MediaPlayer.create(getApplicationContext(), nextVideo.getUri());
                        nextMediaPlayer.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
                        nextMediaPlayer.setOnCompletionListener(onCompletionListener);
                    }
                }
            }
        };
        runnableSaveFile = new Runnable() {
            @Override
            public void run() {
                saving = true;
                saveFile();
                saving = false;
            }
        };
    }

    private void setUpFAB() {
        FloatingActionButton floatingActionButton = findViewById(R.id.fab);
        floatingActionButton.setOnClickListener(onClickListener);
    }

    private void setUpSurfaceView() {
            setUpSurfaceHolderCallback();
            surfaceView = findViewById(R.id.surface_view);
            surfaceView.getHolder().addCallback(surfaceHolderCallback);
    }

    private void setUpMediaPlayersOnCreate() {
        synchronized (mediaPlayerLock) {
            if(masterPlaylist != null) {
                currentVideo = masterPlaylist.next();
                mediaPlayer = MediaPlayer.create(getApplicationContext(), currentVideo.getUri());
                if (mediaPlayer != null) {
                    mediaPlayer.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);         mediaPlayer.setOnCompletionListener(onCompletionListener);
                }
                nextVideo = masterPlaylist.next();
                executorServiceMediaPlayers.submit(runnableNextMediaPlayer);
            }
        }
    }


    private void getVideoFiles() {
        String[] projection = new String[]{
                MediaStore.Video.Media._ID, MediaStore.Video.Media.DISPLAY_NAME,
                MediaStore.Video.Media.TITLE, MediaStore.Images.Media.DATA
        };
        String selection = MediaStore.Video.Media.DISPLAY_NAME + " != ?";
        String[] selectionArgs = new String[]{""};
        String sortOrder = MediaStore.Video.Media.DISPLAY_NAME + " ASC";
        try (Cursor cursor = getApplicationContext().getContentResolver().query(
                MediaStore.Video.Media.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, sortOrder)) {
            if (cursor != null) {
                List<Uri> newURIs = getURIs(cursor);
                if (!uriMap.isEmpty()) {
                    if (masterPlaylist != null) {
                        // Add new videos to the masterPlaylist
                        for (VideoUri videoURIFromUriMap : uriMap.values()) {
                            if (videoURIFromUriMap != null) {
                                if (!masterPlaylist.contains(videoURIFromUriMap)) {
                                    masterPlaylist.add(videoURIFromUriMap);
                                }
                            }
                        }
                        // Remove missing videos
                        ArrayList<VideoUri> a = new ArrayList<>(masterPlaylist.getProbMap().keySet());
                        for (VideoUri videoURI : a) {
                            if (!newURIs.contains(videoURI.getUri())) {
                                masterPlaylist.remove(videoURI);
                                uriMap.remove(videoURI.getUri());
                                videosMap.remove(videoURI.getUri());
                            }
                        }
                    } else {
                        masterPlaylist = new Playlist(
                                new ArrayList<>(uriMap.values()), MASTER_PLAYLIST_NAME);
                    }
                }
            }
        }
        videoFilesLoaded = true;
    }

    private List<Uri> getURIs(Cursor cursor) {
        ArrayList<Uri> newURIs = new ArrayList<>();
        int idCol = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID);
        int nameCol = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME);
        int titleCol = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.TITLE);
        int dataCol = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA);
        while (cursor.moveToNext()) {
            long id = cursor.getLong(idCol);
            String displayName = cursor.getString(nameCol);
            String title = cursor.getString(titleCol);
            String artist = "";
            String data = cursor.getString(dataCol);
            Uri uri = ContentUris.withAppendedId(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, id);
            if (!uriMap.containsKey(uri)) {
                VideoUri videoURI = new VideoUri(uri, data, displayName, artist, title, id);
                uriMap.put(uri, videoURI);
            }
            newURIs.add(uri);
        }
        return newURIs;
    }

    private void getExternalStoragePermissionAndFetchMediaFiles() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
                    != PackageManager.PERMISSION_GRANTED) {
                requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_PERMISSION);
            } else {
                if (!videoFilesLoaded) {
                    getVideoFiles();
                }
            }
        } else {
            if (!videoFilesLoaded) {
                getVideoFiles();
            }
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_PERMISSION && grantResults.length > 0 &&
                grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            getVideoFiles();
            setUpMediaPlayersOnCreate();
            setUpSurfaceView();
        } else {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_PERMISSION);
            }
        }
    }

    private void loadSaveFile() {
        String string = "Loading the save file";
        File file = new File(getBaseContext().getFilesDir(), FILE_SAVE);
        if (file.exists()) {
            try (FileInputStream fileInputStream = getApplicationContext().openFileInput(FILE_SAVE);
                 ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)) {
                masterPlaylist = (Playlist) objectInputStream.readObject();
            } catch (ClassNotFoundException | IOException e) {
                e.printStackTrace();
                string = "Error encountered while loading the save file";
            }
        }
        string = "Save file loaded";
    }

    void saveFile() {
        File file = new File(getBaseContext().getFilesDir(), FILE_SAVE);
        //noinspection ResultOfMethodCallIgnored
        file.delete();
        try (FileOutputStream fos = getApplicationContext().openFileOutput(FILE_SAVE, Context.MODE_PRIVATE);
             ObjectOutputStream objectOutputStream = new ObjectOutputStream(fos)) {
            objectOutputStream.writeObject(masterPlaylist);
            objectOutputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}