removeCallbacksAndMessages(null)的含义

本文介绍了在Activity的onDestroy()方法中如何正确地释放资源,特别是如何通过清空Callbacks和Messages来避免内存泄露的问题。

一般在在onDestroy()方法中,使用了这个的代码:

如果参数为null的话,会将所有的Callbacks和Messages全部清除掉。

这样做的好处是在Acticity退出的时候,可以避免内存泄露。

public class PressureAltitudeQueryUtils { private static final String TAG = PressureAltitudeQueryUtils.class.getName(); private static volatile PressureAltitudeQueryUtils instance; private final SensorManager sensorManager; private final Handler handler = new Handler(Looper.getMainLooper()); private static final long TIMEOUT_MS = 5000; private volatile Double altitude = Double.NaN; public static PressureAltitudeQueryUtils getInstance() { if (instance == null) { synchronized (PressureAltitudeQueryUtils.class) { if (instance == null) { instance = new PressureAltitudeQueryUtils(); } } } return instance; } private PressureAltitudeQueryUtils() { this.sensorManager = (SensorManager) ContextUtil.getAppContext().getSystemService(Context.SENSOR_SERVICE); } public void queryPressureOnce() { if (sensorManager == null) { Log.d(TAG, "sensorManager is null"); return; } Sensor pressureSensor = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE); if (pressureSensor == null) { // LogUtil.debug(TAG, "pressureSensor is null"); return; } SensorEventListener listener = new SensorEventListener() { @Override public void onSensorChanged(SensorEvent event) { float pressure = event.values[0]; double res = SensorManager.getAltitude( SensorManager.PRESSURE_STANDARD_ATMOSPHERE, pressure ); Log.d(TAG, "Pressure Altitude: " + res + " m"); handler.post(()-> { PressureAltitudeQueryUtils.this.altitude = res; cleanup(this); }); } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } }; sensorManager.registerListener(listener, pressureSensor, SensorManager.SENSOR_DELAY_FASTEST); handler.postDelayed(() -> { cleanup(listener); Log.d(TAG, "SensorEventListener timeout"); }, TIMEOUT_MS); } public Double getLastAltitude() { return altitude; } private void cleanup(SensorEventListener listener) { if(listener!=null){ try { sensorManager.unregisterListener(listener); } catch (Exception e) { Log.w(TAG, "sensorManager unregisterListener exception, " + e.getMessage()); } } handler.removeCallbacksAndMessages(null); } } 这段代码有什么问题吗
09-06
package com.weishitechsub.quanminchangKmianfei.fragment.home; import android.Manifest; import android.annotation.SuppressLint; import android.app.AlertDialog; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.net.Uri; import android.os.AsyncTask; import android.os.Environment; import android.os.Handler; import android.os.Looper; import android.util.Log; import android.view.View; import android.widget.ImageView; import android.widget.SeekBar; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import com.hfd.common.base.BaseFragment; import com.hfd.common.net.HttpBuiler; import com.hfd.common.net.JsonGenericsSerializator; import com.hfd.common.net.Url; import com.hfd.common.util.DensityUtil; import com.hfd.common.util.ToastUtil; import com.hw.lrcviewlib.LrcRow; import com.hw.lrcviewlib.LrcView; import com.weishitechsub.quanminchangKmianfei.bean.MusicBean; import com.weishitechsub.quanminchangKmianfei.dialog.PermissionDialog; import com.weishitechsub.quanminchangKmianfei.utils.MusicCache; import com.weishitechsub.quanminchangKmianfei.utils.MusicPlayerManager; import com.weishitechsub.quanminchangKmianfei.utils.PermissionUtils; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import pub.devrel.easypermissions.EasyPermissions; import com.hfd.common.net.GenericsCallback; import com.weishitechsub.quanminchangKmianfei.R; import com.weishitechsub.quanminchangKmianfei.utils.OnMultiClickListener; import okhttp3.Call; /** * 高端语音导航首页 */ public class HomeFragment extends BaseFragment implements EasyPermissions.PermissionCallbacks{ ImageView iv_song,iv_xh,iv_sys,iv_bf,iv_xys,iv_xz; TextView tv_start_time,tv_end_time,tv_no_lyric; LrcView lyricView; SeekBar seekbar; private List<MusicBean.DataBean> restList = new ArrayList<>(); private List<MusicBean.DataBean> musicList; // 存储获取到的歌曲列表 private static final String[] PERMS_CAMERA = new String[] {Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE}; // 需要请求的权限数组 private static final int RC_CAMERA_PERM = 123; // 请求码,唯一标识 private static final int REQUEST_CODE_WRITE_SETTINGS = 101; private AlertDialog progressDialog; private Handler handler; private Runnable updateProgressRunnable; @Override protected int setLayout() { return R.layout.fragment_home; } @Override protected void initView() { iv_song = fvbi(R.id.iv_song); lyricView = fvbi(R.id.lyricView); tv_start_time = fvbi(R.id.tv_start_time); seekbar = fvbi(R.id.seekbar); tv_end_time = fvbi(R.id.tv_end_time); iv_xh = fvbi(R.id.iv_xh); iv_sys = fvbi(R.id.iv_sys); iv_bf = fvbi(R.id.iv_bf); iv_xys = fvbi(R.id.iv_xys); iv_xz = fvbi(R.id.iv_xz); tv_no_lyric = fvbi(R.id.tv_no_lyric); // 设置最大值为100 seekbar.setMax(100); initLyricStyle(); } @Override protected void initClick() { iv_xz.setOnClickListener(new OnMultiClickListener() { @Override public void onMultiClick(View v) { getSavePermissions(); } }); iv_sys.setOnClickListener(v -> playPrevious()); // 上一首 iv_xys.setOnClickListener(v -> playNext()); // 下一首 iv_bf.setOnClickListener(v -> togglePlayPause()); // 播放/暂停 seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (fromUser) { int duration = MusicPlayerManager.getInstance().getDuration(); int newPosition = (int) ((progress / 100.0) * duration); MusicPlayerManager.getInstance().seekTo(newPosition); } } @Override public void onStartTrackingTouch(SeekBar seekBar) {} @Override public void onStopTrackingTouch(SeekBar seekBar) {} }); } @Override protected void initData() { musicAppInfo(); } private void musicAppInfo() { HashMap<String, String> map = new HashMap<>(); showDialog(); HttpBuiler.getInfo(Url.music, map).build().execute( new GenericsCallback<MusicBean>(new JsonGenericsSerializator()) { @Override public void onError(Call call, Exception e, int id) { dissmiss(); ToastUtil.showShortToast(e.getMessage()); } @Override public void onResponse(MusicBean response, int id) { dissmiss(); if ("0".equals(response.getCode()) && response.getData() != null && !response.getData().isEmpty()) { restList = response.getData(); // 获取第三条之后的数据(即从索引2开始到末尾) musicList = new ArrayList<>(restList.subList(3, restList.size())); MusicCache.setMainMusicList(musicList); // 缓存主列表 startPlaybackIfNeeded(); } else { ToastUtil.showShortToast("暂无歌曲数据"); } } }); } /** * 设置歌词样式 */ private void initLyricStyle(){ // 应用样式 lyricView.getLrcSetting() .setTimeTextSize(40) .setSelectLineColor(Color.parseColor("#FF000000")) .setSelectLineTextSize(25) .setNormalRowColor(Color.parseColor("#919191")) .setHeightRowColor(Color.parseColor("#FF000000")) .setNormalRowTextSize(DensityUtil.sp2px(getContext(), 17)) .setHeightLightRowTextSize(DensityUtil.sp2px(getContext(), 17)) .setTrySelectRowTextSize(DensityUtil.sp2px(getContext(), 17)) .setTimeTextColor(Color.parseColor("#FF000000")) .setTrySelectRowColor(Color.parseColor("#FF000000")); lyricView.commitLrcSettings(); } /** * 如果还没有播放,则自动播放第一首 */ private void startPlaybackIfNeeded() { if (musicList == null || musicList.isEmpty()) return; // 默认从第0首开始播放(如果还没播放过) if (MusicPlayerManager.getInstance().getCurrentPlayingPosition() == -1) { playMusicByPosition(0); } else { updateUIWithCurrentSong(); // 否则刷新当前 UI } setupUpdateProgressWithHandler(); // 开始定时更新进度 } /** * 播放指定位置的歌曲 */ private void playMusicByPosition(int position) { if (musicList == null || position < 0 || position >= musicList.size()) return; MusicBean.DataBean song = musicList.get(position); String url = song.getMusic(); // 假设 getMusic() 返回音频 URL MusicPlayerManager.getInstance().play(url, position); } /** * 上一首 */ private void playPrevious() { if (musicList == null || musicList.isEmpty()) return; int pos = MusicPlayerManager.getInstance().getCurrentPlayingPosition(); int prevPos = (pos - 1 + musicList.size()) % musicList.size(); // 循环上一首 playMusicByPosition(prevPos); } /** * 下一首 */ private void playNext() { if (musicList == null || musicList.isEmpty()) return; int pos = MusicPlayerManager.getInstance().getCurrentPlayingPosition(); int nextPos = (pos + 1) % musicList.size(); // 循环下一首 playMusicByPosition(nextPos); } /** * 播放/暂停切换 */ private void togglePlayPause() { if (MusicPlayerManager.getInstance().isPlaying()) { MusicPlayerManager.getInstance().pause(); } else { MusicPlayerManager.getInstance().resume(); } } /** * 更新 UI 显示当前歌曲信息(包括歌词、标题等) */ @SuppressLint("StaticFieldLeak") private void updateUIWithCurrentSong() { MusicBean.DataBean currentSong = MusicPlayerManager.getInstance().getCurrentPlayingSong(); if (currentSong == null) return; String lrcText = currentSong.getLrc(); // List<LrcRow> lrcRows = new LrcDataBuilder().builtFromText(lrcText); new AsyncTask<String, Void, List<LrcRow>>() { @Override protected List<LrcRow> doInBackground(String... params) { return new LrcDataBuilder().builtFromText(params[0]); } @Override protected void onPostExecute(List<LrcRow> rows) { if (rows != null && !rows.isEmpty()) { lyricView.setLrcData(rows); tv_no_lyric.setVisibility(View.GONE); } else { lyricView.setLrcData(null); tv_no_lyric.setVisibility(View.VISIBLE); } } }.execute(lrcText); // 更新时间(模拟或通过 MediaPlayer 获取) int duration = MusicPlayerManager.getInstance().getDuration(); tv_end_time.setText(formatTime(duration)); } /** * 定时更新播放进度条和歌词 */ private void setupUpdateProgressWithHandler() { if (handler == null) { handler = new Handler(Looper.getMainLooper()); } // 定义循环任务 updateProgressRunnable = new Runnable() { @Override public void run() { int currentPosition = MusicPlayerManager.getInstance().getCurrentPosition(); int duration = MusicPlayerManager.getInstance().getDuration(); if (duration > 0) { // 更新进度条 int progress = (int) (((float) currentPosition / duration) * 100); seekbar.setProgress(progress); // 更新时间文本 tv_start_time.setText(formatTime(currentPosition)); //通知 LrcView 当前播放时间(毫秒) lyricView.seekLrcToTime(currentPosition); } // 自动重复执行(每隔 500ms) handler.postDelayed(this, 500); } }; // 开始执行 handler.post(updateProgressRunnable); } /** * 格式化毫秒时间为 mm:ss */ private String formatTime(int timeMs) { int totalSeconds = timeMs / 1000; int minutes = totalSeconds / 60; int seconds = totalSeconds % 60; return String.format("%02d:%02d", minutes, seconds); } /** * 绑定播放器状态监听器 */ @Override public void onResume() { super.onResume(); MusicPlayerManager.getInstance().setOnPlaybackStateChangedListener(playbackListener); if (musicList != null) { updateUIWithCurrentSong(); setupUpdateProgressWithHandler(); } } @Override public void onPause() { super.onPause(); MusicPlayerManager.getInstance().setOnPlaybackStateChangedListener(null); // 停止 Handler 循环 if (handler != null && updateProgressRunnable != null) { handler.removeCallbacks(updateProgressRunnable); } } @Override public void onDestroy() { super.onDestroy(); MusicPlayerManager.getInstance().release(); // 防止内存泄漏 if (handler != null) { handler.removeCallbacksAndMessages(null); // 清空所有消息 handler = null; } } // 播放状态监听器 private final MusicPlayerManager.OnPlaybackStateChangedListener playbackListener = new MusicPlayerManager.OnPlaybackStateChangedListener() { private int consecutiveErrorCount = 0; // 连续错误计数 private static final int MAX_CONSECUTIVE_ERRORS = 3; @Override public void onPlaying(int position) { consecutiveErrorCount = 0; // 成功播放则清零 updatePlayButtonState(true); updateUIWithCurrentSong(); } @Override public void onPaused() { updatePlayButtonState(false); } @Override public void onCompletion() { playNext(); // 正常播放完成才切下一首 } @Override public void onError(String errorMsg) { consecutiveErrorCount++; Log.e("MusicPlayer", "播放错误 [" + consecutiveErrorCount + "]: " + errorMsg); // 显示一次错误提示即可 if (consecutiveErrorCount == 1) { ToastUtil.showShortToast("播放失败,跳过该歌曲"); } // 防止无限循环:最多连续失败3次就停止 if (consecutiveErrorCount >= MAX_CONSECUTIVE_ERRORS) { ToastUtil.showShortToast("连续播放失败,已暂停"); MusicPlayerManager.getInstance().pause(); return; } // 尝试播放下一首 playNext(); } }; /** * 更新播放按钮图标(播放/暂停) */ private void updatePlayButtonState(boolean isPlaying) { if (isPlaying) { iv_bf.setImageResource(R.mipmap.img_bf); // 替换为你自己的暂停图标 } else { iv_bf.setImageResource(R.mipmap.img_bf); // 替换为你自己的播放图标 } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this); } @Override public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) { if(requestCode == RC_CAMERA_PERM){ saveCurrentSong(); } } @Override public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) { } // 内部歌词解析器 public static class LrcDataBuilder { // 加上 static public List<LrcRow> builtFromText(String lrcText) { List<LrcRow> rows = new ArrayList<>(); if (lrcText == null || lrcText.trim().isEmpty()) { return rows; } String[] lines = lrcText.split("\\r?\\n"); for (String line : lines) { line = line.trim(); if (line.isEmpty()) continue; List<LrcRow> parsed = parseLrcLine(line); if (!parsed.isEmpty()) { rows.addAll(parsed); } } Collections.sort(rows); return rows; } private List<LrcRow> parseLrcLine(String line) { List<LrcRow> result = new ArrayList<>(); if (line.isEmpty()) return result; // 支持多种换行符预处理(避免后续污染) line = line.replaceAll("[\\r\\n\\u2028\\u2029]+", " "); Pattern pattern = Pattern.compile("\\[(\\d{1,2}):(\\d{2})(?:\\.(\\d{2,3}))?\\]"); Matcher matcher = pattern.matcher(line); while (matcher.find()) { int min = Integer.parseInt(matcher.group(1)); int sec = Integer.parseInt(matcher.group(2)); int milli = 0; if (matcher.group(3) != null) { milli = Integer.parseInt(matcher.group(3)); if (matcher.group(3).length() == 2) milli *= 10; // 补齐毫秒 } long timeMillis = (min * 60L + sec) * 1000 + milli; int textStart = matcher.end(); int textEnd = line.length(); // 查找下一个时间标签作为结束点 Matcher nextMatcher = pattern.matcher(line); if (nextMatcher.find(matcher.start() + 1)) { textEnd = nextMatcher.start(); } String rawText = line.substring(textStart, textEnd).trim(); // 再次防御性清理各种空白与特殊字符 String text = rawText .replaceAll("[\\p{Cc}\\p{Cf}&&[^\\t]]", " ") // 移除控制字符 .replace((char) 0xA0, ' ') // 不间断空格 .replaceAll("\\s+", " ") // 多空格合并 .trim(); if (!text.isEmpty()) { result.add(new LrcRow(text, matcher.group(0), timeMillis)); } } return result; } } private void getSavePermissions() { if (!PermissionUtils.hasStoragePermissions(getActivity())) { new PermissionDialog(getActivity(), "权限说明:由于下载歌曲,需要相应的存储权限。拒绝将无法使用此功能").show(); PermissionUtils.requestStoragePermissions(getActivity(), RC_CAMERA_PERM); } else { saveCurrentSong(); // 改为保存当前歌曲 } } private void saveCurrentSong() { int pos = MusicPlayerManager.getInstance().getCurrentPlayingPosition(); if (pos == -1) { ToastUtil.showShortToast("没有正在播放的歌曲"); return; } MusicBean.DataBean song = musicList.get(pos); if (song == null || song.getMusic() == null) { ToastUtil.showShortToast("歌曲信息不完整"); return; } showProgressDialog(getActivity()); new SaveTask(song).execute(); // 传入整个 song 对象更安全 } private void showProgressDialog(Context context) { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setMessage("正在保存..."); builder.setCancelable(false); progressDialog = builder.create(); progressDialog.show(); } private class SaveTask extends AsyncTask<Void, Void, Boolean> { private final MusicBean.DataBean song; private String targetPath; private Exception exception; SaveTask(MusicBean.DataBean song) { this.song = song; } @Override protected Boolean doInBackground(Void... voids) { try { String urlStr = song.getMusic(); String fileName = song.getTitle(); if (fileName == null) fileName = "music_" + System.currentTimeMillis(); File dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); if (!dir.exists()) dir.mkdirs(); targetPath = new File(dir, fileName + ".mp3").getAbsolutePath(); InputStream inputStream = new java.net.URL(urlStr).openStream(); FileOutputStream outStream = new FileOutputStream(targetPath); byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { outStream.write(buffer, 0, bytesRead); } outStream.flush(); outStream.close(); inputStream.close(); // 通知媒体库刷新 Intent scanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); scanIntent.setData(Uri.fromFile(new File(targetPath))); getActivity().sendBroadcast(scanIntent); return true; } catch (Exception e) { e.printStackTrace(); exception = e; return false; } } @Override protected void onPostExecute(Boolean success) { if (progressDialog != null && progressDialog.isShowing()) { progressDialog.dismiss(); } if (success) { Toast.makeText(getActivity(), "音乐已保存至: " + targetPath, Toast.LENGTH_LONG).show(); } else { Toast.makeText(getActivity(), "保存失败: " + (exception != null ? exception.getMessage() : "未知错误"), Toast.LENGTH_SHORT).show(); } } } }package com.weishitechsub.quanminchangKmianfei.utils; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.MediaPlayer; import android.os.Build; import android.os.Handler; import android.os.Looper; import android.util.Log; import androidx.annotation.NonNull; import com.weishitechsub.quanminchangKmianfei.bean.MusicBean; import java.util.List; public class MusicPlayerManager { private static volatile MusicPlayerManager instance; // 双检锁 private MediaPlayer mediaPlayer; private OnPlaybackStateChangedListener listener; private int currentPlayingPosition = -1; // 当前主列表播放位置 private String currentPlayingUrl = null; // 私有构造 private MusicPlayerManager() { // MediaPlayer 初始化延迟到首次使用 } // 获取单例 public static MusicPlayerManager getInstance() { if (instance == null) { synchronized (MusicPlayerManager.class) { if (instance == null) { instance = new MusicPlayerManager(); } } } return instance; } // 播放状态监听器 public interface OnPlaybackStateChangedListener { void onPlaying(int position); void onPaused(); void onCompletion(); void onError(String errorMsg); } public void setOnPlaybackStateChangedListener(OnPlaybackStateChangedListener listener) { this.listener = listener; } /** * 播放主列表中的歌曲 */ public void play(@NonNull String musicUrl, int position) { // 如果正在播放同一首歌,直接返回 if (position == currentPlayingPosition && mediaPlayer != null && mediaPlayer.isPlaying() && musicUrl.equals(currentPlayingUrl)) { return; } // 如果切换了歌曲或 URL 不同,则需要重置播放器状态 if (!musicUrl.equals(currentPlayingUrl) || mediaPlayer == null) { try { if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.stop(); } } catch (Exception e) { // 忽略停止异常 } prepareAndPlay(musicUrl, () -> { currentPlayingPosition = position; currentPlayingUrl = musicUrl; if (listener != null) listener.onPlaying(position); }); } else { // 同一 URL,但未播放 → 直接 start try { int currentPosition = mediaPlayer.getCurrentPosition(); int duration = mediaPlayer.getDuration(); if (duration <= 0 || currentPosition < duration - 100) { mediaPlayer.start(); if (listener != null) listener.onPlaying(position); } else { // 已结束,重新 prepare mediaPlayer.seekTo(0); mediaPlayer.start(); if (listener != null) listener.onPlaying(position); } } catch (Exception e) { // 出错则重新准备 prepareAndPlay(musicUrl, () -> { currentPlayingPosition = position; currentPlayingUrl = musicUrl; if (listener != null) listener.onPlaying(position); }); } } } /** * 内部方法:准备并播放音乐 */ private void prepareAndPlay(String musicUrl, Runnable onPreparedAction) { try { boolean needNewPlayer = (mediaPlayer == null); if (needNewPlayer) { mediaPlayer = new MediaPlayer(); setupListeners(); } else { // 使用 try-catch 防止 reset 期间异常,并确保状态复位成功 try { mediaPlayer.reset(); } catch (IllegalStateException e) { // 某些设备可能在此处抛出异常 Log.e("MusicPlayer", "reset failed, recreating MediaPlayer", e); mediaPlayer.release(); mediaPlayer = new MediaPlayer(); setupListeners(); } } // 设置音频属性 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mediaPlayer.setAudioAttributes(new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setUsage(AudioAttributes.USAGE_MEDIA) .build()); } else { mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); } // 添加准备前的状态检查:使用 post 延迟执行,确保 reset 完成 final String url = musicUrl; Handler handler = new Handler(Looper.getMainLooper()); handler.postDelayed(() -> { try { mediaPlayer.setDataSource(url); mediaPlayer.prepareAsync(); mediaPlayer.setOnPreparedListener(mp -> { mp.start(); onPreparedAction.run(); }); } catch (Exception e) { e.printStackTrace(); if (listener != null) { listener.onError("无法设置数据源: " + e.getMessage()); } } }, 50); // 给 reset 留出一点时间(50ms 足够) } catch (Exception e) { e.printStackTrace(); if (listener != null) { listener.onError("初始化失败: " + e.getMessage()); } } } /** * 设置 MediaPlayer 回调监听 */ private void setupListeners() { mediaPlayer.setOnCompletionListener(mp -> { if (listener != null) { listener.onCompletion(); } }); mediaPlayer.setOnErrorListener((mp, what, extra) -> { if (listener != null) { listener.onError("播放错误: code=" + what + ", extra=" + extra); } return true; // 消费错误,不进入默认行为 }); } /** * 暂停播放 */ public void pause() { if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.pause(); if (listener != null) { listener.onPaused(); } } } /** * 继续播放 */ public void resume() { if (mediaPlayer != null && !mediaPlayer.isPlaying() && currentPlayingUrl != null) { mediaPlayer.start(); if (listener != null) { listener.onPlaying(currentPlayingPosition); } } } /** * 是否正在播放 */ public boolean isPlaying() { return mediaPlayer != null && mediaPlayer.isPlaying(); } /** * 获取当前播放的位置(主列表索引) */ public int getCurrentPlayingPosition() { return currentPlayingPosition; } /** * 获取当前播放的音乐 URL */ public String getCurrentPlayingMusicUrl() { return currentPlayingUrl; } /** * 获取当前播放的歌曲信息(从主列表中查找) */ public MusicBean.DataBean getCurrentPlayingSong() { List<MusicBean.DataBean> mainList = MusicCache.getMainMusicList(); if (mainList != null && currentPlayingPosition >= 0 && currentPlayingPosition < mainList.size()) { MusicBean.DataBean song = mainList.get(currentPlayingPosition); if (song != null && song.getMusic().equals(currentPlayingUrl)) { return song; } } // 回退:根据 URL 查找 if (mainList != null && currentPlayingUrl != null) { for (int i = 0; i < mainList.size(); i++) { MusicBean.DataBean song = mainList.get(i); if (song != null && song.getMusic().equals(currentPlayingUrl)) { currentPlayingPosition = i; // 自动修正索引 return song; } } } return null; } /** * 获取当前播放列表(即主列表) */ public List<MusicBean.DataBean> getCurrentPlayingList() { return MusicCache.getMainMusicList(); } /** * 跳转到指定时间 */ public void seekTo(int milliseconds) { if (mediaPlayer != null && mediaPlayer.isPlaying()) { try { mediaPlayer.seekTo(milliseconds); } catch (IllegalStateException e) { // 当前未初始化或未准备好 Log.e("MusicPlayer", "seekTo failed: not prepared", e); } } } /** * 获取当前播放进度(毫秒) */ public int getCurrentPosition() { if (mediaPlayer != null) { try { return mediaPlayer.getCurrentPosition(); } catch (IllegalStateException e) { return 0; } } return 0; } /** * 获取音频总时长(毫秒) */ public int getDuration() { if (mediaPlayer != null) { try { return mediaPlayer.getDuration(); } catch (IllegalStateException e) { return 0; } } return 0; } /** * 释放 MediaPlayer 资源 */ public void release() { if (mediaPlayer != null) { try { if (mediaPlayer.isPlaying()) { mediaPlayer.stop(); } } catch (Exception e) { // 忽略异常 } finally { mediaPlayer.release(); mediaPlayer = null; } } currentPlayingPosition = -1; currentPlayingUrl = null; } } 播放错误: code=-38, extra=0一直弹这个提示框可能是因为请求接口获取的歌曲没有加载完毕就想要播放这个怎么修改
12-10
我的HomeFragment目前代码如下:package com.example.bus.ui.home; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.text.Editable; import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.ArrayAdapter; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import com.example.bus.MainActivity; import com.example.bus.RealTimePoiSuggestHelper; import com.example.bus.SearchResultActivity; import com.example.bus.databinding.FragmentHomeBinding; public class HomeFragment extends Fragment { private FragmentHomeBinding binding; private HomeViewModel homeViewModel; private static final int TIPS_DELAY = 300; // 防抖延迟 private Handler handler = new Handler(Looper.getMainLooper()); private Runnable pendingRequest; @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { binding = FragmentHomeBinding.inflate(inflater, container, false); View root = binding.getRoot(); // 初始化 ViewModel homeViewModel = new ViewModelProvider(this).get(HomeViewModel.class); homeViewModel.getText().observe(getViewLifecycleOwner(), text -> { binding.textHome.setText(text); }); setupSearchSuggestion(); setupSearchAction(); setupSearchBoxWithPermissionControl(); return root; } private void setupSearchBoxWithPermissionControl() { binding.homeInput.setOnClickListener(v -> { MainActivity activity = (MainActivity) requireActivity(); activity.ensureFineLocationPermission(() -> { // 权限已获得,现在才允许 EditText 获取焦点并弹出软键盘 binding.homeInput.post(() -> { binding.homeInput.setFocusableInTouchMode(true); binding.homeInput.requestFocus(); InputMethodManager imm = (InputMethodManager) requireContext() .getSystemService(Context.INPUT_METHOD_SERVICE); if (imm != null) { imm.showSoftInput(binding.homeInput, InputMethodManager.SHOW_IMPLICIT); } }); }); }); } /** * 设置输入建议功能(suggestion) */ private void setupSearchSuggestion() { // 创建建议助手 RealTimePoiSuggestHelper suggestHelper = new RealTimePoiSuggestHelper(requireContext()); suggestHelper.setCurrentCity("全国"); // 可改为动态城市 suggestHelper.setCallback(suggestions -> { if (suggestions.length > 0) { ArrayAdapter<String> adapter = new ArrayAdapter<>( requireContext(), android.R.layout.simple_dropdown_item_1line, suggestions ) { @Override public View getView(int position, View convertView, ViewGroup parent) { View view = super.getView(position, convertView, parent); // 👉 添加点击事件 view.setOnClickListener(v -> { String selectedText = getItem(position); if (selectedText != null && !selectedText.isEmpty()) { binding.homeInput.setText(selectedText); binding.homeInput.clearFocus(); // 隐藏软键盘 InputMethodManager imm = (InputMethodManager) requireContext() .getSystemService(Context.INPUT_METHOD_SERVICE); if (imm != null) { imm.hideSoftInputFromWindow(binding.homeInput.getWindowToken(), 0); } // 直接触发搜索(跳转到 SearchResultActivity) performSearch(); } }); return view; } }; // 主线程更新 UI new Handler(Looper.getMainLooper()).post(() -> { binding.homeInput.setAdapter(adapter); if (requireActivity().getCurrentFocus() == binding.homeInput) { binding.homeInput.showDropDown(); } }); } else { new Handler(Looper.getMainLooper()).post(() -> binding.homeInput.setAdapter(null) ); } }); // 防抖控制 handler = new Handler(Looper.getMainLooper()); pendingRequest = null; binding.homeInput.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void onTextChanged(CharSequence s, int start, int before, int count) { if (pendingRequest != null) { handler.removeCallbacks(pendingRequest); } if (s.length() == 0) { binding.homeInput.setAdapter(null); return; } boolean hasPermission = requireContext().checkSelfPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) == android.content.pm.PackageManager.PERMISSION_GRANTED; if (!hasPermission) { // 没有权限 → 不请求建议,也不显示任何提示 binding.homeInput.setAdapter(null); return; } pendingRequest = () -> suggestHelper.requestSuggestions(s.toString()); handler.postDelayed(pendingRequest, TIPS_DELAY); } @Override public void afterTextChanged(Editable s) {} }); } /** * 设置搜索动作:回车 & 按钮点击 */ private void setupSearchAction() { // 回车搜索 binding.homeInput.setOnEditorActionListener((v, actionId, event) -> { if ((actionId & EditorInfo.IME_MASK_ACTION) == EditorInfo.IME_ACTION_SEARCH) { performSearch(); return true; } return false; }); // 搜索按钮点击 binding.homeSearch.setOnClickListener(v -> performSearch()); } private void performSearch() { String keyword = binding.homeInput.getText().toString().trim(); if (keyword.isEmpty()) { Toast.makeText(requireContext(), "请输入关键词", Toast.LENGTH_SHORT).show(); return; } MainActivity activity = (MainActivity) requireActivity(); activity.ensureFineLocationPermission(() -> { Intent intent = new Intent(requireContext(), SearchResultActivity.class); intent.putExtra("keyword", keyword); startActivity(intent); }); } @Override public void onDestroyView() { super.onDestroyView(); binding = null; // 清理防抖任务 if (handler != null) { handler.removeCallbacksAndMessages(null); } } } 我在想一个问题,第一次点击不弹是不是因为他走在了那个逻辑的前面?
最新发布
12-11
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值