Android中Activity销毁而绘制UI的子线程未销毁的解决方法

作者:简简单单 2015-11-12

如果我们开发一个需要播放音频的应用,我们都知道播放音频要用到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(....);
  }


相关文章

精彩推荐