android-音乐播放器实现及源码下载(三)

本系列博文,详细讲述一个音乐播放器的实现,以及从网络解析数据获取最新推荐歌曲以及歌曲下载的功能。
功能介绍如下:
1、获取本地歌曲列表,实现歌曲播放功能。
2、利用硬件加速感应器,摇动手机实现切换歌曲的功能
3、利用jsoup解析网页数据,从网络获取歌曲列表,同时实现歌曲和歌词下载到手机本地的功能。
4、通知栏提醒,实现仿QQ音乐播放器的通知栏功能.
涉及的技术有:
1、jsoup解析网络网页,从而获取需要的数据
2、android中访问网络,获取文件到本地的网络请求技术,以及下载文件到本地实现断点下载
3、线程池
4、图片缓存
5、service一直在后台运行
6、手机硬件加速器
7、notification通知栏设计
8、自定义广播
9、android系统文件管理

上面两篇博文android-音乐播放器实现及源码下载(一)讲述了activity基类实现和application类的实现,以及最终设计界面展示。
android-音乐播放器实现及源码下载(二)讲述了主界面的设计和实现

本篇主要讲述两个service服务的设计和实现,一个是播放歌曲的service服务,一个是下载歌曲的service服务。
播放歌曲的service服务代码如下:

/**
 * 2015年8月15日 16:34:37
 * 博文地址:http://blog.csdn.net/u010156024
 * 音乐播放服务 服务中启动了硬件加速器感应器
 * 实现功能:摇动手机自动播放下一首歌曲
 */
