** 跨进程的音乐播放器,是通过客户端来控制服务端播放音乐的。所以我们在开发之前先想好我们接下来要开发的思路。**
我说一下我的开发流程,
第一步,先完成服务端播放音乐的功能。但是这里有一个问题,当你把服务端的音乐播放功能写好之后,不方便验证音乐播放的功能是否完善。-------------怎么解决呢?我是这样处理的,在当前项目先不写Service,先在MainActivity中完成一个正儿八经的音乐播放器,实现音乐播放功能之后,再把音乐播放功能从MainActivity中移植到Service中,然后再把MainActivity中的代码注释掉,一个有着音乐播放器功能的服务端就完成了。。。这样会为后来客户端的调试减少了很多工作量。
第二步,Service端音乐播放功能完成了之后,就需要来开发客户端了。首先完成客户端的UI,也就是XML文件,在这里就可以把你之前正儿八经的音乐播放器上写的XML直接copy过来就可以了,各种TextView,Button的初始化和监听也都免了,很方便。但是由于是客户端,一切的实际功能是由服务端来完成的,所以我们在客户端的MainActivity中只保留各种控件的交互就好了。
第三步,最关键的一步来了,建造一条跨越两个程序的‘桥梁’——aidl跨进程通信。完成之后就开始各种的调试吧(ps:由于刚入这行,,所以代码的逻辑很混乱,编着编着就乱了,调了好久,请教了导师,总算是完成了(~ - ~))。
上面是大概的流程图
大概思路就分享到这里,如果我的设计思路有问题或者有更好的设计思路,非常欢迎在评论里探讨!
我们要实现这个播放器可能会用到的知识:UI界面的TextView,Button,ListView的属性(需要用来规划播放器的界面),bind(简单来说aidl就是靠bind来连接的,总之很关键),AIDL(AIDL是进程间通信的一种方式),Parcelable接口(需要这个接口来在AIDL中传递一些原本AIDL不支持的数据类型,比如Music自定义类),自定义的adapter类(如果简单点用ArrayAdapter,这里使用的是MyAdapter的自定义类),MediaPlayer(支持音乐播放)。
使用
按照第一步走,先完成音乐播放器功能,先不管Service
先创建MainActivity.java,创建生成新的project。
接下来写音乐播放器的功能。我先建立了一个Music自定义类,其中定义一首歌的各种属性,比如:musicName,artist(作者),id(歌曲标识符),durtion(歌曲时长)。
Music.java
package com.rickdu.android.playaudiotest;
import java.util.List;
public class Music implements Parcelable {
private int id = 0; //标识码
private int duration = 0;//时长
private String artist = null;//演唱者
private String musicName = null;//音乐名
private String data;//文件路径
public Music() {
}
public Music(Parcel in) {
id = in.readInt();
musicName = in.readString();
artist = in.readString();
duration = in.readInt();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getDuration() {
return duration;
}
public void setDuration(int duration) {
this.duration = duration;
}
public String getArtist() {
return artist;
}
public void setArtist(String artist) {
this.artist = artist;
}
public String getMusicName() {
return musicName;
}
public void setMusicName(String musicName) {
this.musicName = musicName;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
@Override
public String toString() {
return "Music{" +
"id=" + id +
", duration=" + duration +
", artist='" + artist + '\'' +
", musicName='" + musicName + '\'' +
", data='" + data + '\'' +
'}';
}
下面写界面UI
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:orientation="vertical"
tools:context=".MainActivity">
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="6" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_weight="1">
<TextView
android:id="@+id/current_music"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"/>
<TextView
android:id="@+id/current_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="right"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_weight="1"
android:orientation="horizontal">
<Button
android:id="@+id/provious"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="30dp"
android:text="上一首" />
<Button
android:id="@+id/control"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="播放" />
<Button
android:id="@+id/next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:layout_marginLeft="30dp"
android:text="下一首" />
</LinearLayout>
</LinearLayout>
接下来,开始写自定义Adapter(适配器),为什么要用自定义的适配器呢?---------------------------为了界面更好看(> _ <),好吧这是一个因素,更重要的是可以根据自己的意愿去创建数据和数据的布局样式。使用方式灵活,编码逻辑清晰,如果我们要添加或者修改的话,会更方便。
在后面写客户端的时候,也要记得在客户端里也要加上MyAdapter.java 和Music.java
MyAdapter.java
package com.rickdu.android.playaudiotest;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import java.util.List;
public class MyAdapter extends BaseAdapter {
private Context context;
private LayoutInflater layoutInflater;
private List<Music> list;
public MyAdapter(Context context, List<Music> list) {
this.context = context;
layoutInflater = LayoutInflater.from(context);
this.list = list;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int positon) {
return this.list.get(positon);
}
@Override
public long getItemId(int position) {
return position;
}
class MusicHolder {
TextView MusicId ;
TextView MusicName ;
TextView MusicArtist;
TextView MusicDurtion;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// Music music = list.get(position);
if(convertView == null){
convertView = layoutInflater.inflate(R.layout.music_item, null);
}
MusicHolder holder;
if (convertView.getTag() != null) {
holder = (MusicHolder) convertView.getTag();
} else {
holder = new MusicHolder();
holder.MusicId = (TextView) convertView.findViewById(R.id.item_number);
holder.MusicName = (TextView) convertView.findViewById(R.id.music_name);
holder.MusicArtist = (TextView) convertView.findViewById(R.id.artist);
holder.MusicDurtion = (TextView)convertView.findViewById(R.id.durtion);
}
convertView.setTag(holder);
int msec = list.get(position).getDuration();
holder.MusicId.setText(list.get(position).getId()+"");
holder.MusicName.setText(list.get(position).getMusicName()+"");
holder.MusicArtist.setText(list.get(position).getArtist()+"");
holder.MusicDurtion.setText(((msec)/1000)/60+":"+(msec)/10000+"");
return convertView;
}
}
可以看到最后面这个getView方法,它的作用是把music的各种属性放到这个叫Music_item.xml的布局中去,Music_item.xml则是要放入activity_main.xml中去。如下图所示。
Music_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="80dp">
<TextView
android:id="@+id/item_number"
android:layout_width="50dp"
android:layout_height="match_parent"
android:text="0"
android:layout_weight="1"
android:gravity="center|center_horizontal"/>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="5">
<TextView
android:id="@+id/music_name"
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_weight="1"
android:text="null"
android:gravity="left|center"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="30dp">
<TextView
android:id="@+id/artist"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="null"
android:gravity="left|center"/>
<TextView
android:id="@+id/durtion"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="right|center"
android:layout_weight="1"
android:text="00:00"
android:gravity="right|center"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
然后我们继续修改MainActivity中的代码
我们继续会运用到contentResolver,,通过对Cursor实例化,从SD卡中得到路径,歌曲名,作者名,歌曲时长等等数据。对contentResolver和cursor不熟悉的同学,可以先去了解一哈~
然后我们把获取到的数据存入到Music中,并且把整个Music
添加到List这个自定义的列表当中,这样,一首歌的所有数据就存到list列表中了。for循环之后,查询出来的所有音频文件都会存到列表中了。
下面的代码是进行读取和存的操作的。
private void getSong() {
contentResolver = getContentResolver();
mCursor = contentResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
new String[]{
MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.DURATION,
MediaStore.Audio.Media.DATA,
MediaStore.Audio.Media.DISPLAY_NAME},
null,
null,
null);
for (int i = 0; i < mCursor.getCount(); i++) {
Music music = new Music();
mCursor.moveToNext();
if (mCursor.getInt(mCursor.getColumnIndex(MediaStore.Audio.Media.DURATION)) < 400000 && mCursor.getInt(mCursor.getColumnIndex(MediaStore.Audio.Media.DURATION)) > 150000) {
music.setId(id);
music.setDuration(mCursor.getInt(mCursor.getColumnIndex(MediaStore.Audio.Media.DURATION)));
music.setArtist(mCursor.getString(mCursor.getColumnIndex(MediaStore.Audio.Media.ARTIST)));
music.setData(mCursor.getString(mCursor.getColumnIndex(MediaStore.Audio.Media.DATA)));
music.setMusicName(mCursor.getString(mCursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME)));
musicList.add(music);
id++;
}
}
}
现在我们已经从SD卡中读取到了音乐文件,通过MediaPlayer,就可以通过播放器来进行各种操作了。这里就不多说了,直接上代码吧
MainActivity.java
package com.rickdu.android.playaudiotest;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.rickdu.android.playaudiotest.Music;
import android.Manifest;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private MediaPlayer mediaPlayer = new MediaPlayer();
private List<Music> musicList = new ArrayList<>();
private ContentResolver contentResolver;
private int id = 1;
private Cursor mCursor;
private int currentMusicIndex = -1;
private Button controlBu;
private Button proviousBu;
private Button nextBu;
private ListView musicLv;
private TextView currentMusicTv;
private TextView currentProgressTv;
private Intent intent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
confirmPermission();
setMusicAdapter();
setListListener();
intent = new Intent(MainActivity.this, MusicService.class);
startService(intent);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.control:
if (currentMusicIndex == -1) {
currentMusicIndex = 0;
initMediaPlayer();
controlBu.setText("暂停");
} else {
controlMusic();
}
break;
case R.id.provious:
if (currentMusicIndex == 0) {
currentMusicIndex = musicList.size() - 1;
controlBu.setText("暂停");
initMediaPlayer();
} else {
controlBu.setText("暂停");
currentMusicIndex--;
initMediaPlayer();
}
break;
case R.id.next:
if (currentMusicIndex == musicList.size() - 1) {
currentMusicIndex = 0;
controlBu.setText("暂停");
initMediaPlayer();
} else {
controlBu.setText("暂停");
currentMusicIndex++;
initMediaPlayer();
}
break;
default:
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
}
stopService(intent);
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case 1:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
initMediaPlayer();
} else {
Toast.makeText(this, "拒绝权限将无法使用程序", Toast.LENGTH_SHORT).show();
finish();
}
break;
default:
break;
}
}
private void setMusicAdapter() {
MyAdapter myAdapter = new MyAdapter(MainActivity.this, musicList);
musicLv.setAdapter(myAdapter);
}
private void setListListener() {
musicLv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
currentMusicIndex = position;
initMediaPlayer();
controlBu.setText("暂停");
}
});
}
private void confirmPermission() {
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 {
Log.d("init", "is ready");
getSong();
}
}
private void init() {
controlBu = (Button) findViewById(R.id.control);
proviousBu = (Button) findViewById(R.id.provious);
nextBu = (Button) findViewById(R.id.next);
controlBu.setOnClickListener(this);
proviousBu.setOnClickListener(this);
nextBu.setOnClickListener(this);
currentMusicTv = (TextView) findViewById(R.id.current_music);
currentProgressTv = (TextView) findViewById(R.id.current_progress);
musicLv = (ListView) findViewById(R.id.list_view);
}
private void initMediaPlayer() {
try {
mediaPlayer.reset();
mediaPlayer.setDataSource(musicList.get(currentMusicIndex).getData());
mediaPlayer.prepare();
mediaPlayer.start();
currentMusicTv.setText(musicList.get(currentMusicIndex).getMusicName() + musicList.get(currentMusicIndex).getArtist());
Log.d("prepare", "initMediaPlayer: ");
} catch (Exception e) {
e.printStackTrace();
}
}
private void controlMusic() {
if (!mediaPlayer.isPlaying()) {
controlBu.setText("暂停");
mediaPlayer.start();
} else {
mediaPlayer.pause();
controlBu.setText("播放");
}
}
private void getSong() {
contentResolver = getContentResolver();
mCursor = contentResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
new String[]{
MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.DURATION,
MediaStore.Audio.Media.DATA,
MediaStore.Audio.Media.DISPLAY_NAME},
null,
null,
null);
for (int i = 0; i < mCursor.getCount(); i++) {
Music music = new Music();
mCursor.moveToNext();
if (mCursor.getInt(mCursor.getColumnIndex(MediaStore.Audio.Media.DURATION)) < 400000 && mCursor.getInt(mCursor.getColumnIndex(MediaStore.Audio.Media.DURATION)) > 150000) {
music.setId(id);
music.setDuration(mCursor.getInt(mCursor.getColumnIndex(MediaStore.Audio.Media.DURATION)));
music.setArtist(mCursor.getString(mCursor.getColumnIndex(MediaStore.Audio.Media.ARTIST)));
music.setData(mCursor.getString(mCursor.getColumnIndex(MediaStore.Audio.Media.DATA)));
// music.setDuration();
music.setMusicName(mCursor.getString(mCursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME)));
musicList.add(music);
id++;
}
}
}
}
到这里,一个基本的音乐播放器就完成了
接下来就是把这个功能移植到服务端了
服务端(这里就不展示移植的操作了,直接一步到位了)
MusicService.java
package com.rickdu.android.playaudiotest;
import android.app.Service;
import android.content.ContentResolver;
import android.content.Intent;
import android.database.Cursor;
import android.media.MediaPlayer;
import android.os.IBinder;
import android.os.RemoteException;
import android.provider.MediaStore;
import android.util.Log;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
public class MusicService extends Service {
public static final String TAG = MusicService.class.getSimpleName();
private ContentResolver contentResolver;
private Cursor mCursor;
private List<Music> musicList = new ArrayList<>();
private MediaPlayer mediaPlayer = new MediaPlayer();
int id = 0;
private int currentMusicIndex = -1;
@Override
public void onCreate() {
Log.d(TAG, "onCreate: ");
getSong();
}
@Override
public void onDestroy() {
super.onDestroy();
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind:is onCreate ");
return new MusicBinder();
}
class MusicBinder extends MusicInterService.Stub {
@Override
public void play() throws RemoteException {
MusicService.this.play();
Log.d(TAG, "is play in binder");
}
@Override
public void pause() throws RemoteException {
MusicService.this.pause();
}
@Override
public void next() throws RemoteException {
MusicService.this.next();
}
@Override
public void provious() throws RemoteException {
MusicService.this.provious();
}
@Override
public boolean isplaying() throws RemoteException {
return MusicService.this.isplaying();
}
@Override
public List<Music> getList() throws RemoteException {
Log.d(TAG, "MusicService getList");
return musicList;
}
@Override
public void id(int MusicIndex) {
Log.d(TAG, "idin is onCreate id = " + currentMusicIndex);
Musicid(MusicIndex);
}
}
public void Musicid(int musicIndex) {
currentMusicIndex = musicIndex;
Log.d(TAG, "Musicid: musicIndex is " + musicIndex);
initMediaPlayer();
}
public void play() {
Log.d(TAG, "is play in MusicService");
Log.d(TAG, "play: mediaPlayer.getCurrentPosition " + mediaPlayer.getCurrentPosition());
if (currentMusicIndex == -1) {
currentMusicIndex = 0;
initMediaPlayer();
return;
}
if (!mediaPlayer.isPlaying()) {
mediaPlayer.start();
}
}
public void pause() {
mediaPlayer.pause();
}
public void provious() {
if (currentMusicIndex == 0) {
currentMusicIndex = musicList.size() - 1;
initMediaPlayer();
} else {
currentMusicIndex--;
initMediaPlayer();
}
}
public void next() {
if (currentMusicIndex == musicList.size() - 1) {
currentMusicIndex = 0;
initMediaPlayer();
} else {
currentMusicIndex++;
initMediaPlayer();
}
}
public boolean isplaying() {
return mediaPlayer.isPlaying();
}
private void initMediaPlayer() {
try {
Log.d(TAG, "initMediaPlayer: is begin ");
mediaPlayer.reset();
Log.d(TAG, "initMediaPlayer: reset is begin");
mediaPlayer.setDataSource(musicList.get(currentMusicIndex).getData());
Log.d(TAG, "initMediaPlayer: setDataSource is begin");
mediaPlayer.prepare();
Log.d(TAG, "initMediaPlayer: prepare is begin");
mediaPlayer.start();
Log.d(TAG, "initMediaPlayer: start is begin");
// currentMusicTv.setText(musicList.get(currentMusicIndex).getMusicName() + musicList.get(currentMusicIndex).getArtist());
Log.d(TAG, "initMediaPlayer:currentMusicIndex is " + currentMusicIndex);
} catch (Exception e) {
e.printStackTrace();
}
}
private void getSong() {
Log.d(TAG, "getSong: ");
contentResolver = getContentResolver();
mCursor = contentResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
new String[]{
MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.DURATION,
MediaStore.Audio.Media.DATA,
MediaStore.Audio.Media.DISPLAY_NAME},
null,
null,
null);
for (int i = 0; i < mCursor.getCount(); i++) {
Music music = new Music();
mCursor.moveToNext();
if (mCursor.getInt(mCursor.getColumnIndex(MediaStore.Audio.Media.DURATION)) < 400000 && mCursor.getInt(mCursor.getColumnIndex(MediaStore.Audio.Media.DURATION)) > 150000) {
music.setId(id);
music.setDuration(mCursor.getInt(mCursor.getColumnIndex(MediaStore.Audio.Media.DURATION)));
music.setArtist(mCursor.getString(mCursor.getColumnIndex(MediaStore.Audio.Media.ARTIST)));
music.setData(mCursor.getString(mCursor.getColumnIndex(MediaStore.Audio.Media.DATA)));
// music.setDuration();
music.setMusicName(mCursor.getString(mCursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME)));
musicList.add(music);
id++;
}
}
}
}
最关键的重写方法是onbind,它是服务端这边桥梁连接的着力点。
客户端
新建一个项目ControlMusic,总体来说,客户端只有UI控制的功能。这里的XML布局就用服务端的activity_main.xml。我们想要在控制界面上显示歌曲列表,就需要接收来自服务端的歌曲的数据。
package com.rickdu.android.controlmusic;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListView;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
import com.rickdu.android.playaudiotest.Music;
import com.rickdu.android.playaudiotest.MusicInterService;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "MycontrolMusic";
private Button playBu;
private Button pauseBu;
private Button proviousBu;
private Button nextBu;
private ListView musicLv;
private TextView currentMusicTv;
private TextView currentProgressTv;
private SeekBar seekSb;
private MusicInterService musicInterService;
private Intent intent;
private List<Music> list = new ArrayList<>();
private int currentMusicIndex = -1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
bind();
setListener();
}
private void showList() {
try {
list = musicInterService.getList();
MyAdapter myAdapter = new MyAdapter(MainActivity.this, list);
musicLv.setAdapter(myAdapter);
} catch (RemoteException re) {
re.printStackTrace();
}
}
public void bind() {
intent = new Intent();
intent.setAction("com.rickdu.android.MusicService");
intent.setPackage("com.rickdu.android.playaudiotest");
Log.d("bind", "start");
bindService(intent, connection, BIND_AUTO_CREATE);
Log.d("bind", "success");
}
ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
musicInterService = MusicInterService.Stub.asInterface(iBinder);
showList();
Log.d("bind", "is onServiceConnected");
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.d("bind", "is onServiceDisconnected");
musicInterService = null;
}
};
private void setListener(){
musicLv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
currentMusicIndex = position;
int duration = list.get(currentMusicIndex).getDuration();
currentMusicTv.setText(list.get(currentMusicIndex).getMusicName()+" "+list.get(currentMusicIndex).getArtist());
Log.d(TAG, "onItemClick: is begin ");
Log.d(TAG, "onItemClick: currentMusicIndex is " + currentMusicIndex);
try {
musicInterService.id(currentMusicIndex);
Log.d(TAG, "onItemClick: musicInterService.id is work");
}catch (RemoteException e){
e.printStackTrace();
Log.d(TAG, "onItemClick: musicInrerService is broke");
}
}
});
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.play:
play();
break;
case R.id.pause:
pause();
break;
case R.id.provious:
provious();
break;
case R.id.next:
next();
break;
default:
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(connection);
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case 1:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// initMediaPlayer();
} else {
Toast.makeText(this, "拒绝权限将无法使用程序", Toast.LENGTH_SHORT).show();
finish();
}
break;
default:
break;
}
}
private void play(){
if (musicInterService != null) {
try {
currentMusicTv.setText(list.get(currentMusicIndex).getMusicName()+" "+list.get(currentMusicIndex).getArtist());
//
musicInterService.play();
Log.d(TAG, "is work in Control playBu");
} catch (RemoteException e) {
e.printStackTrace();
}
} else {
Log.d(TAG, "musicInterService is null");
}
}
private void pause(){
try {
musicInterService.pause();
} catch (RemoteException e) {
e.printStackTrace();
}
}
private void provious(){
try {
musicInterService.provious();
currentMusicIndex--;
currentMusicTv.setText(list.get(currentMusicIndex).getMusicName()+" "+list.get(currentMusicIndex).getArtist());
} catch (RemoteException e) {
e.printStackTrace();
}
}
private void next(){
try {
musicInterService.next();
currentMusicIndex++;
currentMusicTv.setText(list.get(currentMusicIndex).getMusicName()+" "+list.get(currentMusicIndex).getArtist());
} catch (RemoteException e) {
e.printStackTrace();
}
}
private void init() {
playBu = (Button) findViewById(R.id.play);
pauseBu = (Button) findViewById(R.id.pause);
proviousBu = (Button) findViewById(R.id.provious);
nextBu = (Button) findViewById(R.id.next);
playBu.setOnClickListener(this);
pauseBu.setOnClickListener(this);
proviousBu.setOnClickListener(this);
nextBu.setOnClickListener(this);
currentMusicTv = (TextView) findViewById(R.id.current_music);
currentProgressTv = (TextView) findViewById(R.id.current_progress);
musicLv = (ListView) findViewById(R.id.list_view);
}
}
AIDL
AIDL作为跨进程通信的桥梁,要在两个project里都要出现。而且要必须一样,包名都要一样。
上图是服务端的包结构
上图为客户端的包结构
musicInterService.aidl
// MusicInterService.aidl
package com.rickdu.android.playaudiotest;
// Declare any non-default types here with import statements
import com.rickdu.android.playaudiotest.Music;
interface MusicInterService {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void play();
void pause();
void provious();
void next();
boolean isplaying();
List<Music> getList();
void id( int id);
int currentMusicPosition();
void seek(int progress);
}
Music.aidl
// Music.aidl
package com.rickdu.android.playaudiotest;
// Declare any non-default types here with import statements
parcelable Music;
Music.aidl是为了在跨进程通信时,可以把带有Music信息的List传递过去。
最后别忘了在服务器端AndroidManifest.xml中修改权限和Service的属性
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.rickdu.android.playaudiotest">
<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">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".MusicService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.rickdu.android.MusicService"/>
</intent-filter>
</service>
</application>
</manifest>