AIDL(Android Interface Definition Language)技术的作用主要是用来在Android中进行进程间通信的。

我们的需求是这样的:

第一、我们知道在AndroId中如果需要进行音乐播放,最方便的方法就是使用自带的MediaPlayer对象,如果我们在Activity中控制MediaPlayer对象进行播放,那么一旦你打开了另外一个程序譬如浏览器,那么歌声就会立刻停止,这当然不是我们需要的结果。 我们需要的是在做其他事情的同时能够在后台听歌,于是我们就需要把对MediaPlayer对象的操作放在后台Service中去。


第二、我们已经把对MediaPlayer的操作转移到Service中去了,按照我们以前的做法,我们在Activity中发送一个Intent对象给Service对象,在Intent中传送播放啊、暂停啊一类的信息给Service,这样Service就知道该怎么做了。这一切看起来很美好,可是现在出了一个新问题,那就是我想在Activity中显示一个进度条,这个进度条要跟着Service中的MediaPlayer中的歌曲进度同步向前走,而且如果我点击进度条中的某一个位置,还想让歌曲跳转到新的时间点继续播放,这个,该怎么实现?

第三、我们需要在Activity中操作Service中的MediaPlayer对象,就好像这个对象是自己的一样。我们可以采用Android接口定义语言 AIDL(Android Interface Definition Language)技术


下面就详细讲解实现方法

谷歌官方说明地址:http://developer.android.com/guide/components/aidl.html

定义一个AIDL接口一共有三步:

  1. Create the .aidl file

    This file defines the programming interface with method signatures.

  2. Implement the interface

    The Android SDK tools generate an interface in the Java programming language, based on your.aidlfile. This interface has an inner abstract class namedStubthat extendsBinderand implements methods from your AIDL interface. You must extend theStubclass and implement the methods.

  3. Expose the interface to clients

    Implement aServiceand overrideonBind()to return your implementation of theStubclass.

1.创建.aidl文件:这个文件定义了程序接口,里面包含方法的声明。例如

// IRemoteService.aidlpackage 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的文件

2.实现接口

SDK会根据你的.aidl文件来生成一个接口(既IRemoteService.java),这个接口中含有一个继承自Binder的内部抽象类叫Stub,Stub还实现了.aidl中声明的方法,使用时必须继承Stub类并实现其中的方法。例如:
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    }};

3.交给客户端使用该接口

要想让客户端使用该接口,你必须把mBinder放到一个继承了Service的服务类中,实现onBind方法,并返回mBinder。在客户端(例如一个Activity)声明一个ServiceConnection变量,并使用内部类重写onServiceConnected和onServiceDisconnected方法,在声明一个IRemoteService变量mIRemoteService,在onServiceConnected(ComponentName c,IBinder service)方法中对该变量进行赋值,其值为IRemoteService.Stub.asInterface(service),例如
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(...)
关于如何在进程之间通过IPC机制来传递Java类,我会在下篇博客中讲解!!!
=========================================================================================== 下面我们就运用AIDL技术来实现我们上面提到的需求
首先创建一个ServicePlayer.aidl文件
package com.mjook007.aidl;interface ServicePlayer{void play();void pause();void stop();int getDuration();//歌曲总时间int getCurrentTime();//当前播放时间void setCurrent(int cur);//快进,快退。。boolean isPlaying();//是否处于播放状态}

然后创建一个PlayService的类,该类继承自Service,并重写了onBind方法
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() {@Overridepublic void stop() throws RemoteException {if (mediaPlayer != null) {mediaPlayer.stop();mediaPlayer.release();mediaPlayer = null;Log.v("PlayService", "停止");}}@Overridepublic void setCurrent(int cur) throws RemoteException {if (mediaPlayer != null) {mediaPlayer.seekTo(cur);}}@Overridepublic 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", "播放");}}@Overridepublic void pause() throws RemoteException {if (mediaPlayer != null) {if (isPlaying()) {mediaPlayer.pause();Log.v("PlayService", "暂停");} else {mediaPlayer.start();Log.v("PlayService", "播放");}}}@Overridepublic boolean isPlaying() throws RemoteException {boolean b = false;if (mediaPlayer != null) {b = mediaPlayer.isPlaying();}return b;}@Overridepublic int getDuration() throws RemoteException {int i = 0;if (mediaPlayer != null) {i = mediaPlayer.getDuration();}return i;}@Overridepublic int getCurrentTime() throws RemoteException {int cur = 0;if (mediaPlayer != null) {cur = mediaPlayer.getCurrentPosition();}return cur;}};@Overridepublic 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;}@Overridepublic void onCreate() {super.onCreate();}@Overridepublic 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;}}

这里需要注意的是,我们Stub内部类中要用到的mediaPlayer对象一定要在onBind之前的生命周期里进行初始化!
最后,我们在Activity中进行使用
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() {@Overridepublic void handleMessage(Message msg) {// cur.setText("当前播放时间" + msg.arg1);};};private ServicePlayer sPlayer;private ServiceConnection sConnection = new ServiceConnection() {@Overridepublic void onServiceDisconnected(ComponentName name) {}@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {sPlayer = ServicePlayer.Stub.asInterface(service);Log.v("onServiceConnected", "connected");}};private Runnable updateThread = new Runnable() {@Overridepublic 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处理}};@Overrideprotected 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() {@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {Log.v("SeekBar", "停止调节");try {if (sPlayer != null) {sPlayer.setCurrent(seekBar.getProgress());}} catch (RemoteException e) {e.printStackTrace();}}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {Log.v("SeekBar", "开始调节");}@Overridepublic 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); }@Overridepublic 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;}}}

别忘了,Activity和Service都需要在AndroidManifest中进行声明

源码下载(只包含了AIDL相关的类):http://download.csdn.net/detail/mjook007/5376437

更多相关文章

  1. Android接口回调总结,以及运用到弹窗PopWindow的Demo实现
  2. Ubuntu Linux下android源码下载方法
  3. ANDROID STRINGS.XML的特殊字符_安卓STRING.XML添加空格或字符的
  4. android中TextView属性方法总结
  5. Android 链式调用(方法链)
  6. Android Activity之间切换出现短暂黑屏的处理方法
  7. Android开机启动Activity或者Service方法
  8. 【Android 异步操作】AsyncTask 异步任务 ( 参数简介 | 方法简介

随机推荐

  1. Android底部导航栏组件:BottomNavigationB
  2. Android电源管理分析
  3. Android(安卓)View动画——Alpha、scale
  4. 2011.09.13(2)——— android 图标上面添加
  5. Android(安卓)LCD:LCD基本原理篇
  6. android扫一扫 二维码显示结果中文乱码
  7. Android7.0 设置音量最小时有声音输出
  8. 读取Activity/Processes的PID(Android)
  9. Android如何拍照或选择图片并裁剪
  10. listview 问题总结