第8章系统服务(简易音频播放器的实现)

开发一个简易音乐播放器,要求实现: 综合使用Service,BroadCast,ContentProvider等组件实现后台播放。

  • 播放和暂停、上一首、下一首、停止;
  • 后台播放功能, 按下返回键退出应用后再次打开应用,UI 显示应能与当前的播放状态保持一致;
  • 显示正在播放的歌曲名、作者;
  • 一首歌曲播放完毕能实现自动播放下一首歌曲,并更新界面,显示相关信息;

提交 MusicService 和 MainActivity,运行结果截图。

评分: 1.基本分,60分,实现基本功能,提交的内容完整,条理清晰,可读性好。 2.上一首、下一首;+20分 3.UI销毁后再次打开应能与当前的播放状态保持一致;+20分

在模拟器中上传音乐:

找到路径:C:\Users\17511\Documents\AndroidStudio\DeviceExplorer\emulator-5554\storage\emulated\legacy\Music

 

 

MainActivity.java

package com.example.player;

import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    public static final String CONTROL = "iet.jxufe.cn.android.control";//控制播放、暂停
    public static final String UPDATE = "iet.jxufe.cn.android.update";//更新界面显示

    // 定义音乐的播放状态,0x11代表没有播放;0x12代表正在播放;0x13代表暂停
    int status = 0x11;

    //获取界面中显示歌曲标题、作者文本框
    TextView title, author;
    // 播放/暂停、停止按钮
    ImageButton play, stop,next,pre;
    ActivityReceiver activityReceiver;

    //“启动”服务的intent
    Intent MusicServiceIntent;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 获取程序界面中的两个按钮以及两个文本显示框
        play = (ImageButton) this.findViewById(R.id.play);
        stop = (ImageButton) this.findViewById(R.id.stop);
        next = (ImageButton)this.findViewById(R.id.next);
        pre = (ImageButton)this.findViewById(R.id.pre);
        title = (TextView) findViewById(R.id.title);
        author = (TextView) findViewById(R.id.author);
        // 为两个按钮的单击事件添加监听器
        play.setOnClickListener(this);
        stop.setOnClickListener(this);
        next.setOnClickListener(this);
        pre.setOnClickListener(this);

        activityReceiver = new ActivityReceiver();
        IntentFilter filter = new IntentFilter(UPDATE);
        // 注册BroadcastReceiver
        registerReceiver(activityReceiver, filter);

        MusicServiceIntent = new Intent(this, MusicService.class);
        //判断权限够不够,不够就给
        if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{
                    Manifest.permission.WRITE_EXTERNAL_STORAGE
            }, 1);
        } else {
            //够了就设置路径等,准备播放
            startService(MusicServiceIntent);
        }
    }
    //获取到权限回调方法
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case 1:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    startService(MusicServiceIntent);
                } else {
                    Toast.makeText(this, "权限不够获取不到音乐,程序将退出", Toast.LENGTH_SHORT).show();
                    finish();
                }
                break;
            default:
                break;
        }
    }

    public void onClick(View source) {
        // 创建Intent
        Intent intent = new Intent(CONTROL);
        //System.out.println(source.getId());
        //System.out.println(source.getId() == R.id.play);
        switch (source.getId()) {
            // 按下播放/暂停按钮
            case R.id.play:
                intent.putExtra("control", 1);
                Log.d("MusicService","play pressed");
                break;
            // 按下停止按钮
            case R.id.stop:
                intent.putExtra("control", 2);
                Log.d("MusicService","stop pressed");
                break;
            case R.id.next:
                intent.putExtra("control", 3);
                Log.d("MusicService","next pressed");
                break;
            case R.id.pre:
                intent.putExtra("control", 4);
                Log.d("MusicService","pre pressed");
                break;
        }
        // 发送广播 ,将被Service组件中的BroadcastReceiver接收到
        sendBroadcast(intent);
    }

    // 自定义的BroadcastReceiver,负责监听从Service传回来的广播
    public class ActivityReceiver extends BroadcastReceiver {
        public void onReceive(Context context, Intent intent) {
            // 获取Intent中的update消息,update代表播放状态,默认为-1
            int update = intent.getIntExtra("update", -1);
            // 获取Intent中的current消息,current代表当前正在播放的歌曲,默认为-1
            String songs_title = "";
            songs_title = intent.getStringExtra("songs_title");
            String songs_author = "";
            songs_author = intent.getStringExtra("songs_author");
            if (update >= 0) {
                title.setText(songs_title);
                author.setText(songs_author);
            }
            switch (update) {
                case 0x11:
                    play.setImageResource(R.drawable.play);
                    status = 0x11;
                    break;
                // 控制系统进入播放状态
                case 0x12:
                    // 播放状态下设置使用暂停图标
                    play.setImageResource(R.drawable.pause);
                    // 设置当前状态
                    status = 0x12;
                    break;
                // 控制系统进入暂停状态
                case 0x13:
                    // 暂停状态下设置使用播放图标
                    play.setImageResource(R.drawable.play);
                    // 设置当前状态
                    status = 0x13;
                    break;
            }
        }
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(activityReceiver);
    }
}

 

 这样就会自动添加权限

 MusicService.java