public class PlayService extends Service implements
		MediaPlayer.OnCompletionListener {

	private static final String TAG =
			PlayService.class.getSimpleName();

	private SensorManager mSensorManager;

	private MediaPlayer mPlayer;
	private OnMusicEventListener mListener;
	private int mPlayingPosition; // 当前正在播放
	private WakeLock mWakeLock = null;//获取设备电源锁,防止锁屏后服务被停止
	private boolean isShaking;
	private Notification notification;//通知栏
	private RemoteViews remoteViews;//通知栏布局
	private NotificationManager notificationManager;
	// 单线程池
	private ExecutorService mProgressUpdatedListener = Executors
			.newSingleThreadExecutor();

	public class PlayBinder extends Binder {
		public PlayService getService() {
			return PlayService.this;
		}
	}

	@Override
	public IBinder onBind(Intent intent) {
		mSensorManager.registerListener(mSensorEventListener,
				mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
				SensorManager.SENSOR_DELAY_GAME);
		return new PlayBinder();
	}

	@SuppressWarnings("deprecation")
	@Override
	public void onCreate() {
		super.onCreate();
		acquireWakeLock();//获取设备电源锁
		mSensorManager = (SensorManager)
				getSystemService(Context.SENSOR_SERVICE);
		
		MusicUtils.initMusicList();
		mPlayingPosition = (Integer)
				SpUtils.get(this, Constants.PLAY_POS, 0);

		Uri uri = Uri.parse(MusicUtils.sMusicList.get(
				getPlayingPosition()).getUri());
		mPlayer = MediaPlayer.create(PlayService.this,uri);
		mPlayer.setOnCompletionListener(this);
		// 开始更新进度的线程
		mProgressUpdatedListener.execute(mPublishProgressRunnable);
		
		/**
		 * 该方法虽然被抛弃过时,但是通用!
		 */
		PendingIntent pendingIntent = PendingIntent
				.getActivity(PlayService.this,
				0, new Intent(PlayService.this, PlayActivity.class), 0);
		remoteViews = new RemoteViews(getPackageName(),
				R.layout.play_notification);
		notification = new Notification(R.drawable.ic_launcher,
				"歌曲正在播放", System.currentTimeMillis());
		notification.contentIntent = pendingIntent;
		notification.contentView = remoteViews;
		//标记位,设置通知栏一直存在
		notification.flags =Notification.FLAG_ONGOING_EVENT;
		
		Intent intent = new Intent(PlayService.class.getSimpleName());
		intent.putExtra("BUTTON_NOTI", 1);
		PendingIntent preIntent = PendingIntent.getBroadcast(
				PlayService.this,
				1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
		remoteViews.setOnClickPendingIntent(
				R.id.music_play_pre, preIntent);
		
		intent.putExtra("BUTTON_NOTI", 2);
		PendingIntent pauseIntent = PendingIntent.getBroadcast(
				PlayService.this,
				2, intent, PendingIntent.FLAG_UPDATE_CURRENT);
		remoteViews.setOnClickPendingIntent(
				R.id.music_play_pause, pauseIntent);
		
		intent.putExtra("BUTTON_NOTI", 3);
		PendingIntent nextIntent = PendingIntent.getBroadcast
				(PlayService.this,
				3, intent, PendingIntent.FLAG_UPDATE_CURRENT);
		remoteViews.setOnClickPendingIntent(
				R.id.music_play_next, nextIntent);
		
		intent.putExtra("BUTTON_NOTI", 4);
		PendingIntent exit = PendingIntent.getBroadcast(PlayService.this,
				4, intent, PendingIntent.FLAG_UPDATE_CURRENT);
		remoteViews.setOnClickPendingIntent(
				R.id.music_play_notifi_exit, exit);
		
		notificationManager = (NotificationManager)
				getSystemService(NOTIFICATION_SERVICE);
		setRemoteViews();
		
		/**
		 * 注册广播接收者
		 * 功能:
		 * 监听通知栏按钮点击事件 
		 */
		IntentFilter filter = new IntentFilter(
				PlayService.class.getSimpleName());
		MyBroadCastReceiver receiver = new MyBroadCastReceiver();
		registerReceiver(receiver, filter);
	}
	
	public void setRemoteViews(){
		L.l(TAG, "进入——》setRemoteViews()");
		remoteViews.setTextViewText(R.id.music_name,
				MusicUtils.sMusicList.get(
						getPlayingPosition()).getTitle());
		remoteViews.setTextViewText(R.id.music_author,
				MusicUtils.sMusicList.get(
						getPlayingPosition()).getArtist());
		Bitmap icon = MusicIconLoader.getInstance().load(
				MusicUtils.sMusicList.get(
						getPlayingPosition()).getImage());
		remoteViews.setImageViewBitmap(R.id.music_icon,icon == null
				? ImageTools.scaleBitmap(R.drawable.ic_launcher)
						: ImageTools
				.scaleBitmap(icon));
		if (isPlaying()) {
			remoteViews.setImageViewResource(R.id.music_play_pause,
					R.drawable.btn_notification_player_stop_normal);
		}else {
			remoteViews.setImageViewResource(R.id.music_play_pause,
					R.drawable.btn_notification_player_play_normal);
		}
		//通知栏更新
		notificationManager.notify(5, notification);
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		startForeground(0, notification);//让服务前台运行
		return Service.START_STICKY;
	}

	/**
	 * 感应器的时间监听器
	 */
	private SensorEventListener mSensorEventListener =
			new SensorEventListener() {
		@Override
		public void onSensorChanged(SensorEvent event) {
			if (isShaking)
				return;

			if (Sensor.TYPE_ACCELEROMETER == event.sensor.getType()) {
				float[] values = event.values;
				/**
				 * 监听三个方向上的变化,数据变化剧烈,next()方法播放下一首歌曲
				 */
				if (Math.abs(values[0]) > 8 && Math.abs(values[1]) > 8
						&& Math.abs(values[2]) > 8) {
					isShaking = true;
					next();
					// 延迟200毫秒 防止抖动
					new Handler().postDelayed(new Runnable() {
						@Override
						public void run() {
							isShaking = false;
						}
					}, 200);
				}
			}
		}

		@Override
		public void onAccuracyChanged(Sensor sensor, int accuracy) {

		}
	};

	/**
	 * 更新进度的线程
	 */
	private Runnable mPublishProgressRunnable = new Runnable() {
		@Override
		public void run() {
			while (true) {
				if (mPlayer != null && mPlayer.isPlaying()
						&& mListener != null) {
					mListener.onPublish(mPlayer.getCurrentPosition());
				}
			/*
			 * SystemClock.sleep(millis) is a utility function very similar
			 * to Thread.sleep(millis), but it ignores InterruptedException.
			 * Use this function for delays if you do not use
			 * Thread.interrupt(), as it will preserve the interrupted state
			 * of the thread. 这种sleep方式不会被Thread.interrupt()所打断
			 */SystemClock.sleep(200);
			}
		}
	};

	/**
	 * 设置回调
	 * 
	 * @param l
	 */
	public void setOnMusicEventListener(OnMusicEventListener l) {
		mListener = l;
	}

	/**
	 * 播放
	 * 
	 * @param position
	 *            音乐列表播放的位置
	 * @return 当前播放的位置
	 */
	public int play(int position) {
		L.l(TAG, "play(int position)方法");
		if (position < 0)
			position = 0;
		if (position >= MusicUtils.sMusicList.size())
			position = MusicUtils.sMusicList.size() - 1;

		try {
			mPlayer.reset();
			mPlayer.setDataSource(MusicUtils
					.sMusicList.get(position).getUri());
			mPlayer.prepare();

			start();
			if (mListener != null)
				mListener.onChange(position);
		} catch (Exception e) {
			e.printStackTrace();
		}

		mPlayingPosition = position;
		SpUtils.put(Constants.PLAY_POS, mPlayingPosition);
		setRemoteViews();
		return mPlayingPosition;
	}

	/**
	 * 继续播放
	 * 
	 * @return 当前播放的位置 默认为0
	 */
	public int resume() {
		if (isPlaying())
			return -1;
		mPlayer.start();
		setRemoteViews();
		return mPlayingPosition;
	}

	/**
	 * 暂停播放
	 * 
	 * @return 当前播放的位置
	 */
	public int pause() {
		if (!isPlaying())
			return -1;
		mPlayer.pause();
		setRemoteViews();
		return mPlayingPosition;
	}

	/**
	 * 下一曲
	 * 
	 * @return 当前播放的位置
	 */
	public int next() {
		if (mPlayingPosition >= MusicUtils.sMusicList.size() - 1) {
			return play(0);
		}
		return play(mPlayingPosition + 1);
	}

	/**
	 * 上一曲
	 * 
	 * @return 当前播放的位置
	 */
	public int pre() {
		if (mPlayingPosition <= 0) {
			return play(MusicUtils.sMusicList.size() - 1);
		}
		return play(mPlayingPosition - 1);
	}

	/**
	 * 是否正在播放
	 * 
	 * @return
	 */
	public boolean isPlaying() {
		return null != mPlayer && mPlayer.isPlaying();
	}

	/**
	 * 获取正在播放的歌曲在歌曲列表的位置
	 * 
	 * @return
	 */
	public int getPlayingPosition() {
		return mPlayingPosition;
	}

	/**
	 * 获取当前正在播放音乐的总时长
	 * 
	 * @return
	 */
	public int getDuration() {
		if (!isPlaying())
			return 0;
		return mPlayer.getDuration();
	}

	/**
	 * 拖放到指定位置进行播放
	 * 
	 * @param msec
	 */
	public void seek(int msec) {
		if (!isPlaying())
			return;
		mPlayer.seekTo(msec);
	}

	/**
	 * 开始播放
	 */
	private void start() {
		mPlayer.start();
	}

	/**
	 * 音乐播放完毕 自动下一曲
	 */
	@Override
	public void onCompletion(MediaPlayer mp) {
		next();
	}

	@Override
	public boolean onUnbind(Intent intent) {
		L.l("play service", "unbind");
		mSensorManager.unregisterListener(mSensorEventListener);
		return true;
	}

	@Override
	public void onRebind(Intent intent) {
		super.onRebind(intent);
		if (mListener != null)
			mListener.onChange(mPlayingPosition);
	}

	@Override
	public void onDestroy() {
		L.l(TAG, "PlayService.java的onDestroy()方法调用");
		release();
		stopForeground(true);
		mSensorManager.unregisterListener(mSensorEventListener);
		super.onDestroy();
	}

	/**
	 * 服务销毁时,释放各种控件
	 */
	private void release() {
		if (!mProgressUpdatedListener.isShutdown())
			mProgressUpdatedListener.shutdownNow();
		mProgressUpdatedListener = null;
		//释放设备电源锁
		releaseWakeLock();
		if (mPlayer != null)
			mPlayer.release();
		mPlayer = null;
	}

	// 申请设备电源锁
	private void acquireWakeLock() {
		L.l(TAG, "正在申请电源锁");
		if (null == mWakeLock) {
			PowerManager pm = (PowerManager) this
					.getSystemService(Context.POWER_SERVICE);
			mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK
					| PowerManager.ON_AFTER_RELEASE, "");
			if (null != mWakeLock) {
				mWakeLock.acquire();
				L.l(TAG, "电源锁申请成功");
			}
		}
	}

	// 释放设备电源锁
	private void releaseWakeLock() {
		L.l(TAG, "正在释放电源锁");
		if (null != mWakeLock) {
			mWakeLock.release();
			mWakeLock = null;
			L.l(TAG, "电源锁释放成功");
		}
	}

	private class MyBroadCastReceiver extends BroadcastReceiver{
		@Override
		public void onReceive(Context context, Intent intent) {
			if (intent.getAction().equals(
					PlayService.class.getSimpleName())) {
				L.l(TAG, "MyBroadCastReceiver类——》onReceive()");
				L.l(TAG, "button_noti-->"
				+intent.getIntExtra("BUTTON_NOTI", 0));
				switch (intent.getIntExtra("BUTTON_NOTI", 0)) {
				case 1:
					pre();
					break;
				case 2:
					if (isPlaying()) {
						pause(); // 暂停
					} else {
						resume(); // 播放
					}
					break;
				case 3:
					next();
					break;
				case 4:
					if (isPlaying()) {
						pause();
					}
					//取消通知栏
					notificationManager.cancel(5);
					break;
				default:
					break;
				}
			}
			if (mListener != null) {
				mListener.onChange(getPlayingPosition());
			}
		}
	}
	
	/**
	 * 音乐播放回调接口
	 */
	public interface OnMusicEventListener {
		public void onPublish(int percent);

		public void onChange(int position);
	}
}

