Android Service是分为两种:

  • 本地服务(Local Service): 同一个apk内被调用
  • 远程服务(Remote Service):被另一个apk调用

远程服务需要借助AIDL来完成。

AIDL 是什么

AIDL (Android Interface Definition Language) 是一种IDL 语言,用于生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication, IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参 数。

AIDL IPC机制是面向接口的,像COM或Corba一样,但是更加轻量级。它是使用代理类在客户端和实现端传递数据。

AIDL 的作用

由于每个应用程序都运行在自己的进程空间,并且可以从应用程序UI运行另一个服务进程,而且经常会在不同的进程间传递对象。在Android平台,一个进 程通常不能访问另一个进程的内存空间,所以要想对话,需要将对象分解成操作系统可以理解的基本单元,并且有序的通过进程边界。

  通过代码来实现这个数据传输过程是冗长乏味的,Android提供了AIDL工具来处理这项工作。

选择AIDL的使用场合

官方文档特别提醒我们何时使用AIDL是必要的:只有你允许客户端从不同的应用程序为了进程间的通信而去访问你的service,以及想在你的 service处理多线程。

如果不需要进行不同应用程序间的并发通信(IPC),you should create your interface by implementing a Binder;或者你想进行IPC,但不需要处理多线程的,则implement your interface using a Messenger。无论如何,在使用AIDL前,必须要理解如何绑定service——bindService。

下面用一个客户端Activity操作服务端Service播放音乐的实例演示AIDL的使用。

开发工具: eclipse 3.7(indigo)+ android sdk 4.1+ adt 20.0.2

服务端代码结构

服务端代码结构

客户端代码结构

客户端代码结构

被标记的就是需要动手的。

服务端

新建一个android application project,命名为PlayerServer。 在res下的raw文件夹里面放入一个音乐文件,我这里放入的是Delta Goodrem的《Lost Without You》片段。如果不存在raw这个文件夹就自己新建一个,命名为raw。这个文件夹在raw文件夹下,与layout文件夹平级。raw中的文件遵守标 识符的命名规则,不要出现中文和空格,多个单词可以用下划线连接。

新建一个IRemoteServiice.aidl 文件,加入如下代码:

package pandafang.demo.playerserver;  interface IRemoteService {      void play();      void stop();  }

可见aidl文件的代码跟java的interface一样,但是aidl中不能加public等修饰符。Ctrl + S 保存后 ADT 会根据这个IRemoteService.aidl文件自动生成IRemoteService.java文件。如同R.java文件一样在“gen/包 名”下,代码是自动生成的,不要手动修改。

接下来就是bound service(参考:官方文档)的知识了。IRemoteService.java 中有一个Stub静态抽象类extends Binder implements IRemoteService。自己动手写一个PlayerService 用来播放音乐,播放音乐需要使用android.media.MediaPlayer类。代码如下:

