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

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

/**
 * 2015年8月15日 16:34:37
 * 音乐播放服务 服务中启动了硬件加速器感应器
 * 实现功能:摇动手机自动播放下一首歌曲
 */
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服务是下载歌曲的DownloadService类,代码如下:

/**
 * 2015年8月15日 16:34:37
 */
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();
    }
    /**
     * 发送广播,通知系统扫描指定的文件
     * 
     */
    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
 */
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类实现文件的下载,保存到本地,使用线程池来进行文件的下载,细看看不难明白

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