播放歌曲的服务控制歌曲的播放,该服务功能如下:
1、通过回调接口OnMusicEventListener实现service服务与activity界面进行歌曲播放进度的更新和歌曲的切换。
2、启动了硬件加速器。通过硬件加速器控制手机摇一摇切换歌曲的功能。
3、通知栏启动提醒。
4、创建MediaPlayer进行歌曲的播放。
5、注册广播接受者,监听通知栏的点击时间。
6、获取设备电源锁。这一点请参考我的博文:实现音乐播放器后台Service服务一直存在的解决思路

另一个service服务是下载歌曲的DownloadService类,代码如下:

/**
 * 2015年8月15日 16:34:37
 * 博文地址:http://blog.csdn.net/u010156024
 */
public class DownloadService extends Service {
	private SparseArray<Download> mDownloads = new SparseArray<Download>();
	private RemoteViews mRemoteViews;
	
	public class DownloadBinder extends Binder {
		public DownloadService getService() {
			return DownloadService.this;
		}
	}
	
	@Override
	public IBinder onBind(Intent intent) {
		return new DownloadBinder();
	}
	
	@Override
	public void onCreate() {
		super.onCreate();
	}
	
	public void download(final int id, final String url, final String name) {
		L.l("download", url);
		Download d = new Download(id, url, MusicUtils.getMusicDir() + name);
		d.setOnDownloadListener(mDownloadListener).start(false);
		mDownloads.put(id, d);
	}
	