package pandafang.demo.playerserver;    import java.io.FileDescriptor;  import java.io.IOException;  import android.app.Service;  import android.content.Intent;  import android.media.MediaPlayer;  import android.os.IBinder;  import android.os.RemoteException;  import android.util.Log;    /**   * 播放音乐的服务   * @author Panda Fang   * @date 2012-10-22 10:15:33   */  public class PlayerService extends Service {            public static final String TAG = "PlayerService";            private MediaPlayer mplayer;            // 实现aidl文件中定义的接口      private IBinder mBinder = new IRemoteService.Stub() {                    @Override          public void stop() throws RemoteException {              try {                  if (mplayer.isPlaying()) {                      mplayer.stop();                  }              } catch (Exception e) {                  // TODO: handle exception                  e.printStackTrace();              }          }                    @Override          public void play() throws RemoteException {              try {                  if (mplayer.isPlaying()) {                      return;                  }                  // start之前需要prepare。                  // 如果前面实例化mplayer时使用方法一,则第一次play的时候直接start,不用prepare。                  // 但是stop一次之后,再次play就需要在start之前prepare了。                  // 前面使用方法二 这里就简便了, 不用判断各种状况                  mplayer.prepare();                  mplayer.start();              } catch (Exception e) {                  // TODO: handle exception                  e.printStackTrace();              }          }      };        @Override      public IBinder onBind(Intent intent) {          Log.i(TAG,"service onbind");          if(mplayer==null){              // 方法一说明              // 此方法实例化播放器的同时指定音乐数据源 ,若用此方法在,mplayer.start() 之前不需再调用mplayer.prepare()               // 官方文档有说明 :On success, prepare() will already have been called and must not be called again.              // 译文:一旦create成功,prepare已被调用,勿再调用 。查看源代码可知create方法内部已经调用prepare方法。              // 方法一开始              // mplayer = MediaPlayer.create(this, R.raw.lost);              // 方法一结束                            // 方法二说明              // 若用此方法,在mplayer.start() 之前需要调用mplayer.prepare()               // 方法二开始              mplayer = new MediaPlayer();              try {                  FileDescriptor fd = getResources().openRawResourceFd(R.raw.lost).getFileDescriptor(); // 获取音乐数据源                  mplayer.setDataSource(fd); // 设置数据源                  mplayer.setLooping(true); // 设为循环播放              } catch (IOException e) {                  // TODO Auto-generated catch block                  e.printStackTrace();              }              // 方法二结束              Log.i(TAG,"player created");          }          return mBinder;      }            @Override      public boolean onUnbind(Intent intent) {          if (mplayer != null) {              mplayer.release();          }          Log.i(TAG,"service onUnbind");          return super.onUnbind(intent);      }  }

服务编写好以后,按照惯例在AndroidManifest.xml中加入声明,代码如下:

需要加入的只是...那段,要注意的是 android:process=":remote" 和 intent-filter 。

运行服务端到设备上,准备给客户端调用。

客户端

新建一个android application project,命名为PlayerClient。将服务端放有aidl文件的包直接copy到客户端src目录下,保留包中的aidl文件,其他删除。

编辑 layout 下的 activity_main.xml 布局文件,加入两个按钮,代码如下:

编写MainActivity.java 代码如下:

package pandafang.demo.playerclient;    import pandafang.demo.playerserver.IRemoteService;  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.IBinder;  import android.os.RemoteException;  import android.util.Log;  import android.view.Menu;  import android.view.View;  import android.view.View.OnClickListener;  import android.widget.Button;    /**   * 客户端控制界面   * @author Panda Fang   * @date 2012-10-22 10:36:44   */  public class MainActivity extends Activity {            public static final String TAG = "MainActivity";            // 服务端 AndroidManifest.xml中的intent-filter action声明的字符串      public static final String ACTION = "com.example.playerserver.PlayerService";            private Button playbtn, stopbtn;            private IRemoteService mService;            private boolean isBinded = false;            private ServiceConnection conn = new ServiceConnection() {                    @Override          public void onServiceDisconnected(ComponentName name) {              isBinded = false;              mService = null;          }                    @Override          public void onServiceConnected(ComponentName name, IBinder service) {              mService = IRemoteService.Stub.asInterface(service);              isBinded = true;          }      };        @Override      public void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.activity_main);                    doBind();          initViews();      }            private void initViews() {          playbtn = (Button) findViewById(R.id.button1);          stopbtn = (Button) findViewById(R.id.button2);          playbtn.setOnClickListener(clickListener);          stopbtn.setOnClickListener(clickListener);      }      @Override      public boolean onCreateOptionsMenu(Menu menu) {          getMenuInflater().inflate(R.menu.activity_main, menu);          return true;      }            @Override      protected void onDestroy() {          doUnbind();          super.onDestroy();      }            public void doBind() {          Intent intent = new Intent(ACTION);          bindService(intent, conn, Context.BIND_AUTO_CREATE);      }            public void doUnbind() {          if (isBinded) {              unbindService(conn);              mService = null;              isBinded = false;          }                }      private OnClickListener clickListener = new OnClickListener() {                    @Override          public void onClick(View v) {              if (v.getId() == playbtn.getId()) {                  // play                  Log.i(TAG,"play button clicked");                  try {                      mService.play();                  } catch (RemoteException e) {                      // TODO Auto-generated catch block                      e.printStackTrace();                  }              } else {                  // stop                  Log.i(TAG,"stop button clicked");                  try {                      mService.stop();                  } catch (RemoteException e) {                      // TODO Auto-generated catch block                      e.printStackTrace();                  }              }          }      };    }

MainActivity是根据向导自动生成的,不需要在AndroidManifest.xml中注册声明了。

运行客户端到设备,按下按钮可以播放/停止 效果如图:

Android Service AIDL 远程调用服务 - 简单音乐播放实例

更多相关文章

  1. android listview继承BaseAdapter,自定义的适配器,getView方法执
  2. Android开发常用代码片段(三)
  3. Android 性能优化之Java(Android)代码优化 (三)
  4. Android常用代码之普通及系统权限静默安装APK
  5. 【Android 应用开发】Android资源文件 - 使用资源存储字符串 颜
  6. android 布局文件属性说明
  7. Android 属性文件build.prop,获取属性以及设置属性
  8. 另一个更简单的Android应用程序全屏的方法
  9. Android获取打开各种文件Intent汇总

随机推荐

  1. Android的内存机制和溢出说明
  2. [Android]你不知道的Android进程化(3)--
  3. Android内存阀值修改--内存回收机制配置
  4. 电子书 android高薪之路-android程序员面
  5. Android中的GalleryView实例演示-周末福
  6. android listview局部刷新和模拟应用下载
  7. Android天气预报详解
  8. android Ant混淆打包遇到的几个问题 SDK
  9. android实现系统的返回键和home键
  10. Android Studio下Java Jni技术