即使在最小化应用程序并在浏览片段时重新启动后,声音也会继续播放

Sounds keep playing even after minimizing the app and restarts when navigating through fragments

在我的天气应用程序上成功实现声音后,在搜索城市时播放效果很好。但问题是,即使我最小化应用程序,它仍然 一直重复播放声音,直到我 close/exit 应用程序。我希望它 当我最小化应用程序时暂停,然后从停止的地方继续播放 我回来的那一刻。

所以我尝试添加这段代码:

@Override
    public void onStop() {
        super.onStop();
        mMediaPlayer.pause();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mMediaPlayer.pause();
    }

只在最小化后完全停止声音,返回时不恢复。它也是 减慢应用搜索城市的速度。

此外,该应用程序包含我导航的 3 个片段(今天、每小时和每日选项卡) 通过点击底部导航。如果我在第一个选项卡上并且声音是 播放然后我切换到第二/第三选项卡然后移回第一个选项卡,声音 自动重新启动正在播放的任何声音。我也想解决这个问题。

这是片段的代码:

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();
            }
        });
    }
}

您需要使用服务:https://developer.android.com/guide/components/services

看这个简单的例子:https://www.tutorialspoint.com/how-to-play-background-music-in-android-app

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.

您可以在片段未显示在屏幕上时暂停 mediPlayer,即在 onPause() 回调中,并在 onResume() 中恢复它。

但是要小心,因为 mediaPlayer 不会在 onCreate() 中立即开始,因为它会等到从天气 API 中获取数据;所以你需要做 null-ability 检查:

public void onResume() {
    super.onResume();
    if (mMediaPlayer != null)
        mMediaPlayer.start();
}

@Override
public void onPause() {
    super.onPause();
    if (mMediaPlayer != null)
        mMediaPlayer.pause();
}

旁注:为了避免在进行片段事务时潜在的memory/battery泄漏,如果应用程序被销毁,应该释放mediaPlayer资源:

@Override
public void onDestroy() {
    super.onDestroy();
    // release mMediaPlayer resoruces
    mMediaPlayer.release();
    mMediaPlayer = null;
}

但我建议将 mMediaPlayer 对象添加到 ViewModel 中,以便在 phone 方向等配置更改期间保存其状态。