	private void refreshRemoteView() {
		@SuppressWarnings("deprecation")
		Notification notification = new Notification(
				android.R.drawable.stat_sys_download, "",
				System.currentTimeMillis());
		mRemoteViews = new RemoteViews(getPackageName(),
				R.layout.download_remote_layout);
	    notification.contentView = mRemoteViews;
	    
	    StringBuilder builder = new StringBuilder();
		for(int i=0,size=mDownloads.size();i<size;i++) {
			builder.append(mDownloads.get(mDownloads.keyAt(i))
					.getLocalFileName());
			builder.append("、");
		}
		
		mRemoteViews.setTextViewText(R.id.tv_download_name, 
				builder.substring(0, builder.lastIndexOf("、")));
	    
	    startForeground(R.drawable.ic_launcher, notification);
	}
	
	private void onDownloadComplete(int downloadId) {
		mDownloads.remove(downloadId);
		if(mDownloads.size() == 0) {
			stopForeground(true);
			return;
		}
		
		refreshRemoteView();
	}
	/**
	 * 发送广播,通知系统扫描指定的文件
	 * 请参考我的博文:
	 * http://blog.csdn.net/u010156024/article/details/47681851
	 * 
	 */
	private void scanSDCard() {
		
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
			// 判断SDK版本是不是4.4或者高于4.4
			String[] paths = new String[]{
					Environment.getExternalStorageDirectory().toString()};
			MediaScannerConnection.scanFile(this, paths, null, null);
		} else {
			Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED);
			intent.setClassName("com.android.providers.media",
					"com.android.providers.media.MediaScannerReceiver");
			intent.setData(Uri.parse("file://"+ MusicUtils.getMusicDir()));
			sendBroadcast(intent);
		}
	}
	
	private Download.OnDownloadListener mDownloadListener = 
			new Download.OnDownloadListener() {
		
		@Override
		public void onSuccess(int downloadId) {
			L.l("download", "success");
			Toast.makeText(DownloadService.this, 
					mDownloads.get(downloadId).getLocalFileName() + "下载完成",
					Toast.LENGTH_SHORT).show();
			onDownloadComplete(downloadId);
			scanSDCard();
		}
		
		@Override
		public void onStart(int downloadId, long fileSize) {
			L.l("download", "start");
			refreshRemoteView();
			Toast.makeText(DownloadService.this, "开始下载" + 
					mDownloads.get(downloadId).getLocalFileName(),
					Toast.LENGTH_SHORT).show();
		}
		
		@Override
		public void onPublish(int downloadId, long size) {
//			L.l("download", "publish" + size);
		}
		
		@Override
		public void onPause(int downloadId) {
			L.l("download", "pause");
		}
		
		@Override
		public void onGoon(int downloadId, long localSize) {
			L.l("download", "goon");
		}
		
		@Override
		public void onError(int downloadId) {
			L.l("download", "error");
			Toast.makeText(DownloadService.this, 
					mDownloads.get(downloadId).getLocalFileName() + "下载失败",
					Toast.LENGTH_SHORT).show();
			onDownloadComplete(downloadId);
		}
		
		@Override
		public void onCancel(int downloadId) {
			L.l("download", "cancel");
			onDownloadComplete(downloadId);
		}
	};
}

