You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

移除回调后复用Android Handler失败求助:Service绑定重启回调无效

修复Handler回调在Service重新绑定时失效的问题

你遇到的问题核心是重新绑定时没有正确重启Handler的回调任务,加上当前的Handler创建逻辑存在冗余,导致回调无法复用。下面我来帮你梳理问题并给出具体的修复方案:

问题根源分析

  1. onRebind()未实现:当Service解绑后重新绑定,如果onUnbind()返回true,系统会触发onRebind()而非onBind(),但你的代码没有重写这个方法,导致重新绑定时没有重启回调。
  2. Handler重复实例化:每次onBind()都新建Handler对象,不仅浪费内存,还会导致旧Handler的回调和新Handler脱节,逻辑混乱。
  3. 回调触发未做状态校验:即使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);
        }
    }
}

关键修改说明

  1. Handler初始化移至onCreate():确保整个Service生命周期只有一个Handler实例,避免重复创建导致的逻辑混乱。
  2. 重写onRebind()方法:在Service重新绑定时主动调用readyTime(),重启时间更新回调。
  3. 增加状态校验:
    • 在curTimeRunnable中只在MediaPlayer准备完成且处于播放状态时,才更新位置并继续提交回调。
    • 在pause()中移除回调,resume()中重启回调,避免暂停时的无效资源消耗。
  4. 优化onUnbind()返回值:返回true以支持重新绑定时触发onRebind(),而非重新调用onBind()。
  5. 播放启动时自动启动回调:在onPrepared()中调用readyTime(),确保播放开始后立即更新位置。

内容的提问来源于stack exchange,提问作者Nasib

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 06:53:05