AIDL(Android Interface Definition Language)技术的作用主要是用来在Android中进行进程间通信的。
我们的需求是这样的:
第一、我们知道在AndroId中如果需要进行音乐播放,最方便的方法就是使用自带的MediaPlayer对象,如果我们在Activity中控制MediaPlayer对象进行播放,那么一旦你打开了另外一个程序譬如浏览器,那么歌声就会立刻停止,这当然不是我们需要的结果。 我们需要的是在做其他事情的同时能够在后台听歌,于是我们就需要把对MediaPlayer对象的操作放在后台Service中去。
第三、我们需要在Activity中操作Service中的MediaPlayer对象,就好像这个对象是自己的一样。我们可以采用Android接口定义语言 AIDL(Android Interface Definition Language)技术
下面就详细讲解实现方法
谷歌官方说明地址:http://developer.android.com/guide/components/aidl.html
定义一个AIDL接口一共有三步:
This file defines the programming interface with method signatures.
The Android SDK tools generate an interface in the Java programming language, based on your .aidl
file. This interface has an inner abstract class named Stub
that extends Binder
and implements methods from your AIDL interface. You must extend the Stub
class and implement the methods.
Implement a Service
and override onBind()
to return your implementation of the Stub
class.
// IRemoteService.aidl package com.example.android; // Declare any non-default types here with import statements /** Example service interface */ interface IRemoteService { /** Request the process ID of this service, to do evil things with it. */ int getPid(); /** Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString); }IRemoteService就是AIDL接口的名字,getPid(),basicTypes(....)就是方法的声明。这个文件编译完成后,ADT会为我们在gen目录下自动生成相同包名的(此处为com.example.android)IRemoteService.java的文件
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() { public int getPid(){ return Process.myPid(); } public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) { // Does nothing } };
IRemoteService mIRemoteService;
private ServiceConnection mConnection = new ServiceConnection() {
// Called when the connection with the service is established
public void onServiceConnected(ComponentName className, IBinder service) {
// Following the example above for an AIDL interface,
// this gets an instance of the IRemoteInterface, which we can use to call on the service
mIRemoteService = IRemoteService.Stub.asInterface(service);
}
// Called when the connection with the service disconnects unexpectedly
public void onServiceDisconnected(ComponentName className) {
Log.e(TAG, "Service has unexpectedly disconnected");
mIRemoteService = null;
}
};
这样,我们就可以在客户端调用Service中的方法了,如,mIRemoteService.basicTypes(...)
package com.mjook007.aidl; interface ServicePlayer{ void play(); void pause(); void stop(); int getDuration();//歌曲总时间 int getCurrentTime();//当前播放时间 void setCurrent(int cur);//快进,快退。。 boolean isPlaying();//是否处于播放状态 }
package com.mjook007.service; import java.io.File; import android.app.Service; import android.content.Intent; import android.media.MediaPlayer; import android.net.Uri; import android.os.Environment; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import com.mjook007.aidl.ServicePlayer; import com.mjook007.model.Mp3Info; public class PlayService extends Service { private Mp3Info info = null; MediaPlayer mediaPlayer = null; ServicePlayer.Stub stub = new ServicePlayer.Stub() { @Override public void stop() throws RemoteException { if (mediaPlayer != null) { mediaPlayer.stop(); mediaPlayer.release(); mediaPlayer = null; Log.v("PlayService", "停止"); } } @Override public void setCurrent(int cur) throws RemoteException { if (mediaPlayer != null) { mediaPlayer.seekTo(cur); } } @Override public void play() throws RemoteException { if (mediaPlayer == null) { String path = getMp3Path(info); mediaPlayer = MediaPlayer.create(PlayService.this, Uri.parse("file://" + path)); } if (!isPlaying()) { mediaPlayer.setLooping(false); mediaPlayer.start(); Log.v("PlayService", "播放"); } } @Override public void pause() throws RemoteException { if (mediaPlayer != null) { if (isPlaying()) { mediaPlayer.pause(); Log.v("PlayService", "暂停"); } else { mediaPlayer.start(); Log.v("PlayService", "播放"); } } } @Override public boolean isPlaying() throws RemoteException { boolean b = false; if (mediaPlayer != null) { b = mediaPlayer.isPlaying(); } return b; } @Override public int getDuration() throws RemoteException { int i = 0; if (mediaPlayer != null) { i = mediaPlayer.getDuration(); } return i; } @Override public int getCurrentTime() throws RemoteException { int cur = 0; if (mediaPlayer != null) { cur = mediaPlayer.getCurrentPosition(); } return cur; } }; @Override public IBinder onBind(Intent intent) { Log.v("onBind", "!!!!!!!!!!!!!!!!!!!!"); info = (Mp3Info) intent.getSerializableExtra("mp3Info"); String path = getMp3Path(info); Log.v("service--path", path); mediaPlayer = MediaPlayer.create(this, Uri.parse("file://" + path)); return stub; } @Override public void onCreate() { super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { // 此处返回值可控制服务在被KILL后是否重启 return super.onStartCommand(intent, flags, startId); } private String getMp3Path(Mp3Info info) { String SDCardRoot = Environment.getExternalStorageDirectory() .getAbsolutePath(); String path = SDCardRoot + File.separator + "mp3" + File.separator + info.getMp3Name(); return path; } }
package com.mjook007.mp3player; import com.mjook007.aidl.ServicePlayer; import com.mjook007.model.Mp3Info; import com.mjook007.service.PlayService; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; 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.view.View.OnClickListener; import android.widget.Button; import android.widget.SeekBar; import android.widget.TextView; public class Mp3PlayerActivity extends Activity implements OnClickListener { private Button startBt; private Button pauseBt; private Button stopBt; private SeekBar seekBar; private TextView cur;// 当前播放时间 private TextView dur;// 歌曲总长度 // MediaPlayer mediaPlayer = null; Mp3Info info = null; boolean isPlaying = false; private static Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { // cur.setText("当前播放时间" + msg.arg1); }; }; private ServicePlayer sPlayer; private ServiceConnection sConnection = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { } @Override public void onServiceConnected(ComponentName name, IBinder service) { sPlayer = ServicePlayer.Stub.asInterface(service); Log.v("onServiceConnected", "connected"); } }; private Runnable updateThread = new Runnable() { @Override public void run() { if (sPlayer != null) {// 不执行null Check 会出现NullPointException // 执行了sPlayer的null Check 但依然会出现空指针错误,why?????????? try { int seconds = sPlayer.getCurrentTime() / 1000; int minutes = seconds / 60; seconds = seconds % 60; cur.setText("当前播放时间" + minutes + ":" + seconds + "播放状态" + sPlayer.isPlaying()); dur.setText("歌曲总长度:" + sPlayer.getDuration()); // 更新进度条 seekBar.setMax(sPlayer.getDuration()); seekBar.setProgress(sPlayer.getCurrentTime()); } catch (RemoteException e) { Log.e("Error", "Unable to get Time------------->>"); } } mHandler.postDelayed(updateThread, 500);// 将线程延迟加入handler处理 } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.player); Intent intent = getIntent(); info = (Mp3Info) intent.getSerializableExtra("mp3Info"); startBt = (Button) findViewById(R.id.start); pauseBt = (Button) findViewById(R.id.pause); stopBt = (Button) findViewById(R.id.stop); cur = (TextView) findViewById(R.id.cur); dur = (TextView) findViewById(R.id.dur); seekBar = (SeekBar) findViewById(R.id.seekbar01); seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onStopTrackingTouch(SeekBar seekBar) { Log.v("SeekBar", "停止调节"); try { if (sPlayer != null) { sPlayer.setCurrent(seekBar.getProgress()); } } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onStartTrackingTouch(SeekBar seekBar) { Log.v("SeekBar", "开始调节"); } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } }); startBt.setOnClickListener(this); pauseBt.setOnClickListener(this); stopBt.setOnClickListener(this); intent = new Intent(Mp3PlayerActivity.this, PlayService.class); intent.putExtra("mp3Info", info); bindService(intent, sConnection, Context.BIND_AUTO_CREATE); startService(intent); mHandler.post(updateThread); } @Override protected void onDestroy() { super.onDestroy(); doUnbindService(); } void doUnbindService() { unbindService(sConnection); } @Override public void onClick(View v) { // Intent intent = new Intent(); // intent.setClass(this, PlayService.class); if (sPlayer != null) { try { isPlaying = sPlayer.isPlaying(); } catch (RemoteException e) { e.printStackTrace(); } } switch (v.getId()) { case R.id.start: try { sPlayer.play(); } catch (RemoteException e) { e.printStackTrace(); } break; case R.id.pause: try { sPlayer.pause(); } catch (RemoteException e) { e.printStackTrace(); } break; case R.id.stop: try { sPlayer.stop(); } catch (RemoteException e) { e.printStackTrace(); } break; } } }