package com.example.player;

import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.MediaPlayer;
import android.os.Environment;
import android.os.IBinder;
import android.util.Log;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MusicService extends Service {

    private static final String TAG = "MusicService";
    private List<Map<String,Object>> musicList = new ArrayList<Map<String,Object>>();
    private int songsCount = 0;

    private ServiceReceiver serviceReceiver;

    private MediaPlayer mPlayer;
    //当前的状态,0x11 代表没有播放 ;0x12代表 正在播放;0x13代表暂停
    private int status = 0x11;
    // 记录当前正在播放的音乐
    private int current = 0;

    public MusicService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    public void onCreate(){
        Log.d(TAG,"创建音乐播放服务");
        // 创建BroadcastReceiver
        serviceReceiver = new ServiceReceiver();
        // 创建IntentFilter
        IntentFilter filter = new IntentFilter(MainActivity.CONTROL);
        registerReceiver(serviceReceiver, filter);
        // 创建MediaPlayer
        mPlayer = new MediaPlayer();
        initSongsData();
        // 为MediaPlayer播放完成事件绑定监听器
        mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener(){
            public void onCompletion(MediaPlayer mp){
               /* current++;
                if (current >= songsCount){
                    current = 0;
                }*/
               setNextMusic();
                /* 发送广播通知Activity更改文本框 */
                Intent sendIntent = new Intent(MainActivity.UPDATE);
                Map<String,Object> map = musicList.get(current);
                status = 0x12;
                sendIntent.putExtra("update", status);
                sendIntent.putExtra("songs_title",(String)map.get("songs_title"));
                sendIntent.putExtra("songs_author",(String)map.get("songs_author"));
                // 发送广播 ,将被Activity组件中的BroadcastReceiver接收到
                sendBroadcast(sendIntent);
                // 准备、并播放音乐
                prepareAndPlay((File)map.get("songs_path"));
            }
        });
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG,"启动音乐播放服务");

        //Map<String,Object> map = musicList.get(current);

        /* 发送广播通知Activity更改图标、文本框 */
        /*Intent sendIntent = new Intent(MainActivity.UPDATE);
        sendIntent.putExtra("update", status);
        sendIntent.putExtra("songs_title", (String)map.get("songs_title"));
        sendIntent.putExtra("songs_author",(String)map.get("songs_author"));
        if (status == 0x11)
        {
            sendIntent.putExtra("songs_title","");
            sendIntent.putExtra("songs_author","");
        }
        // 发送广播 ,将被Activity组件中的BroadcastReceiver接收到
        sendBroadcast(sendIntent);*/

        return super.onStartCommand(intent, flags, startId);
    }
    private void setNextMusic(){
        current++;
        if(current>=songsCount){
            current = 0;
        }
    }
    private void setPreMusic(){
        current--;
        if(current<0){
            current = songsCount-1;
        }
    }

    // 初始化MediaPlayer歌曲路径,让MediaPlayer进入到准备状态
    private void initSongsData() {
        try {
            //歌曲路径
            Map<String,Object> map = new HashMap<String, Object>();
            map.put("songs_title","你是温柔本身");
            map.put("songs_author","UN1K");
            map.put("songs_path", new File(Environment.getExternalStorageDirectory(), "/Music/a1.mp3"));
            musicList.add(map);

            map = new HashMap<String, Object>();
            map.put("songs_title","白羊");
            map.put("songs_author","徐秉龙");
            map.put("songs_path", new File(Environment.getExternalStorageDirectory(), "/Music/a2.mp3"));
            musicList.add(map);

            map = new HashMap<String, Object>();
            map.put("songs_title","星空");
            map.put("songs_author","Richard Clayderman .天籁村");
            map.put("songs_path", new File(Environment.getExternalStorageDirectory(), "/Music/a3.mp3"));
            musicList.add(map);

            map = new HashMap<String, Object>();
            map.put("songs_title","我的一个道姑朋友");
            map.put("songs_author","双笙");
            map.put("songs_path", new File(Environment.getExternalStorageDirectory(), "/Music/a4.mp3"));
            musicList.add(map);
            songsCount = musicList.size();
            Log.d(TAG,songsCount+"");

            //mPlayer.setDataSource(((File)musicList.get(0)).getPath()); // 指定音频文件的路径
            //mPlayer.prepare(); // 让MediaPlayer进入到准备状态
        } catch (Exception e) {
            Log.d(TAG, "设置资源,准备阶段出错");
            e.printStackTrace();
        }
    }

    public class ServiceReceiver extends BroadcastReceiver {
        public void onReceive(final Context context, Intent intent){
            Log.d(TAG,"服务中的广播接收器收到广播");
            int control = intent.getIntExtra("control", -1);
            Map<String,Object> map = musicList.get(current);
            switch (control){
                // 播放或暂停
                case 1:
                    // 原来处于没有播放状态
                    if (status == 0x11){
                        // 准备、并播放音乐
                        prepareAndPlay((File) map.get("songs_path"));
                        status = 0x12;
                    }
                    // 原来处于播放状态
                    else if (status == 0x12){
                        mPlayer.pause();// 暂停
                        status = 0x13;// 改变为暂停状态
                    }
                    // 原来处于暂停状态
                    else if (status == 0x13){
                        mPlayer.start();// 播放
                        status = 0x12;// 改变状态
                    }
                    break;
                // 停止声音
                case 2:
                    // 如果原来正在播放或暂停
                    if (status == 0x12 || status == 0x13){
                        current = 0;
                        //mPlayer.reset();
                        mPlayer.stop();// 停止播放
                        status = 0x11;
                    }
                    break;
                case 3:
                setNextMusic();
                status = 0x12;
                map = musicList.get(current);
                prepareAndPlay((File)map.get("songs_path"));
                break;

                case 4:
                    // 上一首
                setPreMusic();
                status = 0x12;
                map = musicList.get(current);
                prepareAndPlay((File) map.get("songs_path"));
                break;

            }
            /*/* 发送广播通知Activity更改图标、文本框 */
           Intent sendIntent = new Intent(MainActivity.UPDATE);
            sendIntent.putExtra("update", status);
            sendIntent.putExtra("songs_title", (String)map.get("songs_title"));
            sendIntent.putExtra("songs_author",(String)map.get("songs_author"));
            if (status == 0x11)
            {
                sendIntent.putExtra("songs_title","");
                sendIntent.putExtra("songs_author","");
            }
            // 发送广播 ,将被Activity组件中的BroadcastReceiver接收到
            sendBroadcast(sendIntent);
        }
    }


    private void prepareAndPlay(File songsPath){
        try{

            mPlayer.reset();
            //使用MediaPlayer加载指定的声音文件。
            mPlayer.setDataSource(songsPath.getPath());
            mPlayer.prepare();// 准备声音
            mPlayer.start();// 播放
        }
        catch (IOException e){
            e.printStackTrace();
        }
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(serviceReceiver);
    }

}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/bg"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="horizontal">

        <ImageButton
            android:id="@+id/pre"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/selector_btn"
            android:src="@drawable/previous_button" />

        <ImageButton
            android:id="@+id/stop"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/selector_btn"
            android:src="@drawable/stop" />

        <ImageButton
            android:id="@+id/play"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/selector_btn"
            android:src="@drawable/play" />

        <ImageButton
            android:id="@+id/next"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/selector_btn"
            android:src="@drawable/next_button" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical">

        <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textColor="#ffffff"
            android:layout_gravity="fill_vertical"
            android:textSize="20sp" />

        <TextView
            android:id="@+id/author"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:layout_gravity="fill_vertical"
            android:textColor="#ffffff"
            android:textSize="18sp" />
    </LinearLayout>

</LinearLayout>

 AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.player">

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <service
            android:name=".MusicService"
            android:enabled="true"
            android:exported="true"></service>

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

 初始界面:

 点击播放:

 点击暂停:

点击下一首:

 

 点击停止:

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值