Android development skills: background processing of music player [Service, Handler, MediaPlayer]

Given the partially completed MusicPlayer project, implement the unfinished service part:
1. Create the MusicService class and implement the function of playing music in the background through the service component;
2. Connect to MusicService through ServiceConnection in MainActivity to control music playback;
3. Use Handler mechanism to communicate between MainActivity and MusicService.

There is currently code:

Relevant resource files can be found by yourself

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/music_bg"
    android:gravity="center"
    android:orientation="vertical">
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="160dp"
        tools:ignore="UselessParent">
        <RelativeLayout
            android:id="@+id/rl_title"
            android:layout_width="300dp"
            android:layout_height="70dp"
            android:layout_centerHorizontal="true"
            android:background="@drawable/title_bg"
            android:gravity="center_horizontal"
            android:paddingStart="80dp"
            tools:ignore="RtlSymmetry">
            <TextView
                android:id="@+id/tv_music_title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="8dp"
                android:text="@string/song_name"
                android:textSize="12sp"
                android:textStyle="bold"
                android:textColor="@android:color/black"/>
            <TextView
                android:layout_marginTop="4dp"
                android:id="@+id/tv_type"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@id/tv_music_title"
                android:layout_alignStart="@id/tv_music_title"
                android:text="@string/pop_music"
                android:textSize="10sp"
                tools:ignore="SmallSp" />
            <SeekBar
                android:id="@+id/sb"
                android:layout_width="150dp"
                android:layout_height="wrap_content"
                android:layout_below="@id/rl_time"
                android:layout_alignParentBottom="true"
                android:thumb="@null" />
            <RelativeLayout
                android:layout_marginTop="4dp"
                android:id="@+id/rl_time"
                android:layout_width="150dp"
                android:layout_height="wrap_content"
                android:layout_below="@id/tv_type">
                <TextView
                    android:id="@+id/tv_progress"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="@string/zero_time"
                    android:textSize="10sp"
                    tools:ignore="SmallSp" />
                <TextView
                    android:id="@+id/tv_total"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignParentEnd="true"
                    android:text="@string/zero_time"
                    android:textSize="10sp"
                    tools:ignore="RelativeOverlap,SmallSp" />
            </RelativeLayout>
        </RelativeLayout>
        <LinearLayout
            android:layout_width="340dp"
            android:layout_height="90dp"
            android:layout_below="@id/rl_title"
            android:layout_centerHorizontal="true"
            android:background="@drawable/btn_bg"
            android:gravity="center_vertical"
            android:paddingStart="120dp"
            android:paddingEnd="10dp">
            <Button
                android:id="@+id/btn_play"
                android:layout_width="0dp"
                android:layout_height="25dp"
                android:layout_margin="4dp"
                android:layout_weight="1"
                android:background="@drawable/btn_bg_selector"
                android:text="@string/play"
                android:textSize="10sp"
                tools:ignore="ButtonStyle,SmallSp" />
            <Button
                android:id="@+id/btn_pause"
                android:layout_width="0dp"
                android:layout_height="25dp"
                android:layout_margin="4dp"
                android:layout_weight="1"
                android:background="@drawable/btn_bg_selector"
                android:text="@string/pause"
                android:textSize="10sp"
                tools:ignore="ButtonStyle,SmallSp" />
            <Button
                android:id="@+id/btn_continue_play"
                android:layout_width="0dp"
                android:layout_height="25dp"
                android:layout_margin="4dp"
                android:layout_weight="1"
                android:background="@drawable/btn_bg_selector"
                android:text="@string/cont"
                android:textSize="10sp"
                tools:ignore="ButtonStyle,SmallSp" />
            <Button
                android:id="@+id/btn_exit"
                android:layout_width="0dp"
                android:layout_height="25dp"
                android:layout_margin="4dp"
                android:layout_weight="1"
                android:background="@drawable/btn_bg_selector"
                android:text="@string/exit"
                android:textSize="10sp"
                tools:ignore="ButtonStyle,SmallSp" />
        </LinearLayout>
        <ImageView
            android:id="@+id/iv_music"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_centerVertical="true"
            android:layout_marginStart="35dp"
            android:layout_marginBottom="50dp"
            android:src="@drawable/img_music"
            android:contentDescription="@string/iv" />
    </RelativeLayout>
