Android 实例:多线程下载,进度条,音乐播放器

简介

本实例实现多线程下载功能,使用多线程下载李健的一张图片和《假如爱有天意》这首歌,可进行播放。

注:本实例未进行SD卡判断等一般细节的优化,但实现主要功能。


实例源码下载

下载地址:https://github.com/zhuanghongji/MultiThreadDownPlayerZhj


实例图片:


实例源码:

Android 实例:多线程下载,进度条,音乐播放器_第1张图片

Manifest.xml 中添加权限:

    <!-- 在SD卡中创建与删除文件权限 -->
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
    <!-- 向SD卡写入数据权限 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <!-- 授权访问网络 -->
    <uses-permission android:name="android.permission.INTERNET"/>


MainActivity.java :

public class MainActivity extends AppCompatActivity {

    private ImageView mImageView;

    private DownUtil mPictureDownUtil, mSongDownUtil;

    private MediaPlayer mMediaPlayer;

    private ProgressBar mPitureProBar, mSongProBar;
    private int mPitureDownStatus, mSongDownStatus;
    private Button mPictureDownBtn, mSongDownBtn;

    private SeekBar mSongSeekBar;
    private int mPlayStatus;
    private ImageButton mPlayBtn;

    private double mDuration;

    Handler mHandler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:     // 改变图片的下载进度条
                    mPitureProBar.setProgress(mPitureDownStatus);
                    break;
                case 2:     // 图片下载完成后,设置歌手图片
                    Bitmap bitmap = BitmapFactory.decodeFile("/mnt/sdcard/李健.jpg");
                    mImageView.setImageBitmap(bitmap);
                    break;
                case 3:     // 改变歌曲文件的下载进度条
                    mSongProBar.setProgress(mSongDownStatus);
                    break;
                case 4:     // 改变播放的进度
                    mSongSeekBar.setProgress(mPlayStatus);
                    break;
                case 5:     // 播放完成后更改播放按钮图
                    mPlayBtn.setImageResource(R.drawable.start);
                    break;
            }
        }
    };

    public static final String URL_DOWNLOAD_JPG = "http://upload.timedg.com/2015/0228/1425097230412.jpg";
    public static final String URL_DOWNLOAD_MP3 = "http://music.baidu.com/data/music/file?link=" +
            "http://yinyueshiting.baidu.com/data2/music/" +
            "245302327/2452981731439064061128.mp3?xcode=10c72737bdd9a2f07ed60eb536e08c87&song_id=245298173";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initViews();
        intiEvents();
        initMediaPlay();

    }

    /**
     * 初始化播放器
     */
    private void initMediaPlay() {
        try {
            mMediaPlayer = new MediaPlayer();
            mMediaPlayer.setDataSource("/mnt/sdcard/假如爱有天意.mp3");
            mMediaPlayer.prepare();
            Log.i("Duration", String.valueOf(mMediaPlayer.getDuration()));
            Log.i("CurrentPosition", String.valueOf(mMediaPlayer.getCurrentPosition()));
        } catch (IOException e) {
            e.printStackTrace();
        }

        new Thread() {
            @Override
            public void run() {
                final Timer timer = new Timer();
                timer.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        double completeRate = mMediaPlayer.getCurrentPosition();
                        double duration = mMediaPlayer.getDuration();
                        mPlayStatus = (int) (completeRate * 100 / duration);
                        mHandler.sendEmptyMessage(4);
                        Log.i("CurrentPosition", String.valueOf(mMediaPlayer.getCurrentPosition()));
                        if (mMediaPlayer.getDuration() - completeRate <= 1000) {
                            timer.cancel();
                            mHandler.sendEmptyMessage(5);
                        }

                    }
                }, 0, 100);
            }
        }.start();
    }

    /**
     * 初始化事件
     */
    private void intiEvents() {
        mPictureDownBtn.setOnClickListener(new MyOnClickListener());
        mSongDownBtn.setOnClickListener(new MyOnClickListener());
        mPlayBtn.setOnClickListener(new MyOnClickListener());

        mSongSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {

            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
                mMediaPlayer.pause();
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                int progress = mSongSeekBar.getProgress();
                mDuration = mMediaPlayer.getDuration();
                mMediaPlayer.seekTo((int) (progress * mDuration / 100));
                mMediaPlayer.start();
            }
        });

    }

    /**
     * 初始化控件
     */
    private void initViews() {
        mImageView = (ImageView) findViewById(R.id.iv_singer_picture);

        mPitureProBar = (ProgressBar) findViewById(R.id.bar_dowmload_picture);
        mSongProBar = (ProgressBar) findViewById(R.id.bar_dowmload_song);
        mPictureDownBtn = (Button) findViewById(R.id.btn_download_picture);
        mSongDownBtn = (Button) findViewById(R.id.btn_download_song);

        mSongSeekBar = (SeekBar) findViewById(R.id.seekbar_song_play);
        mPlayBtn = (ImageButton) findViewById(R.id.btn_song_play);
    }

    public void makeToast(String s) {
        Toast.makeText(MainActivity.this, s, Toast.LENGTH_SHORT).show();
    }

    private class MyOnClickListener implements OnClickListener {
        @Override
        public void onClick(View view) {
            switch (view.getId()) {
                case R.id.btn_download_picture:
                    downloadPicture();
                    break;
                case R.id.btn_download_song:
                    makeToast("歌曲下载");
                    downloadSong();
                    break;
                case R.id.btn_song_play:
                    playSong();
                    break;
            }
        }
    }

    /**
     * 下载歌手图片
     */
    private void downloadPicture() {
        // 初始化DownUtil对象(最后一个参数指定线程数)
        mPictureDownUtil = new DownUtil(URL_DOWNLOAD_JPG,
                "/mnt/sdcard/李健.jpg", 8);

        new Thread() {
            @Override
            public void run() {
                try {
                    // 开始下载
                    mPictureDownUtil.download();
                } catch (Exception e) {
                    e.printStackTrace();
                }

                // 定义每0.1秒调度获取一次系统的完成进度
                final Timer timer = new Timer();
                timer.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        // 获取下载任务的完成比率
                        double completeRate = mPictureDownUtil.getCompleteRate();
                        mPitureDownStatus = (int) (completeRate * 100);
                        // 发送消息通知界面更新进度条
                        mHandler.sendEmptyMessage(1);
                        // 下载完全后取消任务调度
                        if (mPitureDownStatus >= 100) {
                            timer.cancel();
                            mHandler.sendEmptyMessage(2);
                        }
                    }
                }, 0, 100);
            }
        }.start();
    }
    /**
     * 下载歌曲文件
     */
    private void downloadSong() {
        // 初始化DownUtil对象(最后一个参数指定线程数)
        mSongDownUtil = new DownUtil(URL_DOWNLOAD_MP3,
                "/mnt/sdcard/假如爱有天意.mp3", 1);

        new Thread() {
            @Override
            public void run() {
                try {
                    // 开始下载
                    mSongDownUtil.download();
                } catch (Exception e) {
                    e.printStackTrace();
                }

                // 定义每0.1秒调度获取一次系统的完成进度
                final Timer timer = new Timer();
                timer.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        // 获取下载任务的完成比率
                        double completeRate = mSongDownUtil.getCompleteRate();
                        mSongDownStatus = (int) (completeRate * 100);
                        // 发送消息通知界面更新进度条
                        mHandler.sendEmptyMessage(3);
                        // 下载完全后取消任务调度
                        if (mSongDownStatus >= 100) {
                            timer.cancel();
                        }
                    }
                }, 0, 100);
            }
        }.start();
    }
    /**
     * 播放歌曲
     */
    private void playSong() {
        boolean isPlaying = mMediaPlayer.isPlaying();
        if (isPlaying == false) {
            mMediaPlayer.start();
            mPlayBtn.setImageResource(R.drawable.pause);
        } else if ((isPlaying == true)) {
            mMediaPlayer.pause();
            mPlayBtn.setImageResource(R.drawable.start);
        }
    }
}

