上节讲了如何搜索网络音乐并以列表的形式展示在我们面前,该节将述点击网络音乐搜索列表中的歌曲后选择试听或者下载的功能实现.
首先是网络音乐列表添加点击监听,点击后弹出提示框-选择试听或者下载,根据选择的不同将执行不同的功能代码,具体代码如下:
NetFragment添加如下代码:
musicList.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapter, View view, int position, long id) {
final int pos = position;
Log.d(TAG, "item " + position + " clicked");
new AlertDialog.Builder(NetFragment.this.getActivity())
.setIcon(R.drawable.ic_launcher)
.setTitle("下载/试听")
.setMessage("请选择下载或试听")
.setNegativeButton("试听", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(NetFragment.this.getActivity(), "试听", 300).show();
shiTing(pos);
}
})
.setPositiveButton("下载",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
downloadMusic(pos);
Toast.makeText(NetFragment.this.getActivity(), "下载", 300).show();
}
}).show();
}
});
private String formatDuration(long dur) {
long totalSecond = dur / 1000;
String minute = totalSecond / 60 + "";
if (minute.length() < 2) minute = "0" + minute ;
String second = totalSecond % 60 + "";
if (second.length() < 2) second = "0" + second;
return minute + ":" + second;
}
试听功能模块关键代码如下:
private void shiTing(int position) {
Map<String, String> item = searchResults.get(position);
final String downloadUrl = item.get("audioUrl");
final String name = item.get("name");
final String artist = item.get("artist");
MainActivity.currentMusicTitle = item.get("name");
MainActivity.currentMusicArtist = item.get("artist");
MainActivity.currentMusicUrl = item.get("audioUrl");
getMusicDuration(downloadUrl);
MainActivity.isPlaying = true;
MainActivity.MSG = MainActivity.MSG_PLAY;
Log.d(TAG, "send name=" + name);
DownloadFragment.playDownloadMusic = true;
Intent musicIntent = new Intent();
musicIntent.setAction("action.changesong");
NetFragment.this.getActivity().sendBroadcast(musicIntent);
}
//获取音乐时长
private void getMusicDuration(String url) {
final String musicUrl = url;
new Thread(new Runnable() {
@Override
public void run() {
int duration = 0;
MediaPlayer mp = null;
try {
mp = new MediaPlayer();
mp.reset();
mp.setDataSource(musicUrl);
mp.prepare();
duration = mp.getDuration();
} catch (Exception e) {
e.printStackTrace();
} finally {
mp.release();
}
MainActivity.currentMusicDuration = formatDuration(duration);
}
}).start();
}
这样后台播放音乐的service将切换音乐开始试听;
下载功能代码如下:
protected synchronized void downloadMusic(int position) {
Map<String, String> item = searchResults.get(position);
final String downloadUrl = item.get("audioUrl");
final String name = item.get("name");
final String artist = item.get("artist");
final String picUrl = item.get("picUrl");
Intent intent = new Intent(NetFragment.this.getActivity(), DownloadService.class);
intent.putExtra("downloadUrl", downloadUrl);
intent.putExtra("name", name);
intent.putExtra("artist", artist);
intent.putExtra("picUrl", picUrl);
Log.d(TAG, "send name=" + name);
NetFragment.this.getActivity().startService(intent);
}
下载任务一般比较耗时,因此应当在后台服务上运行,这里新建一个下载服务-DownloadService,主要代码如下:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
musicUrl = intent.getStringExtra("downloadUrl");
musicName = intent.getStringExtra("name");
musicArtist = intent.getStringExtra("artist");
picUrl = intent.getStringExtra("picUrl");
Log.d(TAG, "receive name=" + musicName);
downloadMusic();
return super.onStartCommand(intent, flags, startId);
}
private void downloadMusic() {
isDownloading = true;
final String targetMusicFile = Environment.getExternalStorageDirectory().getAbsolutePath() + "/musicplayer/"+ musicName + ".mp3";
final String targetPicFile = Environment.getExternalStorageDirectory().getAbsolutePath()
+ "/musicplayer/albumPic/"+ musicName + "-"+ musicArtist + ".jpg";
final DownloadUtil downloadUtil = new DownloadUtil(musicName, musicUrl, targetMusicFile, 1, this);
Log.d(TAG, "picUr = " + picUrl);
final DownloadUtil downloadUtil1 = new DownloadUtil(musicName, picUrl, targetPicFile, 1, this);
final Map<String, Object> item = new HashMap<String, Object>();
item.put("downloadUrl", musicUrl);
item.put("title", musicName);
item.put("artist", musicArtist);
item.put("musicSize", musicSize + "");
item.put("url", targetMusicFile);
DownloadFragment.downloadingList.add(item);//DownloadFragment将下载任务添加到下载列表中
notifyDownloadFragment();
Map<String, Object> map = new HashMap<String, Object>();
map.put("thread", downloadUtil1);
map.put("musicName", musicName);
threadList.add(map);
new Thread(new Runnable() {
@Override
public void run() {
try {
downloadUtil.download();
} catch (Exception e) {
e.printStackTrace();
File file = new File(targetMusicFile);
File file1 = new File(targetPicFile);
try {
if (file.exists()) {
file.delete();
}
if (file1.exists()) {
file1.delete();
}
} catch (Exception e1) {
e1.printStackTrace();
}
Log.d(TAG, "网络不可用!");
}
}
}).start();
在DownloadService中创建了一个下载任务DownloadUtil,其代码如下:
package com.example.comfortmusic_1.util;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;
import com.example.comfortmusic_1.MainActivity;
import com.example.comfortmusic_1.fragment.DownloadFragment;
import com.example.comfortmusic_1.service.DownloadService;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Environment;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
public class DownloadUtil {
private Context context;
private String path;
private String targetFile;
private int threadNum;
public DownloadThread[] threads;
private int fileSize;
private boolean pause = false;//控制下载任务的暂停和继续
private boolean stop = false;//是否删除下载任务
public static boolean isDownloading;
public static String TAG = "DownloadUtil";
private int count;
private boolean isAlbum;
private String musicName;
private String artist;
SQLiteDatabase db;
URL testUrl = null;
public DownloadUtil(String musicName, String path, String targetFile, int threadNum, Context context) {
this.path = path;
this.targetFile = targetFile;
this.threadNum = threadNum;
this.context = context;
this.musicName = musicName;
threads = new DownloadThread[threadNum];
db = MainActivity.dbHelper.getReadableDatabase();
try {
testUrl = new URL("http://img15.3lian.com/2015/f2/50/d/71.jpg");
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//将下载任务存进数据库
private void saveDownloadTask(int threadId, int startPos, int currentSize, int completeSize, int fileSize) {
db.execSQL("insert into downloadTask_1 values(null, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
new Object[] {musicName, path, targetFile, threadNum, threadId, startPos, currentSize, completeSize, fileSize});
Log.d(TAG, musicName + ":thread " + threadId + " 下载信息存储成功!");
}
//随着下载的进行更新数据库中已下载数据
private void updateDownloadTask(int id, int completeSize) {
// db.execSQL("update downloadTask set completeSize = '" + completeSize +
// "' where musicName like '" + musicName + "' and threadId = '" + id + "'" );
db.execSQL("update downloadTask_1 set completeSize = ? " +
"where musicName = ? and threadId = ?", new Object[]{completeSize, musicName, id});
Log.d(TAG, musicName + ":thread " + id + " 下载信息更新成功!");
Log.d(TAG, "completeSize = " + completeSize);
}
//下载完成从数据库移除对应任务
private void removeDownloadTask() {
db.execSQL("delete from downloadTask_1 where musicName = '" + musicName + "'");
Log.d(TAG, musicName + ":下载完成,移出数据库");
}
//从头开始下载调用该方法,断点下载将调用continueDownloadTask
public void download() throws Exception {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5*1000);
conn.setRequestMethod("GET");
fileSize = conn.getContentLength();
Log.d(TAG, "fileSize = " + fileSize);
int partSize = fileSize / threadNum + 1;
conn.disconnect();
File file = new File(targetFile);
if (!file.exists()) {
file.createNewFile();
}
RandomAccessFile raf = new RandomAccessFile(file, "rwd");
raf.setLength(fileSize);
raf.close();
for (int i=0; i<threadNum; i++) {
Log.d("DownloadUtil", "init thread");
int startPos = i * partSize;
RandomAccessFile currentRaf = new RandomAccessFile(targetFile, "rwd");
currentRaf.seek(startPos);
threads[i] = new DownloadThread(i, startPos, partSize, currentRaf);
threads[i].start();
Log.d("DownloadUtil", "thread" + i);
saveDownloadTask(i, startPos, partSize, 0, fileSize);
}
isDownloading = true;
DownloadService.downloadTask++;
}
//断点下载调用该方法继续下载
public void continueDownloadTask(Map<String, Object> map) {
fileSize = Integer.parseInt((String) map.get("fileSize"));
for (int i=0; i<threadNum; i++) {
try {
Log.d("DownloadUtil", "continue download thread");
int startPos = Integer.parseInt((String) map.get("startPos")) + Integer.parseInt((String) map.get("completeSize"));
RandomAccessFile currentRaf = new RandomAccessFile((String) map.get("targetFile"), "rwd");
currentRaf.seek(startPos);
int partSize = Integer.parseInt((String) map.get("currentSize"));//- Integer.parseInt((String) map.get("completeSize"));
threads[i] = new DownloadThread(i, startPos, partSize, currentRaf);
threads[i].setLength(Integer.parseInt((String) map.get("completeSize")));
Log.d(TAG, "threads" + i + "completeSize = " + (String) map.get("completeSize"));
threads[i].start();
Log.d("DownloadUtil", "continue thread:" + i);
updateDownloadTask(i, threads[i].length);
} catch (Exception e) {
e.printStackTrace();
}
}
isDownloading = true;
DownloadService.downloadTask++;
}
public int getCompleteRate () {
int sumSize = 0;
for (DownloadThread thread: threads) {
sumSize += thread.length;
}
Log.d(TAG, "fileSize = " + fileSize);
return sumSize * 100 / fileSize;
}
public static void skipFully (InputStream in, long bytes) throws IOException {
long remaining = bytes;
long len = 0;
while (remaining > 0) {
len = in.skip(remaining);
remaining -= len;
}
}
//下载任务线程
private class DownloadThread extends Thread {
private int startPos;
private int currentPartSize;
private RandomAccessFile currentRaf;
public int length = 0;
private int id;
public DownloadThread(int id, int startPos, int currentPartSize, RandomAccessFile currentRaf) {
this.id = id;
this.startPos = startPos;
this.currentPartSize = currentPartSize;
this.currentRaf = currentRaf;
}
@Override
public void run() {
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5*1000);
conn.setRequestMethod("GET");
conn.setRequestProperty("Range", "bytes = " + currentPartSize);
InputStream inStream = conn.getInputStream();
skipFully(inStream, this.startPos);
byte[] buffer = new byte[1024];
int hasRead = 0;
while (length < currentPartSize && (hasRead = inStream.read(buffer)) > 0 && !stop && !MainActivity.exit) {
while (pause) {//如果pause=true则暂停下载任务-死循环,pause=false时断开死循环继续下载
Log.d("DownloadThread", "Thread pause! " );
if(stop) {//stop=true时终止下载并在DownloadFragment里删除对应的文件
Log.d("DownloadThread", "Thread break! " );
break;
}
}
Log.d("DownloadThread", "Thread start running! " );
currentRaf.write(buffer, 0, hasRead);
length += hasRead;
updateDownloadTask(id,length);
Log.d("DownloadThread", "Thread running! " );
}
currentRaf.close();
inStream.close();
if (stop) {
return;
}
sleep(500);
notifyDownloadFragment();//下载完成通知
} catch (Exception e) {
e.printStackTrace();
Log.d(TAG, "网络不可用!");
}
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
}
private void notifyDownloadFragment() {
count++;
Intent intent = new Intent();
if (count == threadNum) {
Log.d("DownloadThread", "下载完成");
if (isAlbum) {
intent.setAction("action.refreshAlbum");
} else {
intent.setAction("action.downloadComplete");
}
DownloadService.threadList.remove(DownloadUtil.this);
// DownloadService.downloadingList.remove(DownloadUtil.this);
DownloadService.downloadTask--;
if (DownloadService.downloadTask == 0) isDownloading = false;
Log.d(TAG, "downloadingList" + DownloadFragment.downloadingList.size());
removeDownloadTask();
scanFile(context, targetFile);
}
context.sendBroadcast(intent);
}
/下载完成后扫描添加到音乐列表中
public void scanFile(Context context, String filePath) {
// Toast.makeText(context, " scanning file...", 300).show();
MediaScannerConnection.scanFile(context,
new String[] { filePath }, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path1, Uri uri) {
Log.i("ExternalStorage", "Scanned " + path1);
Log.i("ExternalStorage", "-> uri=" + uri);
}
});
Log.d(TAG, "scanFile");
try {
new Thread().sleep(1000);
notifyMainActivity();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//通知MainActivity更新音乐列表
protected void notifyMainActivity() {
Intent intent = new Intent();
intent.setAction("action.refreshMusicList");
context.sendBroadcast(intent);
}
public boolean isPause() {
return pause;
}
public void setPause(boolean pause) {
this.pause = pause;
}
public boolean isStop() {
return stop;
}
public void setStop(boolean stop) {
this.stop = stop;
if (stop) {
File file = new File(targetFile);
if (file.exists()) {
file.delete();
Log.d("DownloadThread", "移除下载任务:" + file.getName());
if (DownloadService.downloadTask > 0) DownloadService.downloadTask--;
removeDownloadTask();
}
}
}
public boolean isDownloading() {
return isDownloading;
}
public void setDownloading(boolean isDownloading) {
this.isDownloading = isDownloading;
}
public boolean isAlbum() {
return isAlbum;
}
public void setAlbum(boolean isAlbum) {
this.isAlbum = isAlbum;
}
}
同时MainActivity中需要添加几个变量:
public static MyDatabaseHelper dbHelper;
public static String downloadedPath;//下载路径
public static boolean exit = false;//退出应用
//init in onCreate
downloadedPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/musicplayer";
dbHelper = new MyDatabaseHelper(this, "comfortmusic_1.db3", 1);
数据库类MyDatabaseHelper:
package com.example.comfortmusic_1.sqlite;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class MyDatabaseHelper extends SQLiteOpenHelper {
final String CREATE_TABLE_SQL = "create table downloadedMusic_1(_id integer primary key autoincrement, title, artist, duration, url)";
final String CREATE_DOWNLOAD_TASK = "create table downloadTask_1(_id integer primary key autoincrement,"
+ " musicName, url, targetFile, threadNum, threadId, startPos, currentSize, completeSize, fileSize)";
public MyDatabaseHelper(Context context, String name, int version) {
super(context, name, null, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
db.execSQL(CREATE_DOWNLOAD_TASK);
db.execSQL(CREATE_TABLE_SQL);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
System.out.println("-------------onUpdate Called-----------" + oldVersion + "------>" + newVersion);
}
}
到这里下载和试听的主要代码差不多了,代码的方法名都是根据意义来取的,比较好懂;下一节将讲述下载列表的展示和下载的暂停/继续/删除功能.
项目源码下载地址如下:
Android本地及网络音乐播放器