</LinearLayout>

MainActivity.java

package cn.itcast.musicplayer;

import android.animation.ObjectAnimator;
import android.os.Bundle;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    
    
    private static SeekBar sb;
    private static TextView tv_progress, tv_total;
    private ObjectAnimator animator;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();
    }

    private void init() {
    
    
        tv_progress = findViewById(R.id.tv_progress);
        tv_total = findViewById(R.id.tv_total);
        sb = findViewById(R.id.sb);
        findViewById(R.id.btn_play).setOnClickListener(this);
        findViewById(R.id.btn_pause).setOnClickListener(this);
        findViewById(R.id.btn_continue_play).setOnClickListener(this);
        findViewById(R.id.btn_exit).setOnClickListener(this);

        //为滑动条添加事件监听
        sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
    
    
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean
                    fromUser) {
    
                              //滑动条进度改变时,会调用此方法
                if (progress == seekBar.getMax()) {
    
     //当滑动条滑到末端时,结束动画
                    animator.pause();                   //停止播放动画
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
    
    //滑动条开始滑动时调用
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
    
     //滑动条停止滑动时调用
                //根据拖动的进度改变音乐播放进度
                int progress = seekBar.getProgress();//获取seekBar的进度
            }
        });
        ImageView iv_music = findViewById(R.id.iv_music);
        animator = ObjectAnimator.ofFloat(iv_music, "rotation", 0f, 360.0f);
        animator.setDuration(10000);  //动画旋转一周的时间为10秒
        animator.setInterpolator(new LinearInterpolator());
        animator.setRepeatCount(-1);  //-1表示设置动画无限循环
    }

    @Override
    public void onClick(View v) {
    
    
        switch (v.getId()) {
    
    
            case R.id.btn_play:                //播放按钮点击事件
                animator.start();               //播放动画
                break;
            case R.id.btn_pause:               //暂停按钮点击事件
                animator.pause();              //暂停播放动画
                break;
            case R.id.btn_continue_play:     //继续播放按钮点击事件
                animator.start();              //播放动画
                break;
            case R.id.btn_exit:                //退出按钮点击事件
                finish();                         //关闭音乐播放界面
                break;
        }
    }

    @Override
    protected void onDestroy() {
    
    
        super.onDestroy();
        //解绑服务
    }
}


There is already a user interface that includes play, pause, resume, and exit buttons, as well as a rotation animation effect. Now, we need to connect MusicService with MainActivity to implement music playback and control functions.

Step 1: Create MusicService class

Right-click the mouse and select [New]–>[Service]–>[Service]Insert image description here
Insert image description here

Step 1: Create MusicService class

First, you need to create a class named MusicService which will be responsible for handling music playback and communication with MainActivity.

Here you need to prepare a file in mp3 format
Insert image description here

package cn.itcast.musicplayer;

import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.IBinder;

public class MusicService extends Service {
    
    
    private MediaPlayer mediaPlayer;

    public MusicService() {
    
    
    }

    @Override
    public IBinder onBind(Intent intent) {
    
    
        return new MusicBinder();
    }

    public class MusicBinder extends Binder {
    
    
        MusicService getService() {
    
    
            return MusicService.this;
        }
    }

    @Override
    public void onCreate() {
    
    
        super.onCreate();
        mediaPlayer = new MediaPlayer();
        // 在这里设置音乐资源,例如 mediaPlayer.setDataSource(your_music_uri);
        mediaPlayer = MediaPlayer.create(this, R.raw.music); // 加载音乐资源

    }

    // 添加播放音乐的方法
    public void playMusic() {
    
    
        if (!mediaPlayer.isPlaying()) {
    
    
            mediaPlayer.start();
        }
    }

    // 添加暂停音乐的方法
    public void pauseMusic() {
    
    
        if (mediaPlayer.isPlaying()) {
    
    
            mediaPlayer.pause();
        }
    }

