Handler && Timer(音乐播放器)

研究这两个的区别好久了,一个定时器用Handler做出来了,但是用timer怎么都做不出来,现在终于明白了,好开心。

这是我学到的一句话:Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。

在我们Android开发过程中,经常需要执行一些短周期的定时任务,这时候有两个选择Timer或者Handler。然而个人认为:Handler在多个方面比Timer更为优秀,更推荐使用。

  1. 可重复执行

Handler可以重复执行某个任务。
Timer若在某个任务执行/取消之后,再次执行则会抛出一个IllegalStateException异常。为了避免这个异常,需要重新创建一个Timer对象。

  1. UI界面更新

Handler:在创建的时候可以指定所在的线程,一般在Activity中构建的,即主线程上,所以可以在回调方法中很方便的更新界面。
Timer:异步回调,所以必须借助Handler去更新界面,不方便。
既然都得用Handler去更新界面了,为何不如把定时的功能也交给Handler去做呢?

这是实现音乐播放器的功能,随着音乐的播放,自动更新时间。

  • Timer实现
package com.mingrisoft;

import java.io.File;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;

import android.app.Activity;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.net.Uri;
import android.os.Bundle;

import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;

public class MainActivity extends Activity implements OnSeekBarChangeListener {
    private MediaPlayer player = new MediaPlayer(); // MediaPlayer对象
    private boolean isPause = false; // 是否暂停
    private File file; // 要播放的音频文件
    private TextView hint; // 声明显示提示信息的文本框
    private SeekBar sbar;
    private Timer timer, timer2;
    private TimerTask task, task2;
    private int position;
    private TextView mTextView_one, mTextView_two;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        sbar = (SeekBar) findViewById(R.id.sbar);
        final Button button1 = (Button) findViewById(R.id.button1); // 获取播放按钮
        final Button button2 = (Button) findViewById(R.id.button2); // 获取“暂停/继续”按钮
        final Button button3 = (Button) findViewById(R.id.button3); // 获取“停止”按钮
        mTextView_one = (TextView) findViewById(R.id.main_textview_time_one);
        mTextView_two = (TextView) findViewById(R.id.main_textview_time_two);
        file = new File("/mnt/sdcard/Gary Allan - One More Time.mp3"); // 获取要播放的文件

        sbar.setOnSeekBarChangeListener(this);
        // 初始化计时器
        timer = new Timer();
        task = new TimerTask() {
            @Override
            public void run() {
                if (player != null && player.isPlaying()) {
                    final int progress = player.getCurrentPosition();

                    int total = player.getDuration();
                    sbar.setMax(total);
                    sbar.setProgress(progress);
                 //修改界面的相关设置只能在UI线程中执行  
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            String time = formatTime(progress);
                            mTextView_one.setText(time);
                        }
                    });
                }
            }
        };
        timer.schedule(task, 1000, 1000);

        // 为MediaPlayer对象添加完成事件监听器
        player.setOnCompletionListener(new OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                play(); // 重新开始播放
            }
        });
        // 为“播放”按钮添加单击事件监听器
        button1.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                play();// 开始播放音乐
                if (isPause) {
                    button2.setText("暂停");
                    isPause = false; // 设置暂停标记变量的值为false
                }
                button2.setEnabled(true); // “暂停/继续”按钮可用
                button3.setEnabled(true); // “停止”按钮可用
                button1.setEnabled(false); // “播放”按钮不可用
            }
        });
        // 为“暂停/继续”按钮添加单击事件监听器
        button2.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                if (player.isPlaying() && !isPause) {
                    player.pause(); // 暂停播放;
                    isPause = true;
                    ((Button) v).setText("继续");
                    button1.setEnabled(true); // “播放”按钮可用
                } else {
                    player.start(); // 继续播放
                    ((Button) v).setText("暂停");
                    isPause = false;
                    button1.setEnabled(false); // “播放”按钮不可用
                }
            }
        });
        // 为“停止”按钮添加单击事件监听器
        button3.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                player.stop(); // 停止播放;
                button2.setEnabled(false); // “暂停/继续”按钮不可用
                button3.setEnabled(false); // “停止”按钮不可用
                button1.setEnabled(true); // “播放”按钮可用
            }
        });
    }

    // 播放音乐的方法
    private void play() {
        try {
            player.reset();
            player.setDataSource(file.getAbsolutePath()); // 重新设置要播放的音频
            player.prepare(); // 预加载音频
            player.start(); // 开始播放
            int position = player.getDuration();
            mTextView_two.setText(formatTime(position));
        } catch (Exception e) {
            e.printStackTrace(); // 输出异常信息
        }
    }

    @Override
    protected void onDestroy() {
        if (player.isPlaying()) {
            player.stop(); // 停止音频的播放
        }
        player.release(); // 释放资源
        timer.cancel();
        task.cancel();
        timer = null;
        task = null;

        super.onDestroy();
    }

    @Override
    public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onStartTrackingTouch(SeekBar arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onStopTrackingTouch(SeekBar arg0) {
        // TODO Auto-generated method stub
        int position = sbar.getProgress();
        if (player != null && player.isPlaying()) {
            player.seekTo(position);
        }
    }
    private static String formatTime(int time) {

        if (time / 1000 % 60 < 10) {
            return time / 1000 / 60 + ":0" + time / 1000 % 60;
        } else {
            return time / 1000 / 60 + ":" + time / 1000 % 60;
        }
    }
}

这里写图片描述

  • Handler实现
package com.example.mymusic;

import java.io.File;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;

