移除回调后复用Android Handler失败求助:Service绑定重启回调无效
修复Handler回调在Service重新绑定时失效的问题
你遇到的问题核心是重新绑定时没有正确重启Handler的回调任务,加上当前的Handler创建逻辑存在冗余,导致回调无法复用。下面我来帮你梳理问题并给出具体的修复方案:
问题根源分析
onRebind()未实现:当Service解绑后重新绑定,如果onUnbind()返回true,系统会触发onRebind()而非onBind(),但你的代码没有重写这个方法,导致重新绑定时没有重启回调。- Handler重复实例化:每次
onBind()都新建Handler对象,不仅浪费内存,还会导致旧Handler的回调和新Handler脱节,逻辑混乱。 - 回调触发未做状态校验:即使MediaPlayer处于暂停状态,Runnable仍会持续提交,既浪费资源也可能导致无效的位置更新。
修复后的完整代码
import android.app.Service; import android.content.Intent; import android.media.MediaPlayer; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.util.Log; import androidx.annotation.Nullable; import androidx.lifecycle.MutableLiveData; import java.io.IOException; public class MediaService extends Service { private static final String TAG = "MediaService"; IBinder myBinder = new MyBinder(); MediaPlayer mediaPlayer = new MediaPlayer(); boolean isPrepared = false; MutableLiveData<Integer> currrentTime = new MutableLiveData<>(); MutableLiveData<Integer> mediaPlayerDuration = new MutableLiveData<>(); private Handler handlerTime; @Override public void onCreate() { super.onCreate(); Log.d(TAG, "onCreate: MediaService Created"); // 只在Service创建时初始化一次Handler,避免重复创建 handlerTime = new Handler(); } @Nullable @Override public IBinder onBind(Intent intent) { Log.d(TAG, "onBind: Service bound"); // 绑定后重启时间更新回调 readyTime(); return myBinder; } @Override public boolean onUnbind(Intent intent) { Log.d(TAG, "onUnbind: Service unbound"); // 移除所有回调,确保任务完全停止 if (handlerTime != null) { handlerTime.removeCallbacks(curTimeRunnable); } // 返回true,支持后续重新绑定时触发onRebind() return true; } @Override public void onRebind(Intent intent) { super.onRebind(intent); Log.d(TAG, "onRebind: Service rebound"); // 重新绑定时重启时间更新回调 readyTime(); } class MyBinder extends Binder { MediaService getService() { return MediaService.this; } } public void setPathAndPlay(String path) { isPrepared = false; mediaPlayer.reset(); try { mediaPlayer.setDataSource(path); mediaPlayer.prepareAsync(); mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { isPrepared = true; mp.start(); mediaPlayerDuration.postValue(mediaPlayer.getDuration()); // 播放开始后自动启动时间更新 readyTime(); } }); } catch (IOException e) { Log.d(TAG, "setPathAndPlay: Error"); e.printStackTrace(); } } public boolean isMediaPlaying() { return this.mediaPlayer.isPlaying(); } public void pause() { mediaPlayer.pause(); // 暂停时暂时移除回调,避免无效更新 if (handlerTime != null) { handlerTime.removeCallbacks(curTimeRunnable); } } public void resume() { try { mediaPlayer.start(); // 恢复播放时重启回调 readyTime(); } catch (Exception e) { Log.e(TAG, "resume: Failed to resume playback", e); } } public void seekTo(int millis) { mediaPlayer.seekTo(millis); // 拖动后立即更新当前位置 currrentTime.postValue(millis); } public int getTimeTotal() { return mediaPlayer.getDuration(); } public int getCurrentPos() { return mediaPlayer.getCurrentPosition(); } Runnable curTimeRunnable = new Runnable() { @Override public void run() { if (isPrepared && isMediaPlaying()) { currrentTime.postValue(mediaPlayer.getCurrentPosition()); Log.d(TAG, "run: VALUE POSTED"); // 只有在播放状态下才继续延迟提交 if (handlerTime != null) { handlerTime.postDelayed(this, 1); } } } }; public void readyTime() { if (handlerTime != null && isPrepared && isMediaPlaying()) { // 先移除可能存在的旧回调,避免重复提交 handlerTime.removeCallbacks(curTimeRunnable); handlerTime.postDelayed(curTimeRunnable, 1); } } }
关键修改说明
- Handler初始化移至
onCreate():确保整个Service生命周期只有一个Handler实例,避免重复创建导致的逻辑混乱。 - 重写
onRebind()方法:在Service重新绑定时主动调用readyTime(),重启时间更新回调。 - 增加状态校验:
- 在
curTimeRunnable中只在MediaPlayer准备完成且处于播放状态时,才更新位置并继续提交回调。 - 在
pause()中移除回调,resume()中重启回调,避免暂停时的无效资源消耗。
- 在
- 优化
onUnbind()返回值:返回true以支持重新绑定时触发onRebind(),而非重新调用onBind()。 - 播放启动时自动启动回调:在
onPrepared()中调用readyTime(),确保播放开始后立即更新位置。
内容的提问来源于stack exchange,提问作者Nasib
相关产品推荐
相关产品推荐