    // 添加继续播放音乐的方法
    public void continueMusic() {
    
    
        if (!mediaPlayer.isPlaying()) {
    
    
            mediaPlayer.start();
        }
    }

    @Override
    public void onDestroy() {
    
    
        if (mediaPlayer != null) {
    
    
            mediaPlayer.release();
            mediaPlayer = null;
        }
        super.onDestroy();
    }
}

Related variable description:

  1. MusicServiceIt is an Android service class used to handle functions related to music playback.

  2. mediaPlayerIt is a MediaPlayer object used to play music. It is responsible for loading music resources, playing, pausing and continuing to play music.

  3. MusicBinderThe inner class inherits from Binder and is used for communication between the binding service and other components.

  4. onBindThe method is used to return a MusicBinder object so that other components can bind to the service.

  5. onCreateThe method is called when the service is created, which initializes mediaPlayer and loads the music resources. In this example, the music resource is loaded from R.raw.music.

  6. playMusicThe method is used to play music. If the music is not playing, call mediaPlayer.start() to start playing.

  7. pauseMusicThe method is used to pause the music. If the music is playing, call mediaPlayer.pause() to pause it.

  8. continueMusicThe method is used to continue playing music. If the music has been paused, call mediaPlayer.start() to continue playing.

  9. onDestroyThe method is called when the service is destroyed, and it releases the resources of the mediaPlayer object to ensure that no memory leaks occur.

The service allows other components to bind to it to control the playing, pausing and resuming of music; loads a music resource (in this case R.raw.music) and uses MediaPlayer object plays music

Register MusicService in AndroidManifest.xml (check)
Make sure to register MusicService in AndroidManifest.xml file so that the application can start the service normally.
Generally, after we create the service file, it will be automatically registered
Insert image description here

<service android:name=".MusicService" />

Step 2: Connect MusicService in MainActivity

In MainActivity, add code to connect to MusicService and control the music to play, pause, and resume.

private MusicService musicService;
private boolean isBound = false;

private ServiceConnection serviceConnection = new ServiceConnection() {
    
    
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
    
    
        MusicService.MusicBinder binder = (MusicService.MusicBinder) service;
        musicService = binder.getService();
        isBound = true;
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
    
    
        isBound = false;
    }
};

@Override
protected void onStart() {
    
    
    super.onStart();
    Intent intent = new Intent(this, MusicService.class);
    bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}

@Override
protected void onStop() {
    
    
    super.onStop();
    if (isBound) {
    
    
        unbindService(serviceConnection);
        isBound = false;
    }
}

Step 3: Call the MusicService method in MainActivity

Call the method of in the onClick method to control the playback, pause and continuation of music. MusicService

@Override
public void onClick(View v) {
    
    
    switch (v.getId()) {
    
    
        case R.id.btn_play:    // 播放按钮点击事件
            if (isBound) {
    
    
                musicService.playMusic();
            }
            animator.start();    // 播放动画
            break;
        case R.id.btn_pause:   // 暂停按钮点击事件
            if (isBound) {
    
    
                musicService.pauseMusic();
            }
            animator.pause();   // 暂停播放动画
            break;
        case R.id.btn_continue_play: // 继续播放按钮点击事件
            if (isBound) {
    
    
                musicService.continueMusic();
            }
            animator.start();  // 播放动画
            break;
        case R.id.btn_exit:    // 退出按钮点击事件
            finish();            // 关闭音乐播放界面
            break;
    }
}

Currently, we have initially completed the play, pause, continue, and exit functions of the simple music player;
You can try running the project at this time to test the effect!

Step 4: Modify MusicService (to implement communication update UI)

