基於在綁定Service時,與Service實現綁定的組件可以歸屬於不同的應用程序,因此可以實現跨進程的綁定,進而實現通信功能。在跨進程的綁定中,需要使用AIDL來定義接口,本博客將詳細的介紹這種跨進程綁定、通信的方式,由於使用AIDL定義接口時,默認可用的數據類型非常有限。本博客還會介紹如何在AIDL中使用自定義的數據類型,及Parcelable接口的使用方式。
AIDL
進程間的通信方式
AIDL的創建
- AIDL文件的擴展名爲.aidl,而普通java接口爲.java.(我們只需要像創建普通接口那樣先創建,然後把擴展名改爲.aidl即可)
- aidl不允許用任何訪問權限修飾符去修飾該接口,它默認被修飾爲public的。
interface IPlayerAIDL {
void play();
void pause();
void previous();
void next();
}
在創建了AIDL接口後,開發工具會自動在gen下創建匹配的java文件。
使用AIDL遠程綁定Service
服務端的開發步驟:
- 開發AIDL接口的源文件,並保證擴展名爲.aidl。
- 在Service中開發AIDL接口的實現類並實現接口內定義的抽象方法。
- 實現Service的onBind()方法,並將AIDL接口類的實現對象作爲該方法的返回值。
- 註冊Service類,並配置intent-filter(確保該Service可以被隱式激活)
- 部署服務端應用程序到設備上
客戶端的開發步驟:
- 將服務端的AIDL相關文件複製到客戶端中(相關包名類名不可更改)
- 開發ServiceConnection接口的實現類
- 使用隱式意圖綁定服務端的Service
- 在ServiceConnection實現類的onServiceConnected方法中,通過Stub的asInterface方法將IBinder對象轉換爲AIDL接口實現類對象
public class PlayerService extends Service {
private MediaPlayer player;//定義播放器
private int currentPosition;//當前的播放位置
private int musicPosition=0;
String[] musics=new String[]{//歌曲的存放路徑
Environment.getExternalStorageDirectory().getAbsolutePath()+"/Music/d.mp3",
Environment.getExternalStorageDirectory().getAbsolutePath()+"/Music/b.mp3",
Environment.getExternalStorageDirectory().getAbsolutePath()+"/Music/c.mp3",
Environment.getExternalStorageDirectory().getAbsolutePath()+"/Music/a.mp3",
};
@Override
public void onCreate() {
player=new MediaPlayer();
//監聽一首歌是否播放完成
player.setOnCompletionListener(new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
//完成後播放下一首
doNext();
}
});
super.onCreate();
}
private void doPlay(){
player.reset();
try {
player.setDataSource(musics[musicPosition]);
player.prepare();
//播放前先到上次播放位置
player.seekTo(currentPosition);
player.start();
currentPosition=0;
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private void doPause(){
if(player.isPlaying()){
currentPosition=player.getCurrentPosition();
player.pause();
}
}
private void doPrevious(){
musicPosition--;
if(musicPosition<0){
//第一首上一首爲最後一首
musicPosition=musics.length-1;
}
currentPosition=0;
doPlay();
}
private void doNext(){
musicPosition++;
if(musicPosition>=musics.length){
//最後一首下一首爲第一首
musicPosition=0;
}
currentPosition=0;
doPlay();
}
@Override
public IBinder onBind(Intent intent) {
return new InnerBinder();
}
private class InnerBinder extends IPlayerAIDL.Stub{
@Override
public void play() throws RemoteException {
doPlay();
}
@Override
public void pause() throws RemoteException {
doPause();
}
@Override
public void previous() throws RemoteException {
doPrevious();
}
@Override
public void next() throws RemoteException {
doNext();
}
}
@Override
public void onDestroy() {
player.release();
player=null;
super.onDestroy();
}
}
IPlayerAIDL.aidl
interface IPlayerAIDL {
void play();
void pause();
void previous();
void next();
}
AndroidManifest.xml中添加如下代碼:<service android:name=".PlayerService">
<intent-filter>
<action android:name="android.intent.action.PLAYMUSIC_SERVICE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
<!-- 添加讀sd卡的權限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
客戶端代碼如下:
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.wanghongxiang.playmusicclient.MainActivity" >
<ImageButton
android:id="@+id/imageButton2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/imageButton1"
android:layout_below="@+id/imageButton1"
android:layout_marginTop="21dp"
android:onClick="pause"
android:src="@android:drawable/ic_media_pause" />
<ImageButton
android:id="@+id/imageButton3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/imageButton2"
android:layout_centerVertical="true"
android:onClick="next"
android:src="@android:drawable/ic_media_next" />
<ImageButton
android:id="@+id/imageButton1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="26dp"
android:layout_marginTop="63dp"
android:onClick="play"
android:src="@android:drawable/ic_media_play" />
<ImageButton
android:id="@+id/imageButton4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/imageButton3"
android:layout_below="@+id/imageButton3"
android:layout_marginTop="14dp"
android:onClick="previous"
android:src="@android:drawable/ic_media_previous" />
</RelativeLayout>
MainActivity.javapublic class MainActivity extends Activity {
private ServiceConnection conn;
private IPlayerAIDL player;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent=new Intent("android.intent.action.PLAYMUSIC_SERVICE");
conn=new InnerServiceConnection();
bindService(intent, conn, BIND_AUTO_CREATE);
}
private class InnerServiceConnection implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
player=IPlayerAIDL.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
public void play(View v){
try {
player.play();
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void pause(View v){
try {
player.pause();
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void next(View v){
try {
player.next();
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void previous(View v){
try {
player.previous();
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
unbindService(conn);
super.onDestroy();
}
}
關於AIDL使用自定義數據類型傳輸可以參考這裏-------------- AIDL自定義數據類型