I am creating a radio streaming app for a friend. I decided go down the Exo player route. Everything works fine, apart from if I load a new fragment or turn the screen round. Then I get a new instance of Exo player that starts behind the original. It can get really messy – how can I avoid this?
Here is my code. I open the fragment using:
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { root = inflater.inflate(R.layout.fragment_home, container, false); initializeUIElements(root); return root; }
This is how I call Exo player:
private void initializeUIElements(View root) { playSeekBar = root.findViewById(R.id.progressBar1); playSeekBar.setMax(100); playSeekBar.setVisibility(View.INVISIBLE); } @Override public void onStop() { super.onStop(); if (Util.SDK_INT >= 24) { initializeMediaPlayer(root); } } private void initializeMediaPlayer(View root) { playerView = root.findViewById(R.id.video_view); player = new SimpleExoPlayer.Builder(getContext()).build(); playerView.setPlayer(player); MediaItem media = MediaItem.fromUri(revurl); player.setMediaItem(media); player.setPlayWhenReady(playWhenReady); player.seekTo(currentWindow, playbackPosition); player.prepare(); } @Override public void onPause() { super.onPause(); if (Util.SDK_INT < 24) { releasePlayer(); } } @Override public void onStart() { super.onStart(); if (Util.SDK_INT >= 24) { initializeMediaPlayer(root); } } @Override public void onResume() { super.onResume(); if ((Util.SDK_INT < 24 || player == null)) { initializeMediaPlayer(root); } } @SuppressLint("InlinedApi") private void hideUi() { playerView.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); } private boolean playWhenReady = true; private int currentWindow = 0; private long playbackPosition = 0; private void releasePlayer() { if (player != null) { playWhenReady = player.getPlayWhenReady(); playbackPosition = player.getCurrentPosition(); currentWindow = player.getCurrentWindowIndex(); player.release(); player = null; } }
This is an example of the fragment I’m opening:
public class FacebookFragment extends Fragment { Context c; private WebView mwebview; private String url = "https://www.facebook.com/Revotionofdance"; public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View root = inflater.inflate(R.layout.facebook_fragment, container, false); mwebview = (WebView) root.findViewById(R.id.webview); mwebview.setWebViewClient(new WebViewClient()); mwebview.addJavascriptInterface(new WebAppInterface(c), "Android"); WebSettings webSettings = mwebview.getSettings(); webSettings.setJavaScriptEnabled(true); mwebview.loadUrl(url); return root; } public class MyWebViewClient extends WebViewClient { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if ("https://www.facebook.com/Revotionofdance".equals(Uri.parse(url).getHost())) { // This is my website, so do not override; let my WebView load the page return false; } // Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); startActivity(intent); return true; } } public class WebAppInterface { Context mContext; /** * Instantiate the interface and set the context */ WebAppInterface(Context c) { mContext = c; } /** * Show a toast from the web page */ @JavascriptInterface public void showToast(String toast) { Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show(); } }
This is my main activity where I call the fragments from:
DrawerLayout drawer = findViewById(R.id.drawer_layout); NavigationView navigationView = findViewById(R.id.nav_view); // Passing each menu ID as a set of Ids because each // menu should be considered as top level destinations. mAppBarConfiguration = new AppBarConfiguration.Builder( R.id.nav_home, R.id.nav_facebook, R.id.nav_insta,R.id.nav_snap,R.id.nav_rodr) .setDrawerLayout(drawer) .build(); NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment); NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration); NavigationUI.setupWithNavController(navigationView, navController); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onSupportNavigateUp() { NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment); return NavigationUI.navigateUp(navController, mAppBarConfiguration) || super.onSupportNavigateUp(); }
Advertisement
Answer
According to your code above currently in your onStop() method you initialise a new instance of SimpleExoPlayer instead of release it. You initialize the player in the onStart() or onResume() methods depending on the API level and release the created player in your onPause() or onStop() methods.
Currently you have:
@Override public void onStop() { super.onStop(); if (Util.SDK_INT >= 24) { initializeMediaPlayer(root); } }
Change it to:
@Override public void onStop() { super.onStop(); if (Util.SDK_INT >= 24) { releasePlayer(); } }