Add function to obtain relevant information

    // 获取音乐总时长
    public int getTotalDuration() {
    
    
        return mediaPlayer.getDuration();
    }

    // 获取音乐当前播放进度
    public int getCurrentPosition() {
    
    
        return mediaPlayer.getCurrentPosition();
    }

    // 设置音乐播放进度
    public void seekTo(int position) {
    
    
        mediaPlayer.seekTo(position);
    }
    
    // 更新UI,发送消息给MainActivity
    private void updateUI(int progress, int totalDuration) {
    
    
        if (handler != null) {
    
    
            Message message = Message.obtain(handler, UPDATE_UI, progress, totalDuration);
            handler.sendMessage(message);
        }
    }

    @Override
    public void onCreate() {
    
    
        super.onCreate();
        mediaPlayer = MediaPlayer.create(this, R.raw.music); // 加载音乐资源
        mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
    
    
            @Override
            public void onCompletion(MediaPlayer mp) {
    
    
                // 音乐播放完成时的处理
            }
        });

        // 定时发送消息以更新UI
        Runnable runnable = new Runnable() {
    
    
            @Override
            public void run() {
    
    
                if (mediaPlayer != null && mediaPlayer.isPlaying()) {
    
    
                    int progress = mediaPlayer.getCurrentPosition();
                    int totalDuration = mediaPlayer.getDuration();
                    updateUI(progress, totalDuration);
                }
                handler.postDelayed(this, DELAY_MILLIS);
            }
        };
        handler.postDelayed(runnable, DELAY_MILLIS);
    }

This code adds some important functionality to MusicService to communicate with MainActivity and update the UI. Here is a description of the code:

  1. getTotalDuration The function is used to get the total duration of the music. It gets the total duration of the music through the mediaPlayer.getDuration() method and returns that value.

  2. getCurrentPosition function is used to get the current playback progress of music. Get the current playback progress of the music through the mediaPlayer.getCurrentPosition() method, and then return this value.

  3. seekToThe function is used to set the music playback progress. Accepts an integer parameter position, indicating the music playback progress to be set, and uses the mediaPlayer.seekTo(position) method to jump the progress.

  4. updateUIThe function is used to send a message to MainActivity in order to update the UI element. It accepts two parameters, namely the current playback progress progress and the total duration of the music totalDuration. It creates a Message object and sends messages to via handler.sendMessage(message) in order to update UI elements such as progress bars and text. MainActivity

  5. In the onCreate method, send messages regularly to update the UI. Through a Runnable scheduled task, obtain the current playback progress and total music duration, and then call the updateUI function to send a message to MainActivity. To achieve the purpose of constantly updating UI elements.

These functions and logic enable MusicService to communicate with MainActivity, passing the music playback progress and total duration so that MainActivity Ability to update UI elements to provide a user-friendly music playback experience.

Step 5: Modify MainActivity (to achieve communication update UI)

Get the total duration of music in MusicService, and update the position of tv_total and the progress bar in MainActivity, as well as the total duration and progress of formatted music.

    private static final int UPDATE_UI = 1;

    public final static Handler handler = new Handler(Looper.getMainLooper()) {
    
    
        @Override
        public void handleMessage(Message msg) {
    
    
            super.handleMessage(msg);
            if (msg.what == UPDATE_UI) {
    
    
                int progress = msg.arg1;
                int totalDuration = msg.arg2;
                updateUI(progress, totalDuration);
            }
        }
    };

    public static void updateUI(int progress, int totalDuration) {
    
    
        sb.setProgress(progress);
        tv_progress.setText(formatDuration(progress));
        // 更新左侧显示的总时间
        tv_total.setText(formatDuration(totalDuration));
    }

    // 辅助方法来更新进度
    private void updateProgress(int progress) {
    
    
        tv_progress.setText(formatDuration(progress));
    }

    // 辅助方法来格式化音乐时长
    private static String formatDuration(int duration) {
    
    
        int minutes = (duration / 1000) / 60;
        int seconds = (duration / 1000) % 60;
        return String.format(Locale.getDefault(), "%02d:%02d", minutes, seconds);
    }


    // 添加方法来更新总时长
    private void updateTotalDuration(int duration) {
    
    
        tv_total.setText(formatDuration(duration));
        sb.setMax(duration);
    }

    private ServiceConnection serviceConnection = new ServiceConnection() {
    
    
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
    
    
            MusicService.MusicBinder binder = (MusicService.MusicBinder) service;
            musicService = binder.getService();
            isBound = true;

            // 获取音乐总时长并更新UI
            int totalDuration = musicService.getTotalDuration();
            updateTotalDuration(totalDuration);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
    
    
            isBound = false;
        }
    };
    private void init() {
    
    
        // 初始化控件和按钮点击事件监听
        tv_progress = findViewById(R.id.tv_progress);
        tv_total = findViewById(R.id.tv_total);
        sb = findViewById(R.id.sb);


        // ...


        sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
    
    
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    
    
                if (progress == seekBar.getMax()) {
    
    
                    animator.pause();
                }
                updateProgress(progress);

            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
    
    
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
    
    
                // 更新音乐播放进度
                int progress = seekBar.getProgress();
                musicService.seekTo(progress); // 添加 seekTo 方法用于定位音乐进度
            }
        });
                    // ...