该下载服务代码比较简单,主要结合Download类来实现真正的文件下载,同时实现Download类的回调接口Download.OnDownloadListener接口实现数据的传递和更新UI,Download类的代码如下:

/**
 * 支持断点下载
 * 支持回调进度、完成、手动关闭、下载失败、暂停/继续、取得文件大小, 获取文件名
 * 支持设置同时下载线程个数
 * 还需要优化
 * 2015年8月15日 16:34:37
 * 博文地址:http://blog.csdn.net/u010156024
 */
public class Download implements Serializable {
	private static final long serialVersionUID = 0x00001000L;
	private static final int START = 1;					// 开始下载
	private static final int PUBLISH = 2;				// 更新进度
	private static final int PAUSE = 3;					// 暂停下载
	private static final int CANCEL = 4;				// 取消下载
	private static final int ERROR = 5;					// 下载错误
	private static final int SUCCESS = 6;				// 下载成功
	private static final int GOON = 7;  				// 继续下载
	
	private static final String UA = "Mozilla/5.0 (Windows NT 6.1; WOW64)" +
			" AppleWebKit/537.36 (KHTML, like Gecko)" +
			" Chrome/37.0.2041.4 Safari/537.36";
	
	private static ExecutorService mThreadPool;// 线程池
	
	static {
		mThreadPool = Executors.newFixedThreadPool(5);  // 默认5个 
	}
		
	private int mDownloadId;							// 下载id
	private String mFileName; 							// 本地保存文件名
	private String mUrl; 								// 下载地址
	private String mLocalPath;							// 本地存放目录

