1、参考自:https://b23.tv/0mHcF5
2、内容如果有不对的,希望可以指出或补充。
3、由于这部分卡了很久,也算是个完整的练习,所以单独提出来了。
通信方式:
1、本地服务通信:是指应用程序内部
的通信,需要使用IBinder对象进行本地服务。
2、远程服务通信:是指两个应用程序间
的通信,远程服务通信是通过AIDL(Android Interface Definition Language,Android接口定义语言,定义了客户端与服务端的一个标准)实现的。
这里测试的是本地服务通信。
以下都是在Android Studio4.1.1里进行的。
1、准备
activity_play.xml
<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、接口
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属性详解