This code adds communication between MainActivity and MusicService in order to dynamically update the progress of music playback and display the total duration of music. . Here is a description of the code:

  1. defines a in MainActivity, which is a static object that is used to handle the Message sent to update UI elements. The message type is identified by the constant. handlerHandlerMusicServiceUPDATE_UI

  2. updateUIThe function is the core method for updating UI elements. Accepts two parameters, namely the current playback progress progress and the total duration of the music totalDuration. In this method, the position of the progress bar will be set to the current playback progress, the text on the lefttv_progress will be updated to the formatted playback progress, and the total duration text on the left will be updated to the formatted playback progress. a>tv_total will be updated to the total duration of the formatted music.

  3. updateProgressThe method is a helper method used to update the playback progress. Accepts a parameter progress, and updates the text on the left tv_progress to the formatted playback progress.

  4. formatDurationThe method is a helper method used to format music duration. Accepts an integer duration representing the duration of the music in milliseconds, and formats it as minutes:seconds.

  5. updateTotalDurationThe method is used to update the total duration. It accepts a parameter duration, indicating the total duration of the music, and updates the total duration text on the left tv_total to the formatted total duration of the music, and sets the progress bar The maximum value is the total duration of the music.

  6. InserviceConnection, when MusicService and MainActivity are successfully connected, the total duration of the music will be obtained and < /span>updateTotalDuration method to update UI elements.

  7. In the event listener ofsb (SeekBar), through the onProgressChanged method, when the progress of the progress bar changes, it will be called method is called to locate the progress of the music. method, when the user drags the progress bar, the updateProgress method to update the playback progress text on the left. In the onStopTrackingTouchmusicService.seekTo(progress)

These code changes enable MainActivity to work together with MusicService to achieve dynamic updates of music playback progress and display of the total music duration. This is essential to provide a user-friendly music playback experience.

Step 6: Add processing details after the music ends
For these new problems, we can make the following modifications and processing:

  1. Stop animation: The animation should stop as the music completes. We added a music playback completion callback in MusicService to pause the animation when the music ends. This way, the user can see that the music is complete while the animation is no longer rotating, providing a clear visual indication. As shown below:
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
    
    
    @Override
    public void onCompletion(MediaPlayer mp) {
    
    
        // 音乐播放完成时的处理
        animator.pause(); // 停止动画
    }
});
  1. The moving text content on the left cannot reach the maximum value: In order to ensure that the time text on the left reaches the maximum value after the music playback is completed, we updated it in the music playback completion callback Time text on the left. By calling tv_progress.setText(formatDuration(getTotalDuration())) we set the time text on the left to the total duration of the music to indicate that the music is complete
    @Override
    public void onCreate() {
    
    
//……
            @Override
            public void onCompletion(MediaPlayer mp) {
    
    
                // 音乐播放完成时的处理
                animator.pause(); // 停止动画
                tv_progress.setText(formatDuration(getTotalDuration()));
            }
        });

    }

  1. No reminder when music is complete: We added a way to notify the user that music is complete. In the music playback completion callback in MusicService, we use the showToast function to display a short prompt message. This reminder can be expanded based on your needs, for example, you can choose to display a notification, perform additional actions, or add more user feedback.
    private void showToast(String message) {
    
    
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onCreate() {
    
    
//……
            @Override
            public void onCompletion(MediaPlayer mp) {
    
    
                // 音乐播放完成时的处理
                animator.pause(); // 停止动画
                showToast("音乐已完成"); // 显示音乐播放完成的提示
            }
        });

    }