	private boolean isPause = false; 					// 是否暂停
	private boolean isCanceled = false;					// 是否手动停止下载
	
	private OnDownloadListener mListener;		// 监听器
	
	/**
	 * 配置下载线程池的大小
	 * @param maxSize 同时下载的最大线程数
	 */
	public static void configDownloadTheadPool(int maxSize) {
		mThreadPool = Executors.newFixedThreadPool(maxSize);
	}
	
	/**
	 * 添加下载任务
	 * @param downloadId 下载任务的id
	 * @param url        下载地址
	 * @param localPath	  本地存放地址
	 */
	public Download(int downloadId, String url, String localPath) {
		if (!new File(localPath).getParentFile().exists()) {
			new File(localPath).getParentFile().mkdirs();
		}
		
		L.l("下载地址", url);
		
		mDownloadId = downloadId;
		mUrl = url;
		String[] tempArray = url.split("/");
		mFileName = tempArray[tempArray.length-1];
		mLocalPath = localPath.replaceAll("\"|\\(|\\)", "");
	}
	
	/**
	 * 设置监听器
	 * @param listener 设置下载监听器
	 * @return this
	 */
	public Download setOnDownloadListener(OnDownloadListener listener) {
		mListener = listener;
		return this;
	}
	
	/**
	 * 获取文件名
	 * @return 文件名
	 */
	public String getFileName() {
		return mFileName;
	}
	
	public String getLocalFileName() {
		String[] split = mLocalPath.split(File.separator);
		return split[split.length-1];
	}

	/**
	 * 开始下载
	 * params isGoon是否为继续下载
	 */
	@SuppressLint("HandlerLeak")
	public void start(final boolean isGoon) {
		// 处理消息
		final Handler handler = new Handler() {
			@Override
			public void handleMessage(Message msg) {
				switch (msg.what) {
				case ERROR:
					mListener.onError(mDownloadId);
					break;
				case CANCEL:
					mListener.onCancel(mDownloadId);
					break;
				case PAUSE:
					mListener.onPause(mDownloadId);
					break;
				case PUBLISH:
					mListener.onPublish(mDownloadId,
							Long.parseLong(msg.obj.toString()));
					break;
				case SUCCESS:
					mListener.onSuccess(mDownloadId);
					break;
				case START:
					mListener.onStart(mDownloadId,
							Long.parseLong(msg.obj.toString()));
					break;
				case GOON:
					mListener.onGoon(mDownloadId,
							Long.parseLong(msg.obj.toString()));
					break;
				}
			}
		};
		
		// 真正开始下载
		mThreadPool.execute(new Runnable() {
			@Override
			public void run() {
				download(isGoon,handler);
			}
		});
	}
	