DownUtil.java :

public class DownUtil {
    // 定义下载资源的路径
    private String path;
    // 指定所下载的文件的保存位置
    private String targetFile;
    // 定义需要使用多少线程下载资源
    private int threadNum;
    // 定义下载的线程对象
    private DownThread[] threads;
    // 定义下载的文件的总大小
    private long fileSize;

    public DownUtil(String path, String targetFile, int threadNum)
    {
        this.path = path;
        this.threadNum = threadNum;
        // 初始化threads数组
        threads = new DownThread[threadNum];
        this.targetFile = targetFile;
    }

    public void download() throws Exception
    {
        URL url = new URL(path);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();

        conn.setConnectTimeout(5 * 1000);
        conn.setRequestMethod("GET");
        conn.setRequestProperty(
                "Accept",
                "image/gif, image/jpeg, image/pjpeg, image/pjpeg, "
                        + "application/x-shockwave-flash, application/xaml+xml, "
                        + "application/vnd.ms-xpsdocument, application/x-ms-xbap, "
                        + "application/x-ms-application, application/vnd.ms-excel, "
                        + "application/vnd.ms-powerpoint, application/msword, */*");
        conn.setRequestProperty("Accept-Language", "zh-CN");
        conn.setRequestProperty("Charset", "UTF-8");
        conn.setRequestProperty("Connection", "Keep-Alive");

        // 得到文件大小
        fileSize = conn.getContentLength();
        Log.e("fileSize", fileSize + "");
        conn.disconnect();

        long currentPartSize = fileSize / threadNum;
        Log.e("currentPartSize",currentPartSize+"");
        RandomAccessFile file = new RandomAccessFile(targetFile, "rw");

        // 设置本地文件的大小
        file.setLength(fileSize);
        file.close();

        for (int i = 0; i < threadNum; i++)
        {
            // 计算每条线程的下载的开始位置
            long startPos = i * currentPartSize;
            Log.e("startPos",startPos+"");
            // 每个线程使用一个RandomAccessFile进行下载
            RandomAccessFile currentPart = new RandomAccessFile(targetFile,
                    "rw");
            // 定位该线程的下载位置
            currentPart.seek(startPos);
            // 创建下载线程
            threads[i] = new DownThread(startPos, currentPartSize,
                    currentPart);
            // 启动下载线程
            threads[i].start();
        }
    }

