最近在自己的音樂播放器中添加了一個下載網路歌曲的功能,雖然還沒有做到邊下邊播放的功能,不過總算是開了一個頭了,下載功搞定了,離目標也就不遠了。
android內建播放器支援“邊下載邊播放”的功能,當你使用系統瀏覽器點擊一個“MP3的下載連結”時,它就會自動播放這首歌曲並儲存到本地(不知道用第三方瀏覽器是否也如此,筆者認為應該是系統瀏覽器會自動識別MP3下載連結,並調用系統播放器來播放)。
與這個過程類似,在筆者做的音樂播放器中,當使用者選擇“歌曲下載”時,會轉到一個webview中,這裡我將webview的初始url定向到"htpp://www.top100.cn"(巨鯨音樂),當點擊MP3的下載連結時,就會將音樂下載到sdcard的根目錄。webview所在activity的代碼如下:
setContentView(R.layout.web);web = (WebView)findViewById(R.id.web);web.setWebViewClient(new DownLoadWebViewClient(this));WebSettings s = web.getSettings();s.setSaveFormData(false);s.setSavePassword(false);s.setUseWideViewPort(true);s.setJavaScriptEnabled(true);s.setLightTouchEnabled(true);web.setWebChromeClient(new WebChromeClient() {public void onProgressChanged(WebView view, int progress) { //Activity和Webview根據載入程度決定進度條的進度大小 //當載入到100%的時候 進度條自動消失 context.setProgress(progress * 100);}});web.loadUrl("http://www.top100.cn/");
web.setWebViewClient(new DownLoadWebViewClient(this));其中DownLoadWebViewClient就使我們下載MP3檔案的關鍵,它繼承自WebViewClient,這裡我們Override它的public boolean shouldOverrideUrlLoading(WebView view, String url)方法,在方法中我們判斷點擊的連結時否為“下載MP3檔案”,如果成立,則啟動一個service來下載mp3檔案,代碼如下:
public class DownLoadWebViewClient extends WebViewClient {private Context context;public DownLoadWebViewClient(Context context){this.context = context;}@Overridepublic boolean shouldOverrideUrlLoading(WebView view, String url) {Log.i("info", "open an url");String urlStr = "";//存放解碼後的url//如果是utf8編碼if (isUtf8Url(url)){urlStr = Utf8URLdecode(url);//如果不是utf8編碼} else {urlStr = URLDecoder.decode(url);}//如果連結是下載top100.cn中的mp3檔案if (url.substring(url.length()-4).equals(".mp3")&&url.substring(7,10).equals("221")){Log.i("info", "mp3 file");String ss[] = urlStr.split("/");String musicName = ss[ss.length-1];//得到音樂檔案的全名(包括尾碼)Log.i("info", "musicfile: " + musicName);//將下載連結和檔案名稱傳遞給下載模組Intent intent = new Intent(context,DownLoadService.class);intent.putExtra("url", url);intent.putExtra("musicName", musicName);context.startService(intent);}return super.shouldOverrideUrlLoading(view, url);}
這裡略去了url解碼的相關方法。其中DownLoadService用於下載MP3檔案並在,它接收DownLoadWebViewClient傳遞來的url和音樂檔案名稱,代碼如下:
public class DownLoadService extends Service implements Runnable{//實現Runable介面private String URL_str;//網路歌曲的路徑private File download_file;//下載的檔案private int total_read = 0;//已經下載檔案的長度(以位元組為單位)private int readLength = 0;//一次性下載的長度(以位元組為單位)private int music_length = 0;//音樂檔案的長度(以位元組為單位)private boolean flag = false;//是否停止下載,停止下載為trueprivate Thread downThread;//下載線程private String musicName;//下載的檔案名稱@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onCreate() {downThread = new Thread(this);//初始化下載線程downThread.start();}@Overridepublic void onStart(Intent intent, int startId) { URL_str = intent.getExtras().getString("url");//擷取下載連結的url musicName = intent.getExtras().getString("musicName");//擷取下載的檔案名稱}@Overridepublic void onDestroy() {flag = true;//停止下載}//實現Run方法,進行歌曲的下載@Overridepublic void run() {FileOutputStream fos = null;//檔案輸出資料流FileInputStream fis = null;//檔案輸出資料流InputStream is = null;//網路檔案輸入資料流URL url = null;try {url = new URL(URL_str);//網路歌曲的urlHttpURLConnection httpConnection = null;httpConnection = (HttpURLConnection)url.openConnection();//開啟網路連接download_file = new File(Environment.//在sdcard根目錄建立一個與要下載的檔案的名字相同的檔案getExternalStorageDirectory()+ "/" + musicName);fos = new FileOutputStream(download_file, true);//初始設定檔案輸出資料流fis = new FileInputStream(download_file);//初始設定檔案輸入資料流total_read = fis.available();//初始化“已下載部分”的長度,此處應為0music_length = httpConnection.getContentLength();//要下載的檔案的總長度if (is == null) {//如果下載失敗則列印日誌,並返回Log.i("info", "donload failed...");return;}byte buf[] = new byte[1024]; //定義下載緩衝區readLength = 0; //一次性下載的長度Log.i("info", "download start...");//向前台發送開始下載廣播Intent startIntent = new Intent();startIntent.setAction("com.alex.downloadstart");sendBroadcast(startIntent);//如果讀取網路檔案的資料流成功,且使用者沒有選擇停止下載,則開始下載檔案while (readLength != -1 && !flag) {if((readLength = is.read(buf))>0){fos.write(buf, 0, readLength);total_read += readLength;//已下載檔案的長度增加}if (total_read == music_length) {//當已下載的長度等於網路檔案的長度,則下載完成flag = false;Log.i("info", "download complete...");//向前台發送下載完成廣播Intent completeIntent = new Intent();completeIntent.setAction("com.alex.downloadcompleted");sendBroadcast(completeIntent);//關閉輸入輸出資料流fos.close();is.close();fis.close();}Thread.sleep(50);//當前現在休眠50毫秒Log.i("info", "download process : " //列印下載進度+ ((total_read+0.0)/music_length*100+"").substring(0, 4)+"%");}} catch (Exception e) {Intent errorIntent = new Intent();errorIntent.setAction("com.alex.downloaderror");sendBroadcast(errorIntent);e.printStackTrace();}}}
這裡有個小bug,如果下載同一首歌曲多次,程式不會多次建立檔案,而將資料寫入與之同名的檔案中。