Complete code
MainActivity.java

package cn.itcast.musicplayer;

import static cn.itcast.musicplayer.MusicService.mediaPlayer;

import android.animation.ObjectAnimator;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Bundle;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;

import java.util.Locale;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    
    
    private static SeekBar sb;
    public static TextView tv_progress, tv_total;
    public static ObjectAnimator animator;
    private MusicService musicService;
    private boolean isBound = false;
    private static final int UPDATE_UI = 1;

    // 辅助方法来格式化音乐时长
    public static String formatDuration(int duration) {
    
    
        int minutes = (duration / 1000) / 60;
        int seconds = (duration / 1000) % 60;
        return String.format(Locale.getDefault(), "%02d:%02d", minutes, seconds);
    }

    // 添加方法来更新总时长
    private void updateTotalDuration(int duration) {
    
    
        tv_total.setText(formatDuration(duration));
        sb.setMax(duration);
    }
    // 辅助方法来更新进度
    private void updateProgress(int progress) {
    
    
        tv_progress.setText(formatDuration(progress));
    }

    private ServiceConnection serviceConnection = new ServiceConnection() {
    
    
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
    
    
            MusicService.MusicBinder binder = (MusicService.MusicBinder) service;
            musicService = binder.getService();
            isBound = true;

            // 获取音乐总时长并更新UI
            int totalDuration = musicService.getTotalDuration();
            updateTotalDuration(totalDuration);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
    
    
            isBound = false;
        }
    };

    public final static Handler handler = new Handler(Looper.getMainLooper()) {
    
    
        @Override
        public void handleMessage(Message msg) {
    
    
            super.handleMessage(msg);
            if (msg.what == UPDATE_UI) {
    
    
                int progress = msg.arg1;
                int totalDuration = msg.arg2;
                updateUI(progress, totalDuration);
            }
        }
    };

    public static void updateUI(int progress, int totalDuration) {
    
    
        sb.setProgress(progress);
        tv_progress.setText(formatDuration(progress));
        tv_total.setText(formatDuration(totalDuration));
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();
    }

    private void init() {
    
    
        // 初始化控件和按钮点击事件监听
        tv_progress = findViewById(R.id.tv_progress);
        tv_total = findViewById(R.id.tv_total);
        sb = findViewById(R.id.sb);
        findViewById(R.id.btn_play).setOnClickListener(this);
        findViewById(R.id.btn_pause).setOnClickListener(this);
        findViewById(R.id.btn_continue_play).setOnClickListener(this);
        findViewById(R.id.btn_exit).setOnClickListener(this);

        // ...

        sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
    
    
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    
    
                if (progress == seekBar.getMax()) {
    
    
                    animator.pause();
                }
                updateProgress(progress);

            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
    
    
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
    
    
                // 更新音乐播放进度
                int progress = seekBar.getProgress();
                musicService.seekTo(progress); // 添加 seekTo 方法用于定位音乐进度
            }
        });

        // 初始化动画
        ImageView iv_music = findViewById(R.id.iv_music);
        animator = ObjectAnimator.ofFloat(iv_music, "rotation", 0f, 360.0f);
        animator.setDuration(10000);  //动画旋转一周的时间为10秒
        animator.setInterpolator(new LinearInterpolator());
        animator.setRepeatCount(-1);  //-1表示设置动画无限循环
    }

    @Override
    public void onClick(View v) {
    
    
        switch (v.getId()) {
    
    
            case R.id.btn_play:
                if (isBound) {
    
    
                    musicService.playMusic();
                }
                animator.start();
                break;
            case R.id.btn_pause:
                if (isBound) {
    
    
                    musicService.pauseMusic();
                }
                animator.pause();
                break;
            case R.id.btn_continue_play:
                if (isBound) {
    
    
                    musicService.continueMusic();
                }
                animator.start();
                break;
            case R.id.btn_exit:
                finish();
                break;
        }
    }

    @Override
    protected void onStart() {
    
    
        super.onStart();
        Intent intent = new Intent(this, MusicService.class);
        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
    }
    @Override
    protected void onDestroy() {
    
    
        super.onDestroy();
        //解绑服务
        if (isBound) {
    
    
            unbindService(serviceConnection);
            isBound = false;
        }
    }
}

