android音乐播放器——通过webview下载歌曲
最近在自己的音乐播放器中添加了一个下载网络歌曲的功能,虽然还没有做到边下边播放的功能,不过总算是开了一个头了,下载功搞定了,离目标也就不远了。
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; } @Override public 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; //是否停止下载,停止下载为true private Thread downThread; //下载线程 private String musicName; //下载的文件名 @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { downThread = new Thread(this); //初始化下载线程 downThread.start(); } @Override public void onStart(Intent intent, int startId) { URL_str = intent.getExtras().getString("url"); //获取下载链接的url musicName = intent.getExtras().getString("musicName");//获取下载的文件名 } @Override public void onDestroy() { flag = true; //停止下载 } //实现Run方法,进行歌曲的下载 @Override public void run() { FileOutputStream fos = null; //文件输出流 FileInputStream fis = null; //文件输出流 InputStream is = null; //网络文件输入流 URL url = null; try { url = new URL(URL_str); //网络歌曲的url HttpURLConnection 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(); //初始化“已下载部分”的长度,此处应为0 music_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,如果下载同一首歌曲多次,程序不会多次新建文件,而将数据写入与之同名的文件中。