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