如果我们开发一个需要播放音频的应用,我们都知道播放音频要用到MediaPlayer类,我这里,不需要开启Service,就在本Activity播放音频,当Activity销毁的时候,音频便结束
但是有一个重点,需要即时的变化当前播放的时间
我的思路是,开启一个线程,计算当前音频的剩余播放时间,如果>0 则用Handler循环发送一个消息来更改时间UI
Thread tPlay ; tPlay = new Thread(new Runnable() { @Override public void run() { int time = (mPlay.getDuration()-mPlay.getCurrentPosition())/1000; // 获得当前的音频的剩余时间,总时长-当前播放时长 while(time>0&&mPlay!=null) { // 如果剩余时间>0 并且MediaPlayer对象存在,就需要没0.2秒更新一下时间UI try { Thread.sleep(200); time = (mPlay.getDuration()-mPlay.getCurrentPosition())/1000; //获得当前时间 final int finalTime = time; handler.post(new Runnable() { @Override public void run() { topic_play_time.setText(finalTime /60+"'"+ finalTime %60); //更改UI } }); } catch (InterruptedException e) { e.printStackTrace(); } } } });
然后有线程对象了,什么时候start呢,必然是在第一次播放音频的时候,而且只能start一次。
那么问题来了。当我播放音频的时候,或者暂停已经播放一段的音频的时候,用户可能会退出Activity ,
而Activity销毁了,但是这个Activity开启的计算时间更改UI的子线程还存在,它还需要循环计算剩余时间,但是MediaPlayer对象没了,就报错了。
所以,解决方法只能是销毁Activity之前结束这个Activity开启的子线程。
试过几种方法,最后选定了一个最简单,最易理解的方法:
即
1、设置一个全局标记变量boolean flag = true;
2、线程中while 循环判断 flag 是否为true,是则执行内部代码,否则不执行,结束循环了,即线程也随之结束了
tPlay = new Thread(new Runnable() { @Override public void run() { int time = (mPlay.getDuration()-mPlay.getCurrentPosition())/1000; while(time>0&&mPlay!=null&&flag) { } } });
3、在Activity销毁的
@Override protected void onDestroy() { super.onDestroy(); isflag=false; }
方法中更改flag,那么线程中while条件则不成立,线程就结束了,也不会再报错了。
关键代码:
public class TopicDetailActivity extends Activity implements View.OnClickListener { private ImageButton topic_play_music; private Attention entity; private boolean isflag= true; private MediaPlayer mPlay; private Thread tPlay; private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_topic_detail); Intent intent = getIntent(); Serializable attention = intent.getSerializableExtra("attention"); tPlay = new Thread(new Runnable() { @Override public void run() { int time = (mPlay.getDuration()-mPlay.getCurrentPosition())/1000; while(time>0&&mPlay!=null&&isflag) { try { Thread.sleep(200); time = (mPlay.getDuration()-mPlay.getCurrentPosition())/1000; final int finalTime = time; handler.post(new Runnable() { @Override public void run() { topic_play_time.setText(finalTime /60+"'"+ finalTime %60); } }); } catch (InterruptedException e) { e.printStackTrace(); } } } }); initView(); initData(attention); } /** * 数据展示 * @param attention */ private void initData(Serializable attention) { entity = (Attention) attention; int nowTime = Integer.parseInt(entity.getAudiolength()); topic_play_time.setText(nowTime/60 + "'" + nowTime%60); } /** * 初始化控件 */ private void initView() { topic_play_music = (ImageButton) findViewById(R.id.topic_play_music); topic_play_music.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.topic_play_music: if(mPlay==null){ mPlay = new MediaPlayer(); mPlay.reset(); try { mPlay.setDataSource("http://imagecdn.xunjimap.com/image/" + entity.getAudiourl()); mPlay.prepare(); topic_play_music.setImageResource(R.mipmap.ui_detail_pause); mPlay.start(); tPlay.start(); } catch (IOException e) { e.printStackTrace(); } }else{ if (mPlay.isPlaying()){ mPlay.pause(); topic_play_music.setImageResource(R.mipmap.ui_detail_play); }else{ mPlay.start(); topic_play_music.setImageResource(R.mipmap.ui_detail_pause); } } break; } } @Override protected void onDestroy() { super.onDestroy(); isflag=false; try { tPlay.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } if (mPlay!=null){ mPlay.stop(); mPlay.release(); } } }
Activity中开辟了线程更新UI,线程未结束,退出了Activity,导致问题
当一个Activity退出 时,它的子线程还在运行,这时会出现异常问题:
解决方式,在线程中利用Flag标志位
public void run() { while(Flag) { ...... } }
主线程的OnDestory()方法中,利用下面的代码:
Flag = false; try { ThreadSleep(500); // 等待子线程结束 handler.removeMessage(....); }