作为android初学者,最近把疯狂android讲义和疯狂Java讲义看了一遍,看到书中介绍的知识点非常多,很难全部记住,为了更好的掌握基础知识点,我将开发一个网络音乐播放器-EasyMusic来巩固下,也当作是练练手。感兴趣的朋友可以看看,有设计不足的地方也欢迎指出。

开发之前首先介绍下该音乐播放器将要开发的功能(需求):

1.本地音乐的加载和播放;

2.网络音乐的搜索,试听和下载;

3.音乐的断点下载;

4.点击播放图标加载专辑图片,点击歌词加载歌词并滚动显示(支持滑动歌词改变音乐播放进度);

5.支持基于popupWindow的弹出式菜单;

6.支持后台任务栏显示和控制。

该篇主要是介绍实现多线程下载 :
首先介绍下多线程下载任务的建立过程
1.在netMusicFragment界面点击搜索到的网络音乐,弹出确认下载提示框,点击确认开始建立下载任务:

netMusicListView.setOnItemClickListener(new OnItemClickListener() {public void onItemClick(AdapterView<?> parent, View view, final int position, long id) {new AlertDialog.Builder(mContext).setTitle("下载提示").setMessage("是否下载该歌曲?").setNegativeButton("取消", new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int which) {}   })//点击搜索到的网络音乐列表的第position项即可建立下载任务下载对应歌曲.setPositiveButton("确定", new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int which) {createDownloadTask(position);}}).show();}});//创建下载任务,网络音乐列表netMusicList中有对应position位置处的音乐信息protected void createDownloadTask(int position) {String url = (String)netMusicList.get(position).get("audio");String title = (String)netMusicList.get(position).get("title");String artist = (String)netMusicList.get(position).get("artist");String targetFile = MainActivity.downloadedPath + "/" + title + ".mp3";//这里的下载线程暂时设置为固定的数值3,有兴趣的可以改成手动选择线程数量DownloadUtil downloadUtil = new DownloadUtil(url, targetFile, 3, mContext);Log.d(TAG, "DownloadUrl = " + url);Map<String,Object> map = new HashMap<String, Object>();map.put("title", title);map.put("artist", artist);map.put("url", url);map.put("downloadUtil", downloadUtil);//主activity的executeDownloadUtil方法里调用service执行下载任务((MainActivity)this.getActivity()).executeDownloadUtil(downloadUtil);DownloadFragment.downloadingMusic.add(map);}

2.通过MainActivity来调用服务执行后台下载。

public void executeDownloadUtil(DownloadUtil util) {Toast.makeText(mContext, "executeDownloadUtil", Toast.LENGTH_LONG).show();musicService.downloadMusic(util);}

musicService执行下载方法downloadMusic(util),将下载任务放进线程池中,当线程池中有空闲的线程时会开始下载。之所以将下载任务的执行放在service中是因为service可以后台运行,而如果在activity中执行下载任务的话,后台运行播放器的时候下载任务会暂停。musicService的下载相关代码如下:

public void downloadMusic(final DownloadUtil util) {//传进来的下载任务将放入线程池pool.execute(new DownloadRunnable(util));}private class DownloadRunnable implements Runnable {private DownloadUtil util;public DownloadRunnable(DownloadUtil util) {this.util = util;}@Overridepublic void run() {util.download();}}

多线程下载工具的代码如下,相关注释已经写的很清楚了。

package com.sprd.easymusic.util;import java.io.BufferedInputStream;
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 android.content.Context;
import android.content.Intent;
import android.media.MediaScannerConnection;
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
import android.net.Uri;
import android.util.Log;
import android.widget.Toast;public class DownloadUtil {private String TAG = "DownloadUtil";private String targetUrl = null;private String targetFile = null;//多线程下载的线程数量private int threadNum;private Context mContext;//下载文件的大小private int fileSize;//下载任务是否是暂停状态private boolean pause;//下载任务是否被删除了private boolean delete;//下载完成的线程数,当所有线程下载完成时该值等于threadNumprivate int downloadSuccessThread;//下载的线程数组private DownloadThread[] downloadThreads;public DownloadUtil (String targetUrl, String targetFile, int threadNum, Context context) {this.targetUrl = targetUrl;this.targetFile = targetFile;this.threadNum = threadNum;downloadThreads = new DownloadThread[threadNum];mContext = context;}public void download() {try {URL url = new URL(targetUrl);HttpURLConnection conn = (HttpURLConnection)url.openConnection();conn.setConnectTimeout(5000);//获取下载文件的大小fileSize = conn.getContentLength();conn.disconnect();Log.d(TAG, "download:fileSize = " + fileSize);File file = new File(targetFile);if (!file.exists()) {file.createNewFile();Log.d(TAG, "create file:" + file);}RandomAccessFile raf = new RandomAccessFile(file, "rwd");raf.setLength(fileSize);raf.close();//划分每个线程的下载长度int currentFileSize = fileSize / threadNum == 0 ? fileSize / threadNum : fileSize / threadNum + 1;for (int i=0; i<threadNum; i++) {downloadThreads[i] = new DownloadThread(targetUrl, targetFile, currentFileSize * i, currentFileSize);downloadThreads[i].start();}} catch (Exception e) {e.printStackTrace();}}//获取线程进度public int getDownloadProgress() {int downloadLength = 0;for (DownloadThread thread : downloadThreads) {if (thread != null) downloadLength += thread.length;}Log.d(TAG, "fileSize = " + fileSize + " downloadLength = " +downloadLength);return fileSize > 0 ? downloadLength * 100 / fileSize: 0;}public boolean isPause() {return pause;}public void setPause(boolean pause) {this.pause = pause;}public boolean isDelete() {return delete;}public void setDelete(boolean delete) {this.delete = delete;}//下载线程private class DownloadThread extends Thread {//当前线程下载的长度public int length;//下载的资源链接private String downloadUrl = null;//下载内容到file文件中private String file;//下载的起始位置private int startPos;//需要下载的长度private int currentFileSize;//读取网络音乐数据的缓存private BufferedInputStream bis;public DownloadThread (String downloadUrl, String file, int startPos, int currentFileSize) {this.downloadUrl = downloadUrl;this.file = file;this.startPos = startPos;this.currentFileSize = currentFileSize;}public void run() {try {HttpURLConnection conn = (HttpURLConnection)new URL(downloadUrl).openConnection();conn.setConnectTimeout(5000);conn.setRequestMethod("GET");InputStream is = conn.getInputStream();//获取的输入流数据跳过指定字节数is.skip(startPos);File downloadFile = new File(file);RandomAccessFile raf = new RandomAccessFile(downloadFile, "rwd");//对应文件写入位置也跳过指定字节数raf.seek(startPos);bis = new BufferedInputStream(is);int hasRead = 0;byte[] buff = new byte[1024*4];while (length < currentFileSize && (hasRead = bis.read(buff)) > 0 && !delete) {while (pause){Log.d(TAG, "DownloadUtil pause!");}if (!pause) {//字节读入对应文件raf.write(buff, 0, hasRead);length += hasRead;Log.d(TAG, "read " + hasRead + " bytes");}}Log.d(TAG, "download success? " + (fileSize == length));//关闭资源和链接raf.close();bis.close();if (delete) {boolean isDeleted = downloadFile.delete();Log.d(TAG, "delete file success ? " + isDeleted);} else {Log.d(TAG, "run:currentFileSize = " + currentFileSize + " downloadLength = " + length);//当前线程下载完成时,DownloadUtil的downloadSuccessThread值+1downloadSuccessThread++;if (downloadSuccessThread == threadNum) {Log.d(TAG, "download success");Intent notifyIntent = new Intent("action_download_success");notifyIntent.putExtra("url", targetUrl);//给DownloadFragment发送下载完成的广播mContext.sendBroadcast(notifyIntent);scanFileToMedia(targetFile);}}} catch (Exception e) {e.printStackTrace();}}}//将新下载的歌曲扫描到媒体库中,为后面MusicListFragment添加下拉刷新做铺垫public void scanFileToMedia(final String url) {new Thread(new Runnable() {public void run() {MediaScannerConnection.scanFile(mContext, new String[] {url}, null,new MediaScannerConnection.OnScanCompletedListener() {public void onScanCompleted(String path, Uri uri) {Log.d(TAG, "scan completed : file = " + url);}});}}).start();}}

其中实现多线程下载的关键是多个线程的任务分割(每个线程下载的起始位置以及需要下载的长度-最后一个线程下载长度需要额外注意,因为其下载长度可能与前面的线程不同)

最后不要忘了在AndroidManifest中添加权限(读写SD卡,创建和删除文件,连接网络):

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/><uses-permission android:name="android.permission.INTERNET"/>

音乐播放器已完成,下载地址:
Android音乐播放器

基于android的网络音乐播放器-网络音乐的多线程下载(六)相关推荐

  1. iOS开发之网络音乐播放器(SC音乐)(二)

    iOS开发之网络音乐播放器(SC音乐)(二) 前言 iOS开发之网络音乐播放器(SC音乐)(一)已经介绍完播放控制.音乐数据获取解析.歌词显示等.本文在上文的基础上介绍锁屏播放设置,后台播放设置,手势 ...

  2. Android获取第三方音乐播放器的音乐信息

    最近在做Android手机获取第三方音乐播放器的音乐信息.一开始头疼的很,采集第三方的信息太难了,后面看了一遍博文是关于怎么监听系统的音乐播放信息,发现在播放下一首音乐的时候会发送广播,广播会包含下一 ...

  3. 使用android studio时酷狗音乐,17 Android Studio开发实战:音乐播放器——浪花音乐...

    手机上的多媒体内容讲究声情并茂.悦目且悦耳,这样才能让用户的感官得到最大享受.影视播放器由于存在视频自身的画面,反而限制了开发者的施展空间:而音乐播放器允许定制播放画面,开发者有足够空间施展拳脚.本节 ...

  4. mac音乐播放器QQ音乐好用吗?vip绿钻的QQ音乐有哪些版本优势?

    mac音乐播放器QQ音乐好用吗?当然!QQ音乐是Mac平台用户体验极佳的音乐播放器,最新最热的排行榜.歌单.电台.MV天天推荐,智能音乐搜索.猜你喜欢帮你轻松发现音乐.更何况是不需要绿钻和付费包,VI ...

  5. php文件添加音乐播放器,window_win10系统自带Groove音乐播放器在哪?自带Groove音乐播放器添加音乐等功能的使用教程,   播放器在哪?1 - phpStudy...

    win10系统自带Groove音乐播放器在哪?自带Groove音乐播放器添加音乐等功能的使用教程 播放器在哪? 1.点击桌面的左下端"开始菜单"符号,然后在右上角,找到" ...

  6. 【毕业设计】28-基于单片机的音乐播放器简易音乐播放器设计(原理图+源代码+仿真工程+答辩PPT+答辩论文)

    typora-root-url: ./ [毕业设计]28-基于单片机的音乐播放器简易音乐播放器设计(原理图+源代码+仿真工程+答辩PPT+答辩论文) 文章目录 typora-root-url: ./ ...

  7. php 在线调用音乐播放器,宅音乐播放器,HTML5网页播放器,带后台管理及API调用,使用thinkphp编写...

    宅音乐播放器 宅音乐播放器,HTML5网页播放器,集成后台管理及API调用,目前正在开发中,敬请关注 技术栈 后端:thinkphp 5.1 前端:layui 数据库:mysql 演示 整合依赖安装包 ...

  8. html中加入音乐播放器,HTML网页添加音乐播放器做背景音乐代码-标签audio

    是 HTML 5 的一个新标签,定义声音,比如音乐或其他音频流. 调用格式: src="http://sc1.111ttt.com/2016/1/02/04/195040016323.mp3 ...

  9. 基于android的网络音乐播放器-网络音乐的搜索和展示(五)

    作为android初学者,最近把疯狂android讲义和疯狂Java讲义看了一遍,看到书中介绍的知识点非常多,很难全部记住,为了更好的掌握基础知识点,我将开发一个网络音乐播放器-EasyMusic来巩 ...

最新文章

  1. 【Python】写视频的2种常用方法:write_videofile和videoWrite
  2. python报错:float object is not callable
  3. Android Activity为什么要细化出onCreate、onStart、onResume、onPause、onStop、onDesdroy这么多方法让应用去重载?
  4. oracle 试图访问已经在使用的事物处理临时表,解决ORA-14450:试图访问已经在使用的事务处理临时表...
  5. K均值聚类关于初始聚类中心选取的一种改进(python程序)
  6. 导航视图栏左右键自定义
  7. 【数据库系统设计】关系数据库标准语言SQL(3)
  8. .net下4款不错的微信SDK
  9. java 实例化xpath_Java XPath示例教程
  10. expdp的常用用法
  11. 高通路由器(WIFI)芯片处理器IPQ4029介绍
  12. 各省简称 拼音 缩写_近50个拼音/英文缩写合集 (一)
  13. android4g主板,4G安卓核心板
  14. 菜鸟的Java基础知识学习
  15. HONOR Magicbook 进不了系统
  16. ubuntu虚拟机与windows主机文件传输(命令行)
  17. centos下Intel核显应用ffmpeg的qsv插件编解码
  18. python中class什么意思_python-classmethod在此代码中做什么?
  19. AsyncTask介绍
  20. wps交叉表_WPS交叉引用的使用方法

热门文章

  1. matlab对nii文件处理,使用Nibabel库对nii格式图像的读写操作
  2. mysql数据库uuid()_数据库_MySQL_uuid()
  3. 逍遥咏叹(少年诗词)
  4. 易语言api CreateProcess 创建进程
  5. 关于使用python manage.py createsuperuser时报错
  6. FastJson 配置
  7. Oracle19c的CDB和PDB
  8. Java进阶-案例训练(十六)(商城项目(1))
  9. 离散数学——全序/偏序关系及应用
  10. 一款更懂用户的在线文档创作工具-baklib上线了