安卓基础学习 Day14 |服务的通信-音乐播放器(存储卡)

写在前面的话

1、参考自:https://b23.tv/0mHcF5
2、内容如果有不对的,希望可以指出或补充。
3、由于这部分卡了很久,也算是个完整的练习,所以单独提出来了。

一、概述

通信方式:

1、本地服务通信:是指应用程序内部的通信,需要使用IBinder对象进行本地服务。

2、远程服务通信:是指两个应用程序间的通信,远程服务通信是通过AIDL(Android Interface Definition Language,Android接口定义语言,定义了客户端与服务端的一个标准)实现的。

这里测试的是本地服务通信。

二、遇到的问题

以下都是在Android Studio4.1.1里进行的。

报错信息:
在这里插入图片描述
最终换成以下这种虚拟机(成功解决了问题):
在这里插入图片描述

三、步骤展示

1、准备

文件:
在这里插入图片描述
导入音频文件:
在这里插入图片描述
声明:
在这里插入图片描述
2、布局

activity_play.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@mipmap/bg"
    android:gravity="center_vertical"
    >
    <!--拖动条-->
    <SeekBar
        android:id="@+id/seek_bar"

        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:max="100"/>
    <!--按钮-->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:layout_marginTop="20dp">
        <Button
            android:id="@+id/play_or_pause_btn"

            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="播放" />
        <Button
            android:id="@+id/close_btn"

            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="停止" />
    </LinearLayout>

</LinearLayout>

3、接口

activity与服务间通过接口连接。
在这里插入图片描述
在这里插入图片描述
4、服务

PlayerService.java

package com.example.testservice.services;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.Nullable;
import com.example.testservice.presenter.PlayerPresenter;

public class PlayerService extends Service {
    
    

    private static final String TAG = "PlayerService";
    private PlayerPresenter mplayerPresenter;

    @Override
    public void onCreate(){
    
    
        Log.d(TAG,"onCreate...");
        super.onCreate();
        if (mplayerPresenter == null) {
    
    
            mplayerPresenter = new PlayerPresenter();
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
    
    
        Log.d(TAG,"onBind...");
        return mplayerPresenter;
    }

    @Override
    public void onDestroy() {
    
    
        Log.d(TAG,"onDestroy...");
        super.onDestroy();
        mplayerPresenter = null;
    }
}

5、功能实现部分

PlayerPresenter.java

package com.example.testservice.presenter;

import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.Binder;
import android.util.Log;

import com.example.testservice.interfaces.IPlayerControl;
import com.example.testservice.interfaces.IPlayerViewControl;

import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;

//实现逻辑层的服务
public class PlayerPresenter extends Binder implements IPlayerControl {
    
    

    private static final String TAG = "PlayerPresenter";
    private IPlayerViewControl mViewController;
    private int mCurrentState = PLAY_STATE_STOP;//记录当前的播放状态 默认停止
    private MediaPlayer mMediaPlayer;
    private Timer mTimer;
    private SeekTimeTask mTimeTask;

    @Override
    public void registerViewController(IPlayerViewControl viewControl) {
    
    
        this.mViewController = viewControl;
    }

    @Override
    public void unRegisterViewController() {
    
    
        mViewController = null;
    }

    @Override
    public void playOrPause() {
    
    
        Log.i(TAG,"playOrPause...");
        if (mCurrentState==PLAY_STATE_STOP) {
    
    
            //创建播放器
            initPlayer();
            //设置数据源
            try {
    
    
                mMediaPlayer.setDataSource("/mnt/sdcard/testsong.mp3");
                mMediaPlayer.prepare();
                mMediaPlayer.start();
                mCurrentState = PLAY_STATE_PLAY;
                startTimer();//拖动条开始动
            }catch (IOException e){
    
    
                e.printStackTrace();
            }
        }else if(mCurrentState == PLAY_STATE_PLAY){
    
    
            //如果当前状态为播放则暂停
            if (mMediaPlayer != null) {
    
    
                mMediaPlayer.pause();
                mCurrentState = PLAY_STATE_PAUSE;
                stopTimer();//拖动条停止
            }
        }else if(mCurrentState == PLAY_STATE_PAUSE){
    
    
            //如果当前状态为暂停就继续播放
            if (mMediaPlayer != null) {
    
    
                mMediaPlayer.start();
                mCurrentState = PLAY_STATE_PLAY;
                startTimer();
            }
        }
        //通知ui更新界面按钮文字
        if (mViewController != null) {
    
    
            mViewController.onPlayStateChange(mCurrentState);
        }
    }

