編輯:關於Android編程
本文實例講述了Android實現使用流媒體播放遠程mp3文件的方法。分享給大家供大家參考,具體如下:
package com.shadow.util; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import java.util.List; import com.shadow.service.AudioPlayService.LocalBinder; import android.app.Service; import android.content.Context; import android.content.Intent; import android.media.MediaPlayer; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.util.Log; import android.widget.Button; import android.widget.ImageButton; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; /** * 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; //播放器是否處於暫停狀態 private boolean isSame = false; //所點播歌曲是否是當前播放歌曲 private Integer position = -1; //設置播放標記 private List<String> music_name; //歌曲列表 private List<String> music_path; public StreamingMediaPlayer(Context context,TextView textStreamed, ImageButton playButton, Button streamButton,ProgressBar progressBar) { this.context = context; this.textStreamed = textStreamed; this.playButton = playButton; this.progressBar = 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 { this.mediaLengthInKb = mediaLengthInKb; this.mediaLengthInSeconds = mediaLengthInSeconds; r = new Runnable() { public void run() { try { Log.i("downloadAudioIncrement", "downloadAudioIncrement"); downloadAudioIncrement(mediaUrl); } catch (IOException e) { Log.e(getClass().getName(), "Unable to initialize the MediaPlayer for fileUrl=" + mediaUrl, e); return; } } }; playerThread = new Thread(r); playerThread.start(); //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(); cn.addRequestProperty("User-Agent","NSPlayer/10.0.0.4072 WMFSDK/10.0"); cn.connect(); InputStream stream = cn.getInputStream(); if (stream == null) { Log.e(getClass().getName(), "Unable to create InputStream for mediaUrl:" + mediaUrl); } downloadingMediaFile = new File(context.getCacheDir(),"downloadingMedia.dat"); // 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 (downloadingMediaFile.exists()) { downloadingMediaFile.delete(); } FileOutputStream out = new FileOutputStream(downloadingMediaFile); byte buf[] = new byte[16384]; int totalBytesRead = 0, incrementalBytesRead = 0; do { int numread = stream.read(buf); if (numread <= 0) break; out.write(buf, 0, numread); totalBytesRead += numread; incrementalBytesRead += numread; totalKbRead = totalBytesRead/1000; testMediaBuffer(); fireDataLoadUpdate(); } while (validateNotInterrupted()); stream.close(); if (validateNotInterrupted()) { fireDataFullyLoaded(); } } private boolean validateNotInterrupted() { if (isInterrupted) { if (mediaPlayer != null) { mediaPlayer.pause(); //mediaPlayer.release(); } 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) { Log.e(getClass().getName(), "Error copying buffered conent.", e); } } } else if ( mediaPlayer.getDuration() - mediaPlayer.getCurrentPosition() <= 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(); } } }; handler.post(updater); } private void startMediaPlayer() { try { File bufferedFile = new File(context.getCacheDir(),"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); Log.e(getClass().getName(),"Buffered File path: " + bufferedFile.getAbsolutePath()); Log.e(getClass().getName(),"Buffered File length: " + bufferedFile.length()+""); mediaPlayer = createMediaPlayer(bufferedFile); // We have pre-loaded enough content and started the MediaPlayer so update the buttons & progress meters. mediaPlayer.start(); startPlayProgressUpdater(); playButton.setEnabled(true); } catch (IOException e) { Log.e(getClass().getName(), "Error initializing the MediaPlayer.", e); return; } } public void pausePlayer(){ try { getMediaPlayer().pause(); } catch (Exception e) { e.printStackTrace(); } } public void startPlayer(){ getMediaPlayer().start(); } public void stopPlayer(){ getMediaPlayer().stop(); } /** * 根據文件創建一個mediaplayer對象 */ private MediaPlayer createMediaPlayer(File mediaFile) throws IOException { MediaPlayer mPlayer = new MediaPlayer(); mPlayer.setOnErrorListener( new MediaPlayer.OnErrorListener() { public boolean onError(MediaPlayer mp, int what, int extra) { Log.e(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); mPlayer.setDataSource(fis.getFD()); mPlayer.prepare(); return mPlayer; }
package com.shadow.util; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import java.util.List; import com.shadow.service.AudioPlayService.LocalBinder; import android.app.Service; import android.content.Context; import android.content.Intent; import android.media.MediaPlayer; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.util.Log; import android.widget.Button; import android.widget.ImageButton; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; /** * 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; //播放器是否處於暫停狀態 private boolean isSame = false; //所點播歌曲是否是當前播放歌曲 private Integer position = -1; //設置播放標記 private List<String> music_name; //歌曲列表 private List<String> music_path; public StreamingMediaPlayer(Context context,TextView textStreamed, ImageButton playButton, Button streamButton,ProgressBar progressBar) { this.context = context; this.textStreamed = textStreamed; this.playButton = playButton; this.progressBar = 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 { this.mediaLengthInKb = mediaLengthInKb; this.mediaLengthInSeconds = mediaLengthInSeconds; r = new Runnable() { public void run() { try { Log.i("downloadAudioIncrement", "downloadAudioIncrement"); downloadAudioIncrement(mediaUrl); } catch (IOException e) { Log.e(getClass().getName(), "Unable to initialize the MediaPlayer for fileUrl=" + mediaUrl, e); return; } } }; playerThread = new Thread(r); playerThread.start(); //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(); cn.addRequestProperty("User-Agent","NSPlayer/10.0.0.4072 WMFSDK/10.0"); cn.connect(); InputStream stream = cn.getInputStream(); if (stream == null) { Log.e(getClass().getName(), "Unable to create InputStream for mediaUrl:" + mediaUrl); } downloadingMediaFile = new File(context.getCacheDir(),"downloadingMedia.dat"); // 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 (downloadingMediaFile.exists()) { downloadingMediaFile.delete(); } FileOutputStream out = new FileOutputStream(downloadingMediaFile); byte buf[] = new byte[16384]; int totalBytesRead = 0, incrementalBytesRead = 0; do { int numread = stream.read(buf); if (numread <= 0) break; out.write(buf, 0, numread); totalBytesRead += numread; incrementalBytesRead += numread; totalKbRead = totalBytesRead/1000; testMediaBuffer(); fireDataLoadUpdate(); } while (validateNotInterrupted()); stream.close(); if (validateNotInterrupted()) { fireDataFullyLoaded(); } } private boolean validateNotInterrupted() { if (isInterrupted) { if (mediaPlayer != null) { mediaPlayer.pause(); //mediaPlayer.release(); } 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) { Log.e(getClass().getName(), "Error copying buffered conent.", e); } } } else if ( mediaPlayer.getDuration() - mediaPlayer.getCurrentPosition() <= 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(); } } }; handler.post(updater); } private void startMediaPlayer() { try { File bufferedFile = new File(context.getCacheDir(),"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); Log.e(getClass().getName(),"Buffered File path: " + bufferedFile.getAbsolutePath()); Log.e(getClass().getName(),"Buffered File length: " + bufferedFile.length()+""); mediaPlayer = createMediaPlayer(bufferedFile); // We have pre-loaded enough content and started the MediaPlayer so update the buttons & progress meters. mediaPlayer.start(); startPlayProgressUpdater(); playButton.setEnabled(true); } catch (IOException e) { Log.e(getClass().getName(), "Error initializing the MediaPlayer.", e); return; } } public void pausePlayer(){ try { getMediaPlayer().pause(); } catch (Exception e) { e.printStackTrace(); } } public void startPlayer(){ getMediaPlayer().start(); } public void stopPlayer(){ getMediaPlayer().stop(); } /** * 根據文件創建一個mediaplayer對象 */ private MediaPlayer createMediaPlayer(File mediaFile) throws IOException { MediaPlayer mPlayer = new MediaPlayer(); mPlayer.setOnErrorListener( new MediaPlayer.OnErrorListener() { public boolean onError(MediaPlayer mp, int what, int extra) { Log.e(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); mPlayer.setDataSource(fis.getFD()); mPlayer.prepare(); return mPlayer; } /** * 把緩存轉化成mediaplay對象 * 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...e.g. perhaps the user pressed pause boolean wasPlaying = mediaPlayer.isPlaying(); int curPosition = mediaPlayer.getCurrentPosition(); // Copy the currently downloaded content to a new buffered File. Store the old File for deleting later. File oldBufferedFile = new File(context.getCacheDir(),"playingMedia" + counter + ".dat"); File bufferedFile = new File(context.getCacheDir(),"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. bufferedFile.deleteOnExit(); 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 mediaPlayer.pause(); // Create a new MediaPlayer rather than try to re-prepare the prior one. mediaPlayer = createMediaPlayer(bufferedFile); mediaPlayer.seekTo(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 = mediaPlayer.getDuration() - mediaPlayer.getCurrentPosition() <= 1000; if (wasPlaying || atEndOfFile){ mediaPlayer.start(); } // Lastly delete the previously playing buffered File as it's no longer needed. oldBufferedFile.delete(); }catch (Exception e) { Log.e(getClass().getName(), "Error updating to newly loaded content.", e); } } private void fireDataLoadUpdate() { Runnable updater = new Runnable() { public void run() { //textStreamed.setText((totalKbRead + " Kb read")); float loadProgress = ((float)totalKbRead/(float)mediaLengthInKb); //progressBar.setSecondaryProgress((int)(loadProgress*100)); } }; handler.post(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. downloadingMediaFile.delete(); //textStreamed.setText(("Audio full loaded: " + totalKbRead + " Kb read")); } }; handler.post(updater); } //TODO 這個方法應該可以控制歌曲的播放 public MediaPlayer getMediaPlayer() { return mediaPlayer; } public void startPlayProgressUpdater() { float progress = (((float)mediaPlayer.getCurrentPosition()/1000)/mediaLengthInSeconds); progressBar.setProgress((int)(progress*100)); if (mediaPlayer.isPlaying()) { Runnable notification = new Runnable() { public void run() { startPlayProgressUpdater(); } }; handler.postDelayed(notification,1000); } } public void interrupt() { playButton.setEnabled(false); isInterrupted = true; validateNotInterrupted(); } /** * Move the file in oldLocation to newLocation. */ public void moveFile(File oldLocation, File newLocation) throws IOException { if ( oldLocation.exists( )) { 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 = reader.read( buff, 0, buff.length ) ) != -1) { writer.write( buff, 0, numChars ); } } catch( IOException ex ) { throw new IOException("IOException when transferring " + oldLocation.getPath() + " to " + newLocation.getPath()); } finally { try { if ( reader != null ){ writer.close(); reader.close(); } } catch( IOException ex ){ Log.e(getClass().getName(),"Error closing files when transferring " + oldLocation.getPath() + " to " + newLocation.getPath() ); } } } else { throw new IOException("Old location does not exist when transferring " + oldLocation.getPath() + " to " + newLocation.getPath() ); } } /** * 獲取service中的播放器對象 * @return 播放器對象 */ public MediaPlayer getPlayer() { return this.player; } @Override public void onStart(Intent intent, int startId) { super.onStart(intent, startId); /** * 1.現在需要的就是做從PlayActivity裡獲取歌曲列表,和歌曲路徑,歌曲手名 * 並存放到各個集合裡 * 2.之後就是對對這些數組進行處理 */ music_name = new ArrayList<String>(); music_path = new ArrayList<String>(); String info = intent.getStringExtra("info"); //songPath = intent.getStringExtra("songPath"); Toast.makeText(getApplicationContext(), "歌曲播放異常", Toast.LENGTH_SHORT).show(); player = new MediaPlayer(); try { playMusic(info); } catch (Exception e) { Toast.makeText(getApplicationContext(), "歌曲播放異常", Toast.LENGTH_SHORT).show(); e.printStackTrace(); } } //播放音樂 private void playMusic(String info) throws Exception { if ("play".equals(info)) { if (isPause) {// 暫停後,繼續播放 player.start(); isPause = false; } else if (isSame) {// 如果現在播放和與所點播歌曲時同一首,繼續播放所選歌曲 player.start(); } else {// 點播某一首歌曲 play(); } } else if ("pause".equals(info)) { player.pause();// 暫停 isPause = true; } else if ("before".equals(info)) { playBefore();// 播放上一首 } else if ("after".equals(info)) { playAfter();// 播放下一首 } } private void play() throws Exception { //TODO 獲取歌曲路徑 try { Log.i("playtest", "playtest"); // myApp.setPlaying_position(position); //設置歌曲 當前的播放標記 player.reset(); //player.setDataSource(songPath); player.start(); //musicName = music_name.get(position); } catch (Exception e) { e.printStackTrace(); } } 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 StreamingMediaPlayer.this; } } @Override public void onDestroy() { super.onDestroy(); } @Override public boolean onUnbind(Intent intent) { return super.onUnbind(intent); } @Override public IBinder onBind(Intent intent) { return localBinder; } }
更多關於Android相關內容感興趣的讀者可查看本站專題:《Android多媒體操作技巧匯總(音頻,視頻,錄音等)》、《Android開發入門與進階教程》、《Android視圖View技巧總結》、《Android編程之activity操作技巧總結》、《Android操作SQLite數據庫技巧總結》、《Android操作json格式數據技巧總結》、《Android數據庫操作技巧總結》、《Android文件操作技巧匯總》、《Android編程開發之SD卡操作方法匯總》、《Android資源操作技巧匯總》及《Android控件用法總結》
希望本文所述對大家Android程序設計有所幫助。
Android提供了三種動畫機制:屬性動畫(property animation)、補間動畫(tween animation / view animation)、幀動畫(
本文介紹在Android中實現推送方式的基礎知識及相關解決方案。推送功能在手機開發中應用的場景是越來起來了,不說別的,就我們手機上的新聞客戶端就時不j時的推送過來新的消息
本文實例講述了Android編程根據系列圖片繪制動畫的方法。分享給大家供大家參考,具體如下:一、采用系統提供的Animation類,用自帶的方法其中的animation.
本文主要介紹三級緩存的原理解析與實現方式。以前一直覺得三級緩存圖片加載是一個很難理解的東西,但是自己看了一下午再試著寫了一遍之後感覺還是只要沉下心思考還時很容易熟悉掌握的