public class Main extends Activity implements OnClickListener {
    MediaPlayer mMediaPlayer;
    Button mPlayButton, mPauseButton, mStopButton, mForWard, mBackWard;
    SeekBar mSeekBar;
    TextView mTextView_one, mTextView_two;
    Handler mHandler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            if (mMediaPlayer != null) {
                int currentPosition = mMediaPlayer.getCurrentPosition();
                mSeekBar.setProgress(currentPosition);
                String time = formatTime(currentPosition);
                mTextView_one.setText(time);
                mHandler.sendEmptyMessageDelayed(0, 1000);
            }

        };
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        initView();
        setPlayListener();
        setPause();
        setStopListener();
        setSeekBarListener();
    }

    /********************************************************
     * 初始化控件
     */
    private void initView() {
        mPlayButton = (Button) findViewById(R.id.button1);
        mPauseButton = (Button) findViewById(R.id.button2);
        mStopButton = (Button) findViewById(R.id.button3);
        mForWard = (Button) findViewById(R.id.media_forward);
        mBackWard = (Button) findViewById(R.id.media_backwrad);
        mSeekBar = (SeekBar) findViewById(R.id.seekBar);
        mTextView_one = (TextView) findViewById(R.id.main_textview_time_one);
        mTextView_two = (TextView) findViewById(R.id.main_textview_time_two);
        mForWard.setOnClickListener(this);
        mBackWard.setOnClickListener(this);
    }

    /*******************************************************
     * 必须在方法中初始化mMediaPlayer对象 播放音乐的初始化动作 setDataSource(path)设置音频的路径就是mp3的路径
     * mMediaPlayer.prepare()此方法就是装载音频文件
     * mMediaPlayer.start();此方法必须在上面的方法执行完毕后才可以
     * 
     * @param path
     *            传入音频文件的路径参数
     */
    public void play(String path) {
        if (mMediaPlayer == null) {
            try {
                mMediaPlayer = new MediaPlayer();
                mMediaPlayer.setDataSource(path);
                mMediaPlayer.prepare();
                mMediaPlayer.start();
                int position = mMediaPlayer.getDuration();
                mTextView_two.setText(formatTime(position));
                mSeekBar.setMax(position);
                mHandler.sendEmptyMessageDelayed(0, 1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else if (mMediaPlayer != null && !mMediaPlayer.isPlaying()) {
            mMediaPlayer.start();
        }
    }

    /**********************************************************
     * 设置播放按钮的点击事件 同时执行播放音乐方法 此点击事件采用的是匿名内部类的方式实现的
     */
    public void setPlayListener() {
        mPlayButton.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                play("/mnt/sdcard/a.mp3");
            }
        });
    }

    /*********************************************************
     * 暂停当前播放的音乐
     */
    @SuppressLint("SdCardPath")
    public void setPause() {
        mPauseButton.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
                    mMediaPlayer.pause();
                }
            }
        });
        /*
         * if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
         * mMediaPlayer.pause(); }
         */
    }

    /*************************************************
     * 设置点击暂停按钮的点击事件 同时执行mMediaPlayer的暂停pause方法
     * 此点击事件的方式是采用xml属性中设置onclick这个属性的方法
     */
    public void pause(View v) {
        setPause();
    }

    /************************************************
     * 停止播放音乐的方法 同时我们要释放内存,节约内存 把mMediaPlayer赋值为空,返回到开始播放的状态
     */
    public void stop() {
        if (mMediaPlayer != null) {
            mMediaPlayer.stop();
            mMediaPlayer.release();
            mMediaPlayer = null;
        }
    }

    /*************************************************
     * 设置停止按钮的监听事件 此按钮的监听事件采用第三种方式
     */
    public void setStopListener() {
        mStopButton.setOnClickListener(listener);
    }

    OnClickListener listener = new OnClickListener() {
        @Override
        public void onClick(View v) {
            stop();
        }
    };

    /***************************************************
     * 设置快进10秒方法
     */
    public void forWard() {
        if (mMediaPlayer != null) {
            int position = mMediaPlayer.getCurrentPosition();
            mMediaPlayer.seekTo(position + 10000);
        }
    }

    /*****************************************************
     * 设置后退10秒的方法
     */
    public void backWard() {
        if (mMediaPlayer != null) {
            int position = mMediaPlayer.getCurrentPosition();
            if (position > 10000) {
                position -= 10000;
            } else {
                position = 0;
            }
            mMediaPlayer.seekTo(position);
        }
    }

    /********************************************************
     * 设置seekbar的拖动监听事件
     */
    public void setSeekBarListener() {
        mSeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
            public void onStopTrackingTouch(SeekBar seekBar) {

            }

            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            public void onProgressChanged(SeekBar seekBar, int progress,
                    boolean fromUser) {
                // TODO Auto-generated method stub
                if (mMediaPlayer != null && fromUser) {
                    mMediaPlayer.seekTo(progress);
                }
            }
        });
    }

    /*******************************************************
     * 关闭当前页面,停止播放
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        stop();
    }

    /********************************************************
     * 设置快进和后退按钮的监听事件
     * 
     * @param v
     */
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.media_forward:
            forWard();
            break;
        case R.id.media_backwrad:
            backWard();
            break;
        default:
            break;
        }
    }

    /********************************************************
     * 对时间的格式化
     * 
     * @param time
     * @return
     */
    public static String formatTime(int time) {
        if (time / 1000 % 60 < 10) {
            return time / 1000 / 60 + ":0" + time / 1000 % 60;
        } else {
            return time / 1000 / 60 + ":" + time / 1000 % 60;
        }
    }
}

这里写图片描述

总结:

主线程不更新UI,在Timer的例子里面,UI的改变是在主线程里面,所以会有线程安全问题,我想把它单独出来,水平有限,而且代码比较粗糙,不建议使用。

下面是二者安全使用的对比:
http://blog.csdn.net/dany1202/article/details/6129548

猜你喜欢

转载自blog.csdn.net/qq_39396275/article/details/78698781