    public class DownThread extends Thread {
        // 当前线程的下载位置
        private long startPos;
        // 定义当前线程负责下载的文件大小
        private long currentPartSize;
        // 当前线程需要下载的文件块
        private RandomAccessFile currentPart;
        // 定义已经该线程已下载的字节数
        public long length;

        public DownThread(long startPos, long currentPartSize,
                          RandomAccessFile currentPart) {
            this.startPos = startPos;
            this.currentPartSize = currentPartSize;
            this.currentPart = currentPart;
        }

        @Override
        public void run()
        {
            try
            {
                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection)url
                        .openConnection();
                conn.setConnectTimeout(5 * 1000);
                conn.setRequestMethod("GET");
                conn.setRequestProperty(
                        "Accept",
                        "image/gif, image/jpeg, image/pjpeg, image/pjpeg, "
                                + "application/x-shockwave-flash, application/xaml+xml, "
                                + "application/vnd.ms-xpsdocument, application/x-ms-xbap, "
                                + "application/x-ms-application, application/vnd.ms-excel, "
                                + "application/vnd.ms-powerpoint, application/msword, */*");
                conn.setRequestProperty("Accept-Language", "zh-CN");
                conn.setRequestProperty("Charset", "UTF-8");
                InputStream inStream = conn.getInputStream();

                /*
                 * 跳过startPos个字节,表明该线程只下载自己负责哪部分文件
                 * 如果直接使用 InputStream.skip(long n); 会出现错误
                 * 因为用的时候,返回值往往小于n
                 *  Java 官方文档指出这个问题,建议自己去写一个方法来实现这个功能
                 */
                long at = this.startPos;
                while(at > 0) {
                    long amt = inStream.skip(at);
                    if (amt == -1) {
                        throw new RuntimeException(currentPart + ": unexpected EOF");
                    }
                    at -= amt;
                }

                byte[] buffer = new byte[1024];
                int hasRead = 0;
                // 读取网络数据,并写入本地文件
                while (length < currentPartSize
                        && (hasRead = inStream.read(buffer)) > 0)
                {
                    currentPart.write(buffer, 0, hasRead);
                    // 累计该线程下载的总大小
                    length += hasRead;
                }
                currentPart.close();
                inStream.close();
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    }

    // 获取下载的完成百分比
    public double getCompleteRate()
    {
        // 统计多条线程已经下载的总大小
        long sumSize = 0;
        for (int i = 0; i < threadNum; i++)
        {
            sumSize += threads[i].length;
        }
        // 返回已经完成的百分比
        return sumSize * 1.0 / fileSize;
    }

}

activity_main.xml :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical"
              android:padding="16dp">

    <ImageView
        android:id="@+id/iv_singer_picture"
        android:layout_width="match_parent"
        android:layout_height="240dp"
        android:layout_margin="16dp"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center">

        <ProgressBar
            android:id="@+id/bar_dowmload_picture"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            />

        <Button
            android:id="@+id/btn_download_picture"
            android:layout_width="100dp"
            android:layout_height="wrap_content"
            android:layout_margin="6dp"
            android:text="点击下载歌手图片"/>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center">

        <ProgressBar
            android:id="@+id/bar_dowmload_song"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            />

        <Button
            android:id="@+id/btn_download_song"
            android:layout_width="100dp"
            android:layout_height="wrap_content"
            android:layout_margin="6dp"
            android:text="点击下载歌曲文件"/>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center">

        <SeekBar
            android:id="@+id/seekbar_song_play"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            />

        <ImageButton
            android:id="@+id/btn_song_play"
            android:layout_width="100dp"
            android:layout_height="wrap_content"
            android:layout_margin="6dp"
            android:background="#00000000"
            android:src="@drawable/start"/>
    </LinearLayout>

</LinearLayout>

你可能感兴趣的:(多线程,源码,android,下载,音乐播放器)