SoFunction
Updated on 2025-04-05

Android implements the method of using streaming media to play remote mp3 files

This article describes the Android implementation method of using streaming media to play remote mp3 files. Share it for your reference, as follows:

package ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
/**
 * MediaPlayer does not yet support streaming from external URLs so this class provides a pseudo-streaming function
 * by downloading the content incrementally & playing as soon as we get enough audio in our temporary storage.
 */
public class StreamingMediaPlayer extends Service{
  private static final int INTIAL_KB_BUFFER = 96*10/8;//assume 96kbps*10secs/8bits per byte
  private TextView textStreamed;
  private ImageButton playButton;
  private ProgressBar  progressBar;
  // Track for display by progressBar
  private long mediaLengthInKb, mediaLengthInSeconds;
  private int totalKbRead = 0;
  // Create Handler to call View updates on the main UI thread.
  private final Handler handler = new Handler();
  private MediaPlayer   mediaPlayer;
  private File downloadingMediaFile;
  private boolean isInterrupted;
  private Context context;
  private int counter = 0;
  private static Runnable r;
  private static Thread playerThread;
  private LocalBinder localBinder = new LocalBinder();
  private MediaPlayer player;
  private boolean isPause = false;   //Is the player in pause state  private boolean isSame = false;   //Is the on-demand song the currently played song?  private Integer position = -1;    //Set playback mark  private List<String> music_name;   //Song List  private List<String> music_path;
   public StreamingMediaPlayer(Context context,TextView textStreamed, ImageButton  playButton, Button  streamButton,ProgressBar  progressBar)
   {
      = context;
     = textStreamed;
     = playButton;
     = progressBar;
  }
  /**
   * Progressivly download the media to a temporary location and update the MediaPlayer as new content becomes available.
   */
  public void startStreaming(final String mediaUrl, long  mediaLengthInKb, long  mediaLengthInSeconds) throws IOException {
     = mediaLengthInKb;
     = mediaLengthInSeconds;
    r = new Runnable() {
      public void run() {
        try {
          ("downloadAudioIncrement", "downloadAudioIncrement");
          downloadAudioIncrement(mediaUrl);
        } catch (IOException e) {
          (getClass().getName(), "Unable to initialize the MediaPlayer for fileUrl=" + mediaUrl, e);
          return;
        }
      }
    };
    playerThread = new Thread(r);
    ();
    //new Thread(r).start();
  }
  /**
   * Download the url stream to a temporary location and then call the setDataSource
   * for that local file
   */
  public void downloadAudioIncrement(String mediaUrl) throws IOException {
    URLConnection cn = new URL(mediaUrl).openConnection();
    ("User-Agent","NSPlayer/10.0.0.4072 WMFSDK/10.0");
    ();
    InputStream stream = ();
    if (stream == null) {
      (getClass().getName(), "Unable to create InputStream for mediaUrl:" + mediaUrl);
    }
    downloadingMediaFile = new File((),"");
    // Just in case a prior deletion failed because our code crashed or something, we also delete any previously
    // downloaded file to ensure we start fresh. If you use this code, always delete
    // no longer used downloads else you'll quickly fill up your hard disk memory. Of course, you can also
    // store any previously downloaded file in a separate data cache for instant replay if you wanted as well.
    if (()) {
      ();
    }
    FileOutputStream out = new FileOutputStream(downloadingMediaFile);
    byte buf[] = new byte[16384];
    int totalBytesRead = 0, incrementalBytesRead = 0;
    do {
      int numread = (buf);
      if (numread <= 0)
        break;
      (buf, 0, numread);
      totalBytesRead += numread;
      incrementalBytesRead += numread;
      totalKbRead = totalBytesRead/1000;
      testMediaBuffer();
        fireDataLoadUpdate();
    } while (validateNotInterrupted());
        ();
    if (validateNotInterrupted()) {
        fireDataFullyLoaded();
    }
  }
  private boolean validateNotInterrupted() {
    if (isInterrupted) {
      if (mediaPlayer != null) {
        ();
        //();
      }
      return false;
    } else {
      return true;
    }
  }
  /**
   * Test whether we need to transfer buffered data to the MediaPlayer.
   * Interacting with MediaPlayer on non-main UI thread can causes crashes to so perform this using a Handler.
   */
  private void testMediaBuffer() {
    Runnable updater = new Runnable() {
      public void run() {
        if (mediaPlayer == null) {
          // Only create the MediaPlayer once we have the minimum buffered data
          if ( totalKbRead >= INTIAL_KB_BUFFER) {
            try {
              startMediaPlayer();
            } catch (Exception e) {
              (getClass().getName(), "Error copying buffered conent.", e);
            }
          }
        } else if ( () - () <= 1000 ){
          // NOTE: The media player has stopped at the end so transfer any existing buffered data
          // We test for < 1second of data because the media player can stop when there is still
          // a few milliseconds of data left to play
          transferBufferToMediaPlayer();
        }
      }
    };
    (updater);
  }
  private void startMediaPlayer() {
    try {
      File bufferedFile = new File((),"playingMedia" + (counter++) + ".dat");
      // We double buffer the data to avoid potential read/write errors that could happen if the
      // download thread attempted to write at the same time the MediaPlayer was trying to read.
      // For example, we can't guarantee that the MediaPlayer won't open a file for playing and leave it locked while
      // the media is playing. This would permanently deadlock the file download. To avoid such a deadloack,
      // we move the currently loaded data to a temporary buffer file that we start playing while the remaining
      // data downloads.
      moveFile(downloadingMediaFile,bufferedFile);
      (getClass().getName(),"Buffered File path: " + ());
      (getClass().getName(),"Buffered File length: " + ()+"");
      mediaPlayer = createMediaPlayer(bufferedFile);
      // We have pre-loaded enough content and started the MediaPlayer so update the buttons & progress meters.
      ();
      startPlayProgressUpdater();
      (true);
    } catch (IOException e) {
      (getClass().getName(), "Error initializing the MediaPlayer.", e);
      return;
    }
  }
  public void pausePlayer(){
    try {
      getMediaPlayer().pause();
    } catch (Exception e) {
      ();
    }
  }
  public void startPlayer(){
    getMediaPlayer().start();
  }
  public void stopPlayer(){
    getMediaPlayer().stop();
  }
  /**
    * Create a mediaplayer object based on the file
    */
  private MediaPlayer createMediaPlayer(File mediaFile)
  throws IOException {
    MediaPlayer mPlayer = new MediaPlayer();
    (
        new () {
          public boolean onError(MediaPlayer mp, int what, int extra) {
            (getClass().getName(), "Error in MediaPlayer: (" + what +") with extra (" +extra +")" );
            return false;
          }
        });
    // It appears that for security/permission reasons, it is better to pass a FileDescriptor rather than a direct path to the File.
    // Also I have seen errors such as "PVMFErrNotSupported" and "Prepare failed.: status=0x1" if a file path String is passed to
    // setDataSource(). So unless otherwise noted, we use a FileDescriptor here.
    FileInputStream fis = new FileInputStream(mediaFile);
    (());
    ();
    return mPlayer;
}

package ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
/**
 * MediaPlayer does not yet support streaming from external URLs so this class provides a pseudo-streaming function
 * by downloading the content incrementally & playing as soon as we get enough audio in our temporary storage.
 */
public class StreamingMediaPlayer extends Service{
  private static final int INTIAL_KB_BUFFER = 96*10/8;//assume 96kbps*10secs/8bits per byte
  private TextView textStreamed;
  private ImageButton playButton;
  private ProgressBar  progressBar;
  // Track for display by progressBar
  private long mediaLengthInKb, mediaLengthInSeconds;
  private int totalKbRead = 0;
  // Create Handler to call View updates on the main UI thread.
  private final Handler handler = new Handler();
  private MediaPlayer   mediaPlayer;
  private File downloadingMediaFile;
  private boolean isInterrupted;
  private Context context;
  private int counter = 0;
  private static Runnable r;
  private static Thread playerThread;
  private LocalBinder localBinder = new LocalBinder();
  private MediaPlayer player;
  private boolean isPause = false;   //Is the player in pause state  private boolean isSame = false;   //Is the on-demand song the currently played song?  private Integer position = -1;    //Set playback mark  private List<String> music_name;   //Song List  private List<String> music_path;
   public StreamingMediaPlayer(Context context,TextView textStreamed, ImageButton  playButton, Button  streamButton,ProgressBar  progressBar)
   {
      = context;
     = textStreamed;
     = playButton;
     = progressBar;
  }
  /**
   * Progressivly download the media to a temporary location and update the MediaPlayer as new content becomes available.
   */
  public void startStreaming(final String mediaUrl, long  mediaLengthInKb, long  mediaLengthInSeconds) throws IOException {
     = mediaLengthInKb;
     = mediaLengthInSeconds;
    r = new Runnable() {
      public void run() {
        try {
          ("downloadAudioIncrement", "downloadAudioIncrement");
          downloadAudioIncrement(mediaUrl);
        } catch (IOException e) {
          (getClass().getName(), "Unable to initialize the MediaPlayer for fileUrl=" + mediaUrl, e);
          return;
        }
      }
    };
    playerThread = new Thread(r);
    ();
    //new Thread(r).start();
  }
  /**
   * Download the url stream to a temporary location and then call the setDataSource
   * for that local file
   */
  public void downloadAudioIncrement(String mediaUrl) throws IOException {
    URLConnection cn = new URL(mediaUrl).openConnection();
    ("User-Agent","NSPlayer/10.0.0.4072 WMFSDK/10.0");
    ();
    InputStream stream = ();
    if (stream == null) {
      (getClass().getName(), "Unable to create InputStream for mediaUrl:" + mediaUrl);
    }
    downloadingMediaFile = new File((),"");
    // Just in case a prior deletion failed because our code crashed or something, we also delete any previously
    // downloaded file to ensure we start fresh. If you use this code, always delete
    // no longer used downloads else you'll quickly fill up your hard disk memory. Of course, you can also
    // store any previously downloaded file in a separate data cache for instant replay if you wanted as well.
    if (()) {
      ();
    }
    FileOutputStream out = new FileOutputStream(downloadingMediaFile);
    byte buf[] = new byte[16384];
    int totalBytesRead = 0, incrementalBytesRead = 0;
    do {
      int numread = (buf);
      if (numread <= 0)
        break;
      (buf, 0, numread);
      totalBytesRead += numread;
      incrementalBytesRead += numread;
      totalKbRead = totalBytesRead/1000;
      testMediaBuffer();
        fireDataLoadUpdate();
    } while (validateNotInterrupted());
        ();
    if (validateNotInterrupted()) {
        fireDataFullyLoaded();
    }
  }
  private boolean validateNotInterrupted() {
    if (isInterrupted) {
      if (mediaPlayer != null) {
        ();
        //();
      }
      return false;
    } else {
      return true;
    }
  }
  /**
   * Test whether we need to transfer buffered data to the MediaPlayer.
   * Interacting with MediaPlayer on non-main UI thread can causes crashes to so perform this using a Handler.
   */
  private void testMediaBuffer() {
    Runnable updater = new Runnable() {
      public void run() {
        if (mediaPlayer == null) {
          // Only create the MediaPlayer once we have the minimum buffered data
          if ( totalKbRead >= INTIAL_KB_BUFFER) {
            try {
              startMediaPlayer();
            } catch (Exception e) {
              (getClass().getName(), "Error copying buffered conent.", e);
            }
          }
        } else if ( () - () <= 1000 ){
          // NOTE: The media player has stopped at the end so transfer any existing buffered data
          // We test for < 1second of data because the media player can stop when there is still
          // a few milliseconds of data left to play
          transferBufferToMediaPlayer();
        }
      }
    };
    (updater);
  }
  private void startMediaPlayer() {
    try {
      File bufferedFile = new File((),"playingMedia" + (counter++) + ".dat");
      // We double buffer the data to avoid potential read/write errors that could happen if the
      // download thread attempted to write at the same time the MediaPlayer was trying to read.
      // For example, we can't guarantee that the MediaPlayer won't open a file for playing and leave it locked while
      // the media is playing. This would permanently deadlock the file download. To avoid such a deadloack,
      // we move the currently loaded data to a temporary buffer file that we start playing while the remaining
      // data downloads.
      moveFile(downloadingMediaFile,bufferedFile);
      (getClass().getName(),"Buffered File path: " + ());
      (getClass().getName(),"Buffered File length: " + ()+"");
      mediaPlayer = createMediaPlayer(bufferedFile);
      // We have pre-loaded enough content and started the MediaPlayer so update the buttons & progress meters.
      ();
      startPlayProgressUpdater();
      (true);
    } catch (IOException e) {
      (getClass().getName(), "Error initializing the MediaPlayer.", e);
      return;
    }
  }
  public void pausePlayer(){
    try {
      getMediaPlayer().pause();
    } catch (Exception e) {
      ();
    }
  }
  public void startPlayer(){
    getMediaPlayer().start();
  }
  public void stopPlayer(){
    getMediaPlayer().stop();
  }
  /**
    * Create a mediaplayer object based on the file
    */
  private MediaPlayer createMediaPlayer(File mediaFile)
  throws IOException {
    MediaPlayer mPlayer = new MediaPlayer();
    (
        new () {
          public boolean onError(MediaPlayer mp, int what, int extra) {
            (getClass().getName(), "Error in MediaPlayer: (" + what +") with extra (" +extra +")" );
            return false;
          }
        });
    // It appears that for security/permission reasons, it is better to pass a FileDescriptor rather than a direct path to the File.
    // Also I have seen errors such as "PVMFErrNotSupported" and "Prepare failed.: status=0x1" if a file path String is passed to
    // setDataSource(). So unless otherwise noted, we use a FileDescriptor here.
    FileInputStream fis = new FileInputStream(mediaFile);
    (());
    ();
    return mPlayer;
  }
  /**
    * Convert cache to mediaplay object
    * Transfer buffered data to the MediaPlayer.
    * NOTE: Interacting with a MediaPlayer on a non-main UI thread can cause thread-lock and crashes so
    * this method should always be called using a Handler.
    */
  private void transferBufferToMediaPlayer() {
    try {
      // First determine if we need to restart the player after transferring data.... perhaps the user pressed pause
      boolean wasPlaying = ();
      int curPosition = ();
      // Copy the currently downloaded content to a new buffered File. Store the old File for deleting later.
      File oldBufferedFile = new File((),"playingMedia" + counter + ".dat");
      File bufferedFile = new File((),"playingMedia" + (counter++) + ".dat");
      // This may be the last buffered File so ask that it be delete on exit. If it's already deleted, then this won't mean anything. If you want to
      // keep and track fully downloaded files for later use, write caching code and please send me a copy.
      ();
      moveFile(downloadingMediaFile,bufferedFile);
      // Pause the current player now as we are about to create and start a new one. So far (Android v1.5),
      // this always happens so quickly that the user never realized we've stopped the player and started a new one
      ();
      // Create a new MediaPlayer rather than try to re-prepare the prior one.
      mediaPlayer = createMediaPlayer(bufferedFile);
      (curPosition);
      // Restart if at end of prior buffered content or mediaPlayer was previously playing.
      //  NOTE: We test for < 1second of data because the media player can stop when there is still
      // a few milliseconds of data left to play
      boolean atEndOfFile = () - () <= 1000;
      if (wasPlaying || atEndOfFile){
        ();
      }
      // Lastly delete the previously playing buffered File as it's no longer needed.
      ();
    }catch (Exception e) {
      (getClass().getName(), "Error updating to newly loaded content.", e);
    }
  }
  private void fireDataLoadUpdate() {
    Runnable updater = new Runnable() {
      public void run() {
        //((totalKbRead + " Kb read"));
        float loadProgress = ((float)totalKbRead/(float)mediaLengthInKb);
        //((int)(loadProgress*100));
      }
    };
    (updater);
  }
  private void fireDataFullyLoaded() {
    Runnable updater = new Runnable() {
      public void run() {
          transferBufferToMediaPlayer();
          // Delete the downloaded File as it's now been transferred to the currently playing buffer file.
          ();
        //(("Audio full loaded: " + totalKbRead + " Kb read"));
      }
    };
    (updater);
  }
  //TODO This method should control the playback of the song  public MediaPlayer getMediaPlayer() {
    return mediaPlayer;
  }
  public void startPlayProgressUpdater() {
    float progress = (((float)()/1000)/mediaLengthInSeconds);
    ((int)(progress*100));
    if (()) {
      Runnable notification = new Runnable() {
        public void run() {
          startPlayProgressUpdater();
        }
      };
      (notification,1000);
    }
  }
  public void interrupt() {
    (false);
    isInterrupted = true;
    validateNotInterrupted();
  }
  /**
   * Move the file in oldLocation to newLocation.
   */
  public void moveFile(File  oldLocation, File  newLocation)
  throws IOException {
    if ( ( )) {
      BufferedInputStream reader = new BufferedInputStream( new FileInputStream(oldLocation) );
      BufferedOutputStream writer = new BufferedOutputStream( new FileOutputStream(newLocation, false));
      try {
        byte[] buff = new byte[8192];
        int numChars;
        while ( (numChars = ( buff, 0,  ) ) != -1) {
          ( buff, 0, numChars );
         }
      } catch( IOException ex ) {
        throw new IOException("IOException when transferring " + () + " to " + ());
      } finally {
        try {
          if ( reader != null ){
            ();
            ();
          }
        } catch( IOException ex ){
          (getClass().getName(),"Error closing files when transferring " + () + " to " + () );
        }
      }
    } else {
      throw new IOException("Old location does not exist when transferring " + () + " to " + () );
    }
  }
  /**
    * Get the player object in the service
    * @return player object
    */
  public MediaPlayer getPlayer() {
    return ;
  }
  @Override
  public void onStart(Intent intent, int startId) {
    (intent, startId);
    /**
      * 1. What you need now is to get the song list, the song path, and the song name from the PlayActivity
      * and store it in various collections
      * 2. Then, process these arrays
      */
    music_name = new ArrayList<String>();
    music_path = new ArrayList<String>();
    String info = ("info");
    //songPath = ("songPath");
    (getApplicationContext(), "Song plays abnormally", Toast.LENGTH_SHORT).show();
    player = new MediaPlayer();
    try {
      playMusic(info);
    } catch (Exception e) {
      (getApplicationContext(), "Song plays abnormally", Toast.LENGTH_SHORT).show();
      ();
    }
  }
  //Play music  private void playMusic(String info) throws Exception {
    if ("play".equals(info)) {
      if (isPause) {// After pause, continue playing        ();
        isPause = false;
      } else if (isSame) {// If the song you are playing now and is the same as the on-demand song, continue to play the selected song        ();
      } else {// On-demand song        play();
      }
    } else if ("pause".equals(info)) {
      ();// pause      isPause = true;
    } else if ("before".equals(info)) {
      playBefore();// Play the previous song    } else if ("after".equals(info)) {
      playAfter();// Play the next song    }
  }
  private void play() throws Exception {
    //TODO Get the song path    try {
      ("playtest", "playtest");
    // myApp.setPlaying_position(position); //Set the song Current playback mark      ();
      //(songPath);
      ();
      //musicName = music_name.get(position);
    } catch (Exception e) {
      ();
    }
  }
  private void playBefore() throws Exception {
    if (position == 0) {
      position = music_name.size() - 1;
    } else {
      position--;
    }
    play();
  }
  private void playAfter() throws Exception {
    if (position == 0) {
      position = music_name.size() + 1;
    } else {
      position++;
    }
    play();
  }
  public class LocalBinder extends Binder {
    public StreamingMediaPlayer getService() {
      return ;
    }
  }
  @Override
  public void onDestroy() {
    ();
  }
  @Override
  public boolean onUnbind(Intent intent) {
    return (intent);
  }
  @Override
  public IBinder onBind(Intent intent) {
    return localBinder;
  }
}

For more information about Android related content, please check out the topic of this site:Android multimedia operation skills summary (audio, video, recording, etc.)》、《Android development introduction and advanced tutorial》、《Android View View Tips Summary》、《Android programming activity operation skills summary》、《Summary of Android's SQLite database skills》、《Summary of Android operating json format data skills》、《Android database operation skills summary》、《Android file operation skills summary》、《A summary of SD card operation methods for Android programming and development》、《Android resource operation skills summary"and"Android control usage summary

I hope this article will be helpful to everyone's Android programming design.