    //初始化播放器
    private void initPlayer() {
    
    
        if (mMediaPlayer == null) {
    
    
            //音频播放器
            mMediaPlayer = new MediaPlayer();
            //播放类型 因为播放的mp3
            mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        }
    }

    // 停止过后再播放就是从头开始
    @Override
    public void stopPlay() {
    
    
        Log.i(TAG,"stopPlay...");
        if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
    
    
            mMediaPlayer.stop();
            mCurrentState = PLAY_STATE_STOP;
            stopTimer();
            if (mViewController != null) {
    
    
                mViewController.onPlayStateChange(mCurrentState);//更新播放状态
            }
            mMediaPlayer.release();
            mMediaPlayer = null;
        }
    }

    @Override
    public void seekTo(int seek) {
    
    
        Log.i(TAG,"seekTo..."+seek);
        //seek是0~100
        //转换 得到seek百分比
        if (mMediaPlayer != null) {
    
    
            //.getDuration()取到的是音频的真实“长度”
            int tarSeek = (int) (seek * 1.0f / 100 * mMediaPlayer.getDuration());
            mMediaPlayer.seekTo(tarSeek);
        }
    }

    //开启一个timerTask
    private void startTimer(){
    
    
        if (mTimer == null) {
    
    
            mTimer = new Timer();
        }
        if (mTimeTask == null) {
    
    
            mTimeTask = new SeekTimeTask();
        }
        mTimer.schedule(mTimeTask,0,500);//500毫秒
    }
    //停止
    private void stopTimer(){
    
    
        if (mTimeTask != null) {
    
    
            mTimeTask.cancel();
            mTimeTask = null;//置空
        }
        if (mTimer != null) {
    
    
            mTimer.cancel();
            mTimer = null;
        }
    }

    private class SeekTimeTask extends TimerTask{
    
    
        @Override
        public void run() {
    
    
            if (mMediaPlayer != null && mViewController != null) {
    
    
                //获取当前的播放进度
                int currentPosition = mMediaPlayer.getCurrentPosition();
                //Log.d(TAG,"current play position..."+currentPosition);
                //当前的播放进度/总的播放进度
                int curPosition = (int) (currentPosition * 1.0f / mMediaPlayer.getDuration() *100);
                //改变ui界面拖动条
                mViewController.onSeekChange(curPosition);
            }
        }
    }
}

6、活动窗口

PlayerActivity.java

package com.example.testservice;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.SeekBar;

import androidx.annotation.Nullable;

import com.example.testservice.interfaces.IPlayerControl;
import com.example.testservice.interfaces.IPlayerViewControl;
import com.example.testservice.services.PlayerService;

import static com.example.testservice.interfaces.IPlayerControl.PLAY_STATE_PAUSE;
import static com.example.testservice.interfaces.IPlayerControl.PLAY_STATE_PLAY;
import static com.example.testservice.interfaces.IPlayerControl.PLAY_STATE_STOP;

//ui的展示
//activity与服务间通过接口连接
public class PlayerActivity extends Activity {
    
    