	/**
	 * 下载方法
	 * @param handler 消息处理器
	 */
	private void download(boolean isGoon, Handler handler) {
		Message msg = null;
		L.l("开始下载。。。");
		try {
			RandomAccessFile localFile =
					new RandomAccessFile(new File(mLocalPath), "rwd");

			DefaultHttpClient client = new DefaultHttpClient();
			client.setParams(getHttpParams());
			HttpGet get = new HttpGet(mUrl);

			long localFileLength = getLocalFileLength();
			final long remoteFileLength = getRemoteFileLength();
			long downloadedLength = localFileLength;
			
			// 远程文件不存在
			if (remoteFileLength == -1l) {
				L.l("下载文件不存在...");
				localFile.close();
				handler.sendEmptyMessage(ERROR);
				return;
			}

			// 本地文件存在
			if (localFileLength > -1l && localFileLength < remoteFileLength) {
				L.l("本地文件存在...");
				localFile.seek(localFileLength);
				get.addHeader("Range", "bytes=" + localFileLength + "-"
						+ remoteFileLength);
			}
			
			msg = Message.obtain();
			
			// 如果不是继续下载
			if(!isGoon) {
				// 发送开始下载的消息并获取文件大小的消息
				msg.what = START;
				msg.obj = remoteFileLength;
			}else {
				msg.what = GOON;
				msg.obj = localFileLength;
			}
			
			handler.sendMessage(msg);
			
			HttpResponse response = client.execute(get);
			int httpCode = response.getStatusLine().getStatusCode();
			if (httpCode >= 200 && httpCode <= 300) {
				InputStream in = response.getEntity().getContent();
				byte[] bytes = new byte[1024];
				int len = -1;
				while (-1 != (len = in.read(bytes))) {
					localFile.write(bytes, 0, len);
					downloadedLength += len;
					if ((int)(downloadedLength/
							(float)remoteFileLength * 100) % 10 == 0) {
						// 发送更新进度的消息
						msg = Message.obtain();
						msg.what = PUBLISH;
						msg.obj = downloadedLength;
						handler.sendMessage(msg);
					}
					
					// 暂停下载, 退出方法
					if (isPause) {
						// 发送暂停的消息
						handler.sendEmptyMessage(PAUSE);
						L.l("下载暂停...");
						break;
					}
					
					// 取消下载, 删除文件并退出方法
					if (isCanceled) {
						L.l("手动关闭下载。。");
						localFile.close();
						client.getConnectionManager().shutdown();
						new File(mLocalPath).delete();
						// 发送取消下载的消息
						handler.sendEmptyMessage(CANCEL);
						return;
					}
				}

				localFile.close();
				client.getConnectionManager().shutdown();
				// 发送下载完毕的消息
				if(!isPause) handler.sendEmptyMessage(SUCCESS);
			}
		} catch (Exception e) {
			e.printStackTrace();
			// 发送下载错误的消息
			handler.sendEmptyMessage(ERROR);
		}
	}

	/**
	 * 暂停/继续下载
	 * param pause 是否暂停下载
	 * 暂停 return true
	 * 继续 return false
	 */
	public synchronized boolean pause(boolean pause) {
		if(!pause) {
			L.l("继续下载");
			isPause = false;
			start(true); // 开始下载
		}else {
			L.l("暂停下载");
			isPause = true;
		}
		return isPause;
	}

	/**
	 * 关闭下载, 会删除文件
	 */
	public synchronized void cancel() {
		isCanceled = true;
		if(isPause) {
			new File(mLocalPath).delete();
		}
	}

	/**
	 * 获取本地文件大小
	 * @return 本地文件的大小 or 不存在返回-1
	 */
	public synchronized long getLocalFileLength() {
		long size = -1l;
		File localFile = new File(mLocalPath);
		if (localFile.exists()) {
			size = localFile.length();
		}
		L.l("本地文件大小" + size);
		return size <= 0 ? -1l : size;
	}

	/**
	 * 获取远程文件大小 or 不存在返回-1
	 * @return
	 */
	public synchronized long getRemoteFileLength() {
		long size = -1l;
		try {
			DefaultHttpClient client = new DefaultHttpClient();
			client.setParams(getHttpParams());
			HttpGet get = new HttpGet(mUrl);

			HttpResponse response = client.execute(get);
			int httpCode = response.getStatusLine().getStatusCode();
			if (httpCode >= 200 && httpCode <= 300) {
				size = response.getEntity().getContentLength();
			}

			client.getConnectionManager().shutdown();
		} catch (Exception e) {
			e.printStackTrace();
		}

		L.l("远程文件大小" + size);
		return size;
	}

	/**
	 * 设置http参数 不能设置soTimeout
	 * @return HttpParams http参数
	 */
	private static HttpParams getHttpParams() {
		HttpParams params = new BasicHttpParams();

		HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
		HttpProtocolParams.setUseExpectContinue(params, true);
		HttpProtocolParams.setUserAgent(params, UA);
//		ConnManagerParams.setTimeout(params, 10000);
//		HttpConnectionParams.setConnectionTimeout(params, 10000);
		
		return params;
	}
	
	/**
	 * 关闭下载线程池
	 */
	public static void closeDownloadThread() {
		if(null != mThreadPool) {
			mThreadPool.shutdownNow();
		}
	}
	/**
	 * 下载过程中的监听器
	 * 更新下载信息
	 *
	 */
	public interface OnDownloadListener {
		public void onStart(int downloadId, long fileSize);  // 回调开始下载
		public void onPublish(int downloadId, long size);	 // 回调更新进度
		public void onSuccess(int downloadId);				 // 回调下载成功
		public void onPause(int downloadId); 				 // 回调暂停
		public void onError(int downloadId);				 // 回调下载出错
		public void onCancel(int downloadId);			     // 回调取消下载
		public void onGoon(int downloadId, long localSize);  // 回调继续下载
	}
}

