Handler && Timer(音乐播放器)

来源:互联网 发布:去外企工作好吗 知乎 编辑:程序博客网 时间:2024/06/03 21:14

研究这两个的区别好久了,一个定时器用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