** 跨进程的音乐播放器,是通过客户端来控制服务端播放音乐的。所以我们在开发之前先想好我们接下来要开发的思路。**
我说一下我的开发流程,
第一步,先完成服务端播放音乐的功能。但是这里有一个问题,当你把服务端的音乐播放功能写好之后,不方便验证音乐播放的功能是否完善。-------------怎么解决呢?我是这样处理的,在当前项目先不写Service,先在MainActivity中完成一个正儿八经的音乐播放器,实现音乐播放功能之后,再把音乐播放功能从MainActivity中移植到Service中,然后再把MainActivity中的代码注释掉,一个有着音乐播放器功能的服务端就完成了。。。这样会为后来客户端的调试减少了很多工作量。
第二步,Service端音乐播放功能完成了之后,就需要来开发客户端了。首先完成客户端的UI,也就是XML文件,在这里就可以把你之前正儿八经的音乐播放器上写的XML直接copy过来就可以了,各种TextView,Button的初始化和监听也都免了,很方便。但是由于是客户端,一切的实际功能是由服务端来完成的,所以我们在客户端的MainActivity中只保留各种控件的交互就好了。
第三步,最关键的一步来了,建造一条跨越两个程序的‘桥梁’——aidl跨进程通信。完成之后就开始各种的调试吧(ps:由于刚入这行,,所以代码的逻辑很混乱,编着编着就乱了,调了好久,请教了导师,总算是完成了(~ - ~))。

android 音频播放器工具类_xml

上面是大概的流程图

大概思路就分享到这里,如果我的设计思路有问题或者有更好的设计思路,非常欢迎在评论里探讨!

我们要实现这个播放器可能会用到的知识: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中去。如下图所示。

android 音频播放器工具类_xml_02


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里都要出现。而且要必须一样,包名都要一样。

android 音频播放器工具类_android_03

上图是服务端的包结构

android 音频播放器工具类_音乐播放_04

上图为客户端的包结构
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>