基于android的网络音乐播放器-网络音乐的多线程下载(六)
作为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的网络音乐播放器-网络音乐的多线程下载(六)相关推荐
- iOS开发之网络音乐播放器(SC音乐)(二)
iOS开发之网络音乐播放器(SC音乐)(二) 前言 iOS开发之网络音乐播放器(SC音乐)(一)已经介绍完播放控制.音乐数据获取解析.歌词显示等.本文在上文的基础上介绍锁屏播放设置,后台播放设置,手势 ...
- Android获取第三方音乐播放器的音乐信息
最近在做Android手机获取第三方音乐播放器的音乐信息.一开始头疼的很,采集第三方的信息太难了,后面看了一遍博文是关于怎么监听系统的音乐播放信息,发现在播放下一首音乐的时候会发送广播,广播会包含下一 ...
- 使用android studio时酷狗音乐,17 Android Studio开发实战:音乐播放器——浪花音乐...
手机上的多媒体内容讲究声情并茂.悦目且悦耳,这样才能让用户的感官得到最大享受.影视播放器由于存在视频自身的画面,反而限制了开发者的施展空间:而音乐播放器允许定制播放画面,开发者有足够空间施展拳脚.本节 ...
- mac音乐播放器QQ音乐好用吗?vip绿钻的QQ音乐有哪些版本优势?
mac音乐播放器QQ音乐好用吗?当然!QQ音乐是Mac平台用户体验极佳的音乐播放器,最新最热的排行榜.歌单.电台.MV天天推荐,智能音乐搜索.猜你喜欢帮你轻松发现音乐.更何况是不需要绿钻和付费包,VI ...
- php文件添加音乐播放器,window_win10系统自带Groove音乐播放器在哪?自带Groove音乐播放器添加音乐等功能的使用教程, 播放器在哪?1 - phpStudy...
win10系统自带Groove音乐播放器在哪?自带Groove音乐播放器添加音乐等功能的使用教程 播放器在哪? 1.点击桌面的左下端"开始菜单"符号,然后在右上角,找到" ...
- 【毕业设计】28-基于单片机的音乐播放器简易音乐播放器设计(原理图+源代码+仿真工程+答辩PPT+答辩论文)
typora-root-url: ./ [毕业设计]28-基于单片机的音乐播放器简易音乐播放器设计(原理图+源代码+仿真工程+答辩PPT+答辩论文) 文章目录 typora-root-url: ./ ...
- php 在线调用音乐播放器,宅音乐播放器,HTML5网页播放器,带后台管理及API调用,使用thinkphp编写...
宅音乐播放器 宅音乐播放器,HTML5网页播放器,集成后台管理及API调用,目前正在开发中,敬请关注 技术栈 后端:thinkphp 5.1 前端:layui 数据库:mysql 演示 整合依赖安装包 ...
- html中加入音乐播放器,HTML网页添加音乐播放器做背景音乐代码-标签audio
是 HTML 5 的一个新标签,定义声音,比如音乐或其他音频流. 调用格式: src="http://sc1.111ttt.com/2016/1/02/04/195040016323.mp3 ...
- 基于android的网络音乐播放器-网络音乐的搜索和展示(五)
作为android初学者,最近把疯狂android讲义和疯狂Java讲义看了一遍,看到书中介绍的知识点非常多,很难全部记住,为了更好的掌握基础知识点,我将开发一个网络音乐播放器-EasyMusic来巩 ...
最新文章
- 【Python】写视频的2种常用方法:write_videofile和videoWrite
- python报错:float object is not callable
- Android Activity为什么要细化出onCreate、onStart、onResume、onPause、onStop、onDesdroy这么多方法让应用去重载?
- oracle 试图访问已经在使用的事物处理临时表,解决ORA-14450:试图访问已经在使用的事务处理临时表...
- K均值聚类关于初始聚类中心选取的一种改进(python程序)
- 导航视图栏左右键自定义
- 【数据库系统设计】关系数据库标准语言SQL(3)
- .net下4款不错的微信SDK
- java 实例化xpath_Java XPath示例教程
- expdp的常用用法
- 高通路由器(WIFI)芯片处理器IPQ4029介绍
- 各省简称 拼音 缩写_近50个拼音/英文缩写合集 (一)
- android4g主板,4G安卓核心板
- 菜鸟的Java基础知识学习
- HONOR Magicbook 进不了系统
- ubuntu虚拟机与windows主机文件传输(命令行)
- centos下Intel核显应用ffmpeg的qsv插件编解码
- python中class什么意思_python-classmethod在此代码中做什么?
- AsyncTask介绍
- wps交叉表_WPS交叉引用的使用方法
热门文章
- matlab对nii文件处理,使用Nibabel库对nii格式图像的读写操作
- mysql数据库uuid()_数据库_MySQL_uuid()
- 逍遥咏叹(少年诗词)
- 易语言api CreateProcess 创建进程
- 关于使用python manage.py createsuperuser时报错
- FastJson 配置
- Oracle19c的CDB和PDB
- Java进阶-案例训练(十六)(商城项目(1))
- 离散数学——全序/偏序关系及应用
- 一款更懂用户的在线文档创作工具-baklib上线了