    private static final String TAG = "PlayerActivity";
    private SeekBar mSeekBar;
    private Button mPlayOrPause,mClose;
    private PlayerConnection mPlayerConnection;
    private IPlayerControl mIPlayerControl;
    private boolean isUserTouchProgressBar = false;//默认用户没触摸到拖动条

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_play);
        initView();
        //设置相关事件
        initEvent();

        //另需开启服务 这样服务才能长期运行
        initService();
        //绑定服务
        initBindService();
    }

    //开启播放的服务
    private void initService() {
    
    
        Log.d(TAG,"initService");
        startService(new Intent(this,PlayerService.class));
    }

    //绑定服务
    private void initBindService() {
    
    
        Log.d(TAG,"initBindService");
        Intent intent = new Intent(this, PlayerService.class);
        if(mPlayerConnection == null) {
    
    
            mPlayerConnection = new PlayerConnection(); //ctrl+alt+f抽取成员变量
        }
        bindService(intent,mPlayerConnection,BIND_AUTO_CREATE);
    }
    private class PlayerConnection implements ServiceConnection{
    
    
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
    
    
            Log.d(TAG,"onServiceConnected------>"+service);
            mIPlayerControl = (IPlayerControl) service;//这样才能进行下面的控件事件
            //按钮功能的接口连接
            mIPlayerControl.registerViewController(mIPlayerViewControl);
        }
        @Override
        //断开连接(绑定
        public void onServiceDisconnected(ComponentName name) {
    
    
            Log.d(TAG,"onServiceDisconnected");
            mIPlayerControl = null;
        }
    }

    //初始化控件事件
    private void initEvent() {
    
    
        mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
    
    
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    
    
                //进度条发生改变
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
    
    
                //拖动进度条
                isUserTouchProgressBar = true;
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
    
    
                int touchProgress = seekBar.getProgress();
                Log.d(TAG,"touchProgress------>"+touchProgress);
                //停止拖动
                if (mIPlayerControl != null) {
    
    
                    mIPlayerControl.seekTo(touchProgress);
                }
                isUserTouchProgressBar = false;//没用触摸到拖动条
            }
        });
        mPlayOrPause.setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View v) {
    
    
                //播放或暂停
                if(mIPlayerControl != null){
    
    
                    mIPlayerControl.playOrPause();
                }
            }
        });
        mClose.setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View v) {
    
    
                //点击关闭按钮
                //需要判空,否则容易崩掉
                if(mIPlayerControl != null) {
    
    
                    mIPlayerControl.stopPlay();
                }
            }
        });
    }

    //初始化各控件
    private void initView() {
    
    
        mSeekBar = (SeekBar) this.findViewById(R.id.seek_bar);
        mPlayOrPause = (Button) this.findViewById(R.id.play_or_pause_btn);
        mClose = (Button) this.findViewById(R.id.close_btn);
    }

    //释放服务
    @Override
    protected void onDestroy() {
    
    
        super.onDestroy();
        if(mPlayerConnection != null){
    
    
            mIPlayerControl.unRegisterViewController();//释放资源
            Log.d(TAG,"unbind service------>onDestroy");
            unbindService(mPlayerConnection);
        }
    }

    //ui状态
    private IPlayerViewControl mIPlayerViewControl = new IPlayerViewControl() {
    
    
        @Override
        public void onPlayStateChange(int state) {
    
    
            //根据播放状态来修改ui(按钮上显示的文字)
            switch (state){
    
    
                case PLAY_STATE_PLAY:
                    mPlayOrPause.setText("暂停");
                    break;
                case PLAY_STATE_PAUSE:
                case PLAY_STATE_STOP:
                    mPlayOrPause.setText("播放");
                    break;
            }
        }
        @Override
        public void onSeekChange(final int seek) {
    
    
            //在Android中progressBar、surfaceView控件可以用子线程更新(不是主线程但也没问题原因)
            Log.d(TAG,"current thread-----"+Thread.currentThread().getName());//从结果得出不是主线程
            //所以进行如下操作:
            runOnUiThread(new Runnable() {
    
    
                @Override
                public void run() {
    
    
                    //改变播放进度(拖动条)
                    if (!isUserTouchProgressBar) {
    
    
                        mSeekBar.setProgress(seek);
                    }
                }
            });
        }
    };
}

四、结果展示

可在后台运行。
在这里插入图片描述

五、补充

1、MediaPlayer播放音频与视频

2、seekBar属性详解

猜你喜欢

转载自blog.csdn.net/luck_ch09/article/details/113007061