MusicService.java

package cn.itcast.musicplayer;

import static cn.itcast.musicplayer.MainActivity.formatDuration;
import static cn.itcast.musicplayer.MainActivity.handler;
import static cn.itcast.musicplayer.MainActivity.animator;
import static cn.itcast.musicplayer.MainActivity.tv_progress;

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.os.Message;
import android.widget.Toast;

public class MusicService extends Service {
    
    
    public static MediaPlayer mediaPlayer;
    private final IBinder binder = new MusicBinder();
    private final int UPDATE_UI = 1;
    private final int DELAY_MILLIS = 1000; // 延迟1秒发送消息

    public MusicService() {
    
    
    }
    @Override
    public IBinder onBind(Intent intent) {
    
    
        return binder;
    }

    public class MusicBinder extends Binder {
    
    
        MusicService getService() {
    
    
            return MusicService.this;
        }
    }
    private void showToast(String message) {
    
    
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onCreate() {
    
    
        super.onCreate();
        mediaPlayer = MediaPlayer.create(this, R.raw.music); // 加载音乐资源
        mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
    
    
            @Override
            public void onCompletion(MediaPlayer mp) {
    
    
                // 音乐播放完成时的处理
                animator.pause(); // 停止动画
                tv_progress.setText(formatDuration(getTotalDuration()));
                showToast("音乐已完成"); // 显示音乐播放完成的提示
            }
        });

        // 定时发送消息以更新UI
        Runnable runnable = new Runnable() {
    
    
            @Override
            public void run() {
    
    
                if (mediaPlayer != null && mediaPlayer.isPlaying()) {
    
    
                    int progress = mediaPlayer.getCurrentPosition();
                    int totalDuration = mediaPlayer.getDuration();
                    updateUI(progress, totalDuration);
                }
                handler.postDelayed(this, DELAY_MILLIS);
            }
        };
        handler.postDelayed(runnable, DELAY_MILLIS);
    }

    // 更新UI,发送消息给MainActivity
    private void updateUI(int progress, int totalDuration) {
    
    
        if (handler != null) {
    
    
            Message message = Message.obtain(handler, UPDATE_UI, progress, totalDuration);
            handler.sendMessage(message);
        }
    }

    // 添加播放音乐的方法
    public void playMusic() {
    
    
        if (!mediaPlayer.isPlaying()) {
    
    
            mediaPlayer.start();
        }
    }

    // 添加暂停音乐的方法
    public void pauseMusic() {
    
    
        if (mediaPlayer.isPlaying()) {
    
    
            mediaPlayer.pause();
        }
    }

    // 添加继续播放音乐的方法
    public void continueMusic() {
    
    
        if (!mediaPlayer.isPlaying()) {
    
    
            mediaPlayer.start();
        }
    }

    @Override
    public void onDestroy() {
    
    
        if (mediaPlayer != null) {
    
    
            mediaPlayer.release();
            mediaPlayer = null;
        }
        super.onDestroy();
    }

    // 获取音乐总时长
    public int getTotalDuration() {
    
    
        return mediaPlayer.getDuration();
    }

    // 获取音乐当前播放进度
    public int getCurrentPosition() {
    
    
        return mediaPlayer.getCurrentPosition();
    }

    // 设置音乐播放进度
    public void seekTo(int position) {
    
    
        mediaPlayer.seekTo(position);
    }

}

achieve effect

The most important thing is to be able to后台播放音乐
Please add image description

Guess you like

Origin blog.csdn.net/qq_22841387/article/details/133862753