Android开发:如何将mFilePath从RecordingService传递到RecordingFragment?
如何从RecordingService传递mFilePath到RecordingFragment
首先先整理你提供的代码:
你的RecordingService.java代码
public class RecordingService extends Service { private static final String LOG_TAG = "RecordingService"; private String mFileName = null; private String mFilePath = null; private MediaRecorder mRecorder = null; private DBHelper mDatabase; private long mStartingTimeMillis = 0; private long mElapsedMillis = 0; private int mElapsedSeconds = 0; private OnTimerChangedListener onTimerChangedListener = null; private static final SimpleDateFormat mTimerFormat = new SimpleDateFormat("mm:ss", Locale.getDefault()); private Timer mTimer = null; private TimerTask mIncrementTimerTask = null; @Override public IBinder onBind(Intent intent) { return null; } public interface OnTimerChangedListener { void onTimerChanged(int seconds); } @Override public void onCreate() { super.onCreate(); mDatabase = new DBHelper(getApplicationContext()); } @Override public int onStartCommand(Intent intent, int flags, int startId) { startRecording(); return START_STICKY; } @Override public void onDestroy() { if (mRecorder != null) { stopRecording(); } super.onDestroy(); } public void startRecording() { setFileNameAndPath(); mRecorder = new MediaRecorder(); mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); mRecorder.setOutputFile(mFilePath); mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); mRecorder.setAudioChannels(1); if (MySharedPreferences.getPrefHighQuality(this)) { mRecorder.setAudioSamplingRate(44100); mRecorder.setAudioEncodingBitRate(192000); } try { mRecorder.prepare(); mRecorder.start(); mStartingTimeMillis = System.currentTimeMillis(); //startTimer(); //startForeground(1, createNotification()); } catch (IOException e) { Log.e(LOG_TAG, "prepare() failed"); } } public void setFileNameAndPath(){ int count = 0; File f; do{ count++; mFileName = getString(R.string.default_file_name) + "_" + (mDatabase.getCount() + count) + ".mp4"; mFilePath = Environment.getExternalStorageDirectory().getAbsolutePath(); mFilePath += "/SoundRecorder/" + mFileName; f = new File(mFilePath); }while (f.exists() && !f.isDirectory()); } public String getFileNameAndPath(){ return mFilePath; } public void stopRecording() { mRecorder.stop(); mElapsedMillis = (System.currentTimeMillis() - mStartingTimeMillis); mRecorder.release(); Toast.makeText(this, getString(R.string.toast_recording_finish) + " " + mFilePath, Toast.LENGTH_LONG).show(); //remove notification if (mIncrementTimerTask != null) { mIncrementTimerTask.cancel(); mIncrementTimerTask = null; } mRecorder = null; try { mDatabase.addRecording(mFileName, mFilePath, mElapsedMillis); Log.v("RecordingService", mFilePath); Bundle bundle = new Bundle(); RecordFragment recordFragment = new RecordFragment(); bundle.putString("filepath", mFilePath); recordFragment.setArguments(bundle); } catch (Exception e){ Log.e(LOG_TAG, "exception", e); } } private void startTimer() { mTimer = new Timer(); mIncrementTimerTask = new TimerTask() { @Override public void run() { mElapsedSeconds++; if (onTimerChangedListener != null) onTimerChangedListener.onTimerChanged(mElapsedSeconds); NotificationManager mgr = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); mgr.notify(1, createNotification()); } }; mTimer.scheduleAtFixedRate(mIncrementTimerTask, 1000, 1000); } //TODO: private Notification createNotification() { NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getApplicationContext()) .setSmallIcon(R.drawable.ic_mic_white_36dp) .setContentTitle(getString(R.string.notification_recording)) .setContentText(mTimerFormat.format(mElapsedSeconds * 1000)) .setOngoing(true); mBuilder.setContentIntent(PendingIntent.getActivities(getApplicationContext(), 0, new Intent[]{new Intent(getApplicationContext(), MainActivity.class)}, 0)); return mBuilder.build(); } }
你的RecordingFragment.java代码
public class RecordFragment extends Fragment { // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER private static final String ARG_POSITION = "position"; private static final String LOG_TAG = RecordFragment.class.getSimpleName(); private int position; //Recording controls private FloatingActionButton mRecordButton = null; // evaluation button private FloatingActionButton mEvalButton = null; private Button mPauseButton = null; private TextView mRecordingPrompt; private int mRecordPromptCount = 0; private boolean mStartRecording = true; private boolean mPauseRecording = true; private Chronometer mChronometer = null; long timeWhenPaused = 0; //stores time when user clicks pause button String filePath; /** * Use this factory method to create a new instance of * this fragment using the provided parameters. * * @return A new instance of fragment Record_Fragment. */ public static
你当前代码的问题
在stopRecording()方法里,你新建了一个RecordFragment实例并设置Bundle参数,但这个新实例和界面上正在显示的Fragment完全不是同一个对象,所以参数根本传不到你想要的那个Fragment里。
下面给你几种可行的解决方案:
方案一:使用LocalBroadcastManager发送广播(简单易实现)
这是组件间通信的常用方式,适合Service向Fragment传递数据。
步骤1:在RecordingService中发送广播
修改stopRecording()方法,替换掉原来创建Fragment的代码:
public void stopRecording() { // ... 其他代码保持不变 ... try { mDatabase.addRecording(mFileName, mFilePath, mElapsedMillis); Log.v("RecordingService", mFilePath); // 发送广播传递mFilePath Intent intent = new Intent("RECORDING_FINISHED_ACTION"); intent.putExtra("filepath", mFilePath); LocalBroadcastManager.getInstance(this).sendBroadcast(intent); } catch (Exception e){ Log.e(LOG_TAG, "exception", e); } }
步骤2:在RecordingFragment中注册广播接收器
在Fragment的生命周期方法中注册/注销接收器:
private BroadcastReceiver mRecordingFinishedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // 获取传递的文件路径 filePath = intent.getStringExtra("filepath"); // 这里可以做你需要的操作,比如更新UI Log.d(LOG_TAG, "Received file path: " + filePath); } }; @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // 注册广播接收器 IntentFilter filter = new IntentFilter("RECORDING_FINISHED_ACTION"); LocalBroadcastManager.getInstance(requireContext()).registerReceiver(mRecordingFinishedReceiver, filter); } @Override public void onDestroyView() { super.onDestroyView(); // 注销接收器,避免内存泄漏 LocalBroadcastManager.getInstance(requireContext()).unregisterReceiver(mRecordingFinishedReceiver); }
方案二:使用ViewModel(推荐,适合Jetpack架构)
如果你的项目使用Jetpack,可以用ViewModel来共享数据,这样Service和Fragment可以访问同一个ViewModel实例。
步骤1:创建共享的ViewModel
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModel; public class RecordingViewModel extends ViewModel { private final MutableLiveData<String> recordingFilePath = new MutableLiveData<>(); public void setRecordingFilePath(String path) { recordingFilePath.setValue(path); } public MutableLiveData<String> getRecordingFilePath() { return recordingFilePath; } }
步骤2:在RecordingService中更新ViewModel
public void stopRecording() { // ... 其他代码保持不变 ... try { mDatabase.addRecording(mFileName, mFilePath, mElapsedMillis); Log.v("RecordingService", mFilePath); // 更新ViewModel中的数据 RecordingViewModel viewModel = new ViewModelProvider( new ViewModelStoreOwner() { @NonNull @Override public ViewModelStore getViewModelStore() { return getApplication().getViewModelStore(); } }, ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication()) ).get(RecordingViewModel.class); viewModel.setRecordingFilePath(mFilePath); } catch (Exception e){ Log.e(LOG_TAG, "exception", e); } }
步骤3:在RecordingFragment中观察ViewModel数据
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); RecordingViewModel viewModel = new ViewModelProvider(requireActivity()).get(RecordingViewModel.class); viewModel.getRecordingFilePath().observe(getViewLifecycleOwner(), newPath -> { filePath = newPath; // 数据更新时执行操作,比如更新UI Log.d(LOG_TAG, "Received file path from ViewModel: " + filePath); }); }
方案三:绑定Service并使用回调接口
如果你的Service是可绑定的,可以通过绑定Service,让Fragment和Service建立连接,然后通过回调传递数据。
步骤1:修改RecordingService为可绑定
public class RecordingService extends Service { // ... 其他成员变量 ... private final IBinder mBinder = new LocalBinder(); private OnRecordingFinishedListener mFinishedListener; // 定义回调接口 public interface OnRecordingFinishedListener { void onRecordingFinished(String filePath); } public class LocalBinder extends Binder { RecordingService getService() { return RecordingService.this; } } @Override public IBinder onBind(Intent intent) { return mBinder; } // 设置回调监听 public void setOnRecordingFinishedListener(OnRecordingFinishedListener listener) { mFinishedListener = listener; } // 修改stopRecording方法,调用回调 public void stopRecording() { // ... 其他代码保持不变 ... try { mDatabase.addRecording(mFileName, mFilePath, mElapsedMillis); Log.v("RecordingService", mFilePath); // 通过回调传递数据 if (mFinishedListener != null) { mFinishedListener.onRecordingFinished(mFilePath); } } catch (Exception e){ Log.e(LOG_TAG, "exception", e); } } // ... 其他方法 ... }
步骤2:在RecordingFragment中绑定Service并设置回调
private RecordingService mRecordingService; private boolean mBound = false; private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName className, IBinder service) { RecordingService.LocalBinder binder = (RecordingService.LocalBinder) service; mRecordingService = binder.getService(); mBound = true; // 设置回调监听 mRecordingService.setOnRecordingFinishedListener(new RecordingService.OnRecordingFinishedListener() { @Override public void onRecordingFinished(String filePath) { RecordFragment.this.filePath = filePath; // 执行操作 Log.d(LOG_TAG, "Received file path via callback: " + filePath); } }); } @Override public void onServiceDisconnected(ComponentName arg0) { mBound = false; } }; @Override public void onStart() { super.onStart(); // 绑定Service Intent intent = new Intent(requireContext(), RecordingService.class); requireContext().bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } @Override public void onStop() { super.onStop(); // 解绑Service if (mBound) { requireContext().unbindService(mConnection); mBound = false; } }
以上三种方案都能解决你的问题,其中**方案一(LocalBroadcast)**最容易快速实现,**方案二(ViewModel)**更符合现代Android架构的最佳实践,推荐在使用Jetpack的项目中使用。
内容的提问来源于stack exchange,提问作者Mohamad Handy Nugraha
相关产品推荐
相关产品推荐