Download类实现文件的下载,保存到本地,使用线程池来进行文件的下载,细看看不难明白,里面没有需要特别说明的,如果大家有不明白的地方,欢迎留言,我会尽快给答复的。_

下一篇博文收尾,讲述播放界面的实现和最后的总结。

想要源代码的同学,给我发邮件吧(nyyin@qq.com)


PS:
代码已更新,本地手机没有MP3文件的话,也不会出现崩溃。

  • 2
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 17
    评论
### 回答1: 在CSDN上可以找到许多关于Android在线音乐播放器源码的资源。首先,我们可以在CSDN的搜索框中输入关键词"Android音乐播放器源码"。在搜索结果中,我们可以看到很多用户分享的音乐播放器源码。我们可以根据自己的需求选择合适的源码。一般来说,这些源码会提供基本的音乐播放功能,比如播放、暂停、上一首、下一首、进度控制等。一些高级的源码可能会提供更多功能,比如歌词显示、音效调节、扫描本地音乐库等功能。在CSDN上,我们可以参考其他开发者的源码,并从中学习到一些编程技巧和实现细节。这样可以帮助我们在自己的项目中快速实现一个功能完善的音乐播放器。另外,CSDN还有很多关于Android音乐播放器开发的教程,可以帮助我们了解整个开发过程,并提供一些实用的开发技巧和经验。总之,在CSDN上找到合适的Android音乐播放器源码,是快速实现一个功能齐全的音乐播放器的有效方法。 ### 回答2: Android是一款开源的移动操作系统,许多开发者都在该平台上开发各种应用程序。其中,音乐播放器是非常受欢迎的一种应用。CSDN是一个面向开发者的知识分享平台,上面有丰富的技术文档和源代码资源。 在CSDN上,可以找到许多人分享的Android在线音乐播放器源码。这些源码基本上都是开放的,可以供人们学习和借鉴。使用这些源码,我们可以快速了解音乐播放器的核心功能,例如音乐的播放、暂停、切换歌曲、调整音量等。此外,一些源码还可能包含了网络请求、本地音乐库扫描、歌词显示等更高级的功能。 在获取到源码后,我们可以把它导入到Android Studio这样的开发工具中,进行进一步的研究和修改。通过分析源码实现方式,我们可以学到一些开发技巧和设计思路,提升自己的开发水平。同时,也可以根据实际需要进行修改和优化,打造属于自己的音乐播放器应用。 总之,CSDN上可以找到不少Android在线音乐播放器源码,通过学习和使用这些源码,我们可以快速搭建起一个音乐播放器应用的基本框架,并且通过修改和优化,实现更加个性化的功能。 ### 回答3: CSDN是中国最大的技术社区之一,提供了丰富的技术文章、博客和论坛。在CSDN上,可以找到许多关于Android在线音乐播放器的开源代码。 Android在线音乐播放器是一种应用程序,可以通过互联网在线播放音乐。CSDN上提供的这些源码旨在帮助开发人员快速开发功能完善的音乐播放器应用。 这些源码通常包含了音乐播放器的基本功能,如音乐搜索、播放、暂停、停止、快进、快退等。同时还可能包含播放列表管理、歌词显示、分享功能等扩展功能。这些源码使用Android开发框架,如Java语言、Android Studio等进行开发。 开发人员可以通过下载这些源码并进行相应的修改和定制,以满足自己的需求。比如可以更改界面风格、增加或删除功能,甚至与其他应用进行整合。 CSDN上的这些源码不仅可以作为学习Android开发的参考,还可以用于实际项目中的开发。开发人员可以根据自己的需求,选择合适的源码进行修改和使用,快速搭建起一个功能完善的音乐播放器应用。 总之,在CSDN上可以找到许多Android在线音乐播放器源码,这些源码是一个很好的起点,可以帮助开发人员更快地开发出高质量的音乐播放器应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值