转载地址: http://www.cnblogs.com/zchajax/archive/2011/05/15/2047129.html
最近在自己的音乐播放器中添加了一个下载网络歌曲的功能,虽然还没有做到边下边播放的功能,不过总算是开了一个头了,下载功搞定了,离目标也就不远了。
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,如果下载同一首歌曲多次,程序不会多次新建文件,而将数据写入与之同名的文件中。