即使在最小化应用程序并在浏览片段时重新启动后,声音也会继续播放 [英] Sounds keep playing even after minimizing the app and restarts when navigating through fragments
问题描述
在我的天气应用程序上成功实现声音后,搜索城市时播放效果非常好.但问题是,即使我最小化应用程序,它仍然一直重复播放声音,直到我关闭/退出应用程序.我愿意当我最小化应用程序时暂停,然后从它停止的地方继续播放回来的那一刻.
After successfully implementing sounds on my weather app, it plays quite OK when a city is searched. But the problem is that even when I minimize the app, it still keeps playing the sounds on repeat until I close/exit the app. I would like it to pause when I minimize the app, then continue playing from exactly where it stopped the moment I enter back.
所以我尝试添加此代码:
So I tried adding this code:
@Override
public void onStop() {
super.onStop();
mMediaPlayer.pause();
}
@Override
public void onDestroy() {
super.onDestroy();
mMediaPlayer.pause();
}
它只是在最小化后完全停止声音,并且在返回时不会恢复.它也是降低了应用搜索城市的速度.
It only entirely stops the sound after minimizing and doesn't resume when entering back. It also slows the app from searching a city.
此外,该应用程序包含我导航的 3 个片段(今天、每小时和每日标签)通过单击底部导航.如果我在第一个选项卡上并且声音是播放然后我切换到第二/第三个标签然后移回第一个标签,声音自动重新启动播放的任何声音.我也想解决这个问题.
Also, the app contains 3 fragments(today, hourly and daily tabs) which I navigate through by clicking on the bottom navs. If I'm on the first tab and the sound is playing then I switch to the 2nd/3rd tab then move back to the first tab, the sound automatically restarts whichever sound it is playing. I would like to resolve the issue also.
这是片段的代码:
public class FirstFragment extends Fragment {
private WeatherDataViewModel viewModel;
private MediaPlayer mMediaPlayer; // Single MediaPlayer object
public FirstFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_first, container, false);
// For displaying weather data
final TextView current_temp = rootView.findViewById(R.id.textView10);
final TextView current_output = rootView.findViewById(R.id.textView11);
final TextView rise_time = rootView.findViewById(R.id.textView25);
final TextView set_time = rootView.findViewById(R.id.textView26);
final TextView temp_out = rootView.findViewById(R.id.textView28);
final TextView Press_out = rootView.findViewById(R.id.textView29);
final TextView Humid_out = rootView.findViewById(R.id.textView30);
final TextView Ws_out = rootView.findViewById(R.id.textView33);
final TextView Visi_out = rootView.findViewById(R.id.textView34);
final TextView Cloud_out = rootView.findViewById(R.id.textView35);
final ImageView current_icon = rootView.findViewById(R.id.imageView6);
final SwipeRefreshLayout realSwipe = rootView.findViewById(R.id.real_swipe);
// Get our ViewModel instance
viewModel = new ViewModelProvider(this).get(WeatherDataViewModel.class);
// And whenever the data changes, refresh the UI
viewModel.getWeatherDataLiveData().observe(getViewLifecycleOwner(), data -> {
realSwipe.setOnRefreshListener(() -> {
// perform you action here for ex. add refresh screen code here
new Handler().postDelayed(() -> {
// this code is for stop refreshing icon, After 1000 ms automatically refresh icon will stop
realSwipe.setRefreshing(false);
}, 1000);
});
int drawableResource = -1; // here define default icon for example R.drawable.default_weather_icon
int soundResource = -1; // Default sound is nothing
if (data != null) {
current_temp.setVisibility(View.VISIBLE);
current_temp.setText(data.getMain().getTemp() + " ℃"); // for that you can use strings resource and templates more in https://developer.android.com/guide/topics/resources/string-resource.html#formatting-strings
current_output.setVisibility(View.VISIBLE);
current_output.setText(data.getWeather().get(0).getDescription());
rise_time.setVisibility(View.VISIBLE);
rise_time.setText(data.getSys().getSunrise() + " ");
set_time.setVisibility(View.VISIBLE);
set_time.setText(data.getSys().getSunset() + " ");
temp_out.setVisibility(View.VISIBLE);
temp_out.setText(data.getMain().getTemp() + " ℃");
Press_out.setVisibility(View.VISIBLE);
Press_out.setText(data.getMain().getPressure() + " hpa");
Humid_out.setVisibility(View.VISIBLE);
Humid_out.setText(data.getMain().getHumidity() + " %");
Ws_out.setVisibility(View.VISIBLE);
Ws_out.setText(data.getWind().getSpeed() + " Km/h");
Visi_out.setVisibility(View.VISIBLE);
Visi_out.setText(data.getVisibility() + " m");
Cloud_out.setVisibility(View.VISIBLE);
Cloud_out.setText(data.getClouds().getAll() + " %");
// get actual weather.
String icon = data.getWeather().get(0).getIcon();
switch (icon) {
case "01d":
case "01n":
drawableResource = R.drawable.sun;
soundResource = R.raw.clear_sky_sound;
break;
case "02d":
case "021n":
drawableResource = R.drawable.few_clouds;
soundResource = R.raw.clouds_sound;
break;
case "03d":
case "03n":
drawableResource = R.drawable.scattered_clouds;
soundResource = R.raw.clouds_sound;
break;
case "04d":
case "04n":
drawableResource = R.drawable.broken_clouds;
soundResource = R.raw.clouds_sound;
break;
case "09d":
case "09n":
drawableResource = R.drawable.shower_rain;
soundResource = R.raw.shower_rain_sound;
break;
case "10d":
case "10n":
drawableResource = R.drawable.small_rain;
soundResource = R.raw.shower_rain_sound;
break;
case "11d":
case "11n":
drawableResource = R.drawable.thunderstorm;
soundResource = R.raw.thunderstorm_sound;
break;
case "13d":
case "13n":
drawableResource = R.drawable.snow;
soundResource = R.raw.snow_sound;
break;
case "50d":
case "50n":
drawableResource = R.drawable.mist;
soundResource = R.raw.mist_sound;
break;
}
if (drawableResource != -1)
current_icon.setImageResource(drawableResource);
if (soundResource != -1) {
if (mMediaPlayer != null) {
// stop the playing
if (mMediaPlayer.isPlaying()) {
mMediaPlayer.stop();
}
// release mMediaPlayer resoruces
mMediaPlayer.release();
mMediaPlayer = null;
}
// Play the new resource
prepareMediaPlayer(soundResource);
}
} else {
Log.e("TAG", "No City found");
current_temp.setVisibility(View.GONE);
current_output.setVisibility(View.GONE);
rise_time.setVisibility(View.GONE);
set_time.setVisibility(View.GONE);
temp_out.setVisibility(View.GONE);
Press_out.setVisibility(View.GONE);
Humid_out.setVisibility(View.GONE);
Ws_out.setVisibility(View.GONE);
Visi_out.setVisibility(View.GONE);
Cloud_out.setVisibility(View.GONE);
Toast.makeText(requireActivity(), "No City found", Toast.LENGTH_SHORT).show();
}
});
return rootView;
}
public void getWeatherData(String name) {
// The ViewModel controls loading the data, so we just
// tell it what the new name is - this kicks off loading
// the data, which will automatically call through to
// our observe() call when the data load completes
viewModel.setCityName(name);
}
private void prepareMediaPlayer(int resource) {
// add track file
mMediaPlayer = MediaPlayer.create(requireActivity(), resource);
// listening to when the media file finishes playing so that we can release the resources
mMediaPlayer.setLooping(true);
mMediaPlayer.start();
}
}
天气数据视图模型:
public class WeatherDataViewModel extends ViewModel {
// This will save the city name
private SavedStateHandle state;
// This is where we'll store our result from the server
private MutableLiveData<Example> mutableWeatherData = new MutableLiveData<>();
public WeatherDataViewModel(SavedStateHandle savedStateHandle) {
state = savedStateHandle;
String savedCityName = state.get("name");
if (savedCityName != null) {
// We already had a previously saved name, so we'll
// start loading right away
loadData();
}
}
// This is what our Fragment will use to get the latest weather data
public LiveData<Example> getWeatherDataLiveData() {
return mutableWeatherData;
}
// When you get a new city name, we'll save that in our
// state, then load the new data from the server
public void setCityName(String name) {
state.set("name", name);
loadData();
}
private void loadData() {
// Get the last name that was set
String name = state.get("name");
// Now kick off a load from the server
ApiInterface apiInterface = ApiClient.getClient().create(ApiInterface.class);
Call<Example> call = apiInterface.getWeatherData(name);
call.enqueue(new Callback<Example>() {
@Override
public void onResponse(@NonNull Call<Example> call, @NonNull Response<Example> response) {
// Save the response we've gotten
// This will automatically update our UI
mutableWeatherData.setValue(response.body());
}
@Override
public void onFailure(@NotNull Call<Example> call, @NotNull Throwable t) {
t.printStackTrace();
}
});
}
}
推荐答案
当我最小化应用程序时,它仍然重复播放声音直到我关闭/退出应用程序.我希望它在最小化时暂停应用程序,然后从它停止的地方继续播放我回车的那一刻.
when I minimize the app, it still keeps playing the sounds on repeat until I close/exit the app. I would like it to pause when I minimize the app, then continue playing from exactly where it stopped the moment I enter back.
当片段未显示在屏幕上时,即在 onPause()
回调中,您可以暂停 mediPlayer,并在 onResume()
中恢复它.
You can pause the mediPlayer when the fragment is not shown on the screen i.e in onPause()
callback, and resume it in onResume()
.
但是要小心,因为 mediaPlayer
不会在 onCreate()
中立即启动,因为它会等到从天气 API 获取数据;所以你需要做空能力检查:
But be careful as the mediaPlayer
doesn't start immediately in onCreate()
because it waits until the data is grabbed from the weather API; so you need to do null-ability check:
public void onResume() {
super.onResume();
if (mMediaPlayer != null)
mMediaPlayer.start();
}
@Override
public void onPause() {
super.onPause();
if (mMediaPlayer != null)
mMediaPlayer.pause();
}
附注:为了避免在进行片段事务时潜在的内存/电池泄漏,如果应用程序被破坏,应该释放 mediaPlayer
资源:
Side note: In order to avoid potential memory/battery leaks whenever you do fragment transaction, The mediaPlayer
resource should be released if the application is destroyed:
@Override
public void onDestroy() {
super.onDestroy();
// release mMediaPlayer resoruces
mMediaPlayer.release();
mMediaPlayer = null;
}
但我建议将 mMediaPlayer
对象添加到 ViewModel
中,以便在手机方向等配置更改期间保存其状态.
But I would suggest to add the mMediaPlayer
object into the ViewModel
, in order to save its state during configuration changes like phone orientation.
这篇关于即使在最小化应用程序并在浏览片段时重新启动后,声音也会继续播放的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!