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

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 远程调用服务 简单音乐播放实例的实现

时间: 2025-01-05 14:43:45

Android Service AIDL 远程调用服务 简单音乐播放实例的实现的相关文章

Android开发---MediaPlayer简单音乐播放器

Android开发-MediaPlayer简单音乐播放器 功能介绍 实现一个简单的播放器,类似网易云音乐形式,功能包括: 播放.暂停,停止,退出功能: 后台播放功能: 进度条显示播放进度.拖动进度条改变进度功能: 播放时图片旋转,显示当前播放时间功能: 界面样式 功能实现 1. MediaPlayer的实现 MediaPlayer常用方法介绍 MediaPlayer的实现包括初始化MediaPlayer,MediaPlayer的功能实现,包括播放.暂停.停止.离开等,具体细节如下: MediaP

Android 实现简单音乐播放器(一)

今天掐指一算,学习Android长达近两个月了,今天开始,对过去一段时间的学习收获以及遇到的疑难杂症做一些总结. 简单音乐播放器是我自己完成的第一个功能较为完整的APP,可以说是我的Android学习之路上的一个小小里程碑,给我增加了很多信心(~~真容易获得满足~~).从下面开始,我将详细介绍MusicPlayer的设计过程. 首先,先看一下这个项目的工程目录和运行效果:      从上面的图片看到,整个工程的布局文件有两个:activity_main.xml和musiclist.xml,其中,

Android——简单音乐播放器

使用MediaPlayer做的简单音乐播放器,更多内容请到百度经验查看   http://jingyan.baidu.com/article/60ccbceb63452364cab197f1.html <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:l

Android 实现简单音乐播放器(二)

在Android 实现简单音乐播放器(一)中,我介绍了MusicPlayer的页面设计. 现在,我将解析MusicPlayer的功能实现,就讲一些主要的点和有趣的细节,结合MainActivity.java代码进行说明(写出来可能有点碎……一向不太会总结^·^). 一.功能菜单 在MusicPlayer中,我添加了三个菜单: search(搜索手机中的音乐文件,更新播放列表). clear(清除播放列表……这个功能是最初加进去的,后来改进之后,已经没什么实际意义). exit(退出). menu

Android 4.3实现类似iOS在音乐播放过程中如果有来电则音乐声音渐小铃声渐大的效果

目前Android的实现是:有来电时,音乐声音直接停止,铃声直接直接使用设置的铃声音量进行铃声播放. Android 4.3实现类似iOS在音乐播放过程中如果有来电则音乐声音渐小铃声渐大的效果. 如果要实现这个效果,首先要搞清楚两大问题: 1.来电时的代码主要实现流程. 2.主流音乐播放器在播放过程中,如果有来电,到底在收到了什么事件后将音乐暂停了? 一:来电时的代码主要实现流程 我不是第一研究来电代码的人,网上已经有高手对这个流程剖析过,不是不完全符合我的要求,我参考过的比较有价值的是如下两个

Android开发6:Service的使用(简单音乐播放器的实现)

前言 啦啦啦~各位好久不见啦~博主最近比较忙,而且最近一次实验也是刚刚结束~ 好了不废话了,直接进入我们这次的内容~ 在这篇博文里我们将学习Service(服务)的相关知识,学会使用 Service 进行后台工作, 学会使用 Service 与 Activity 进行通信,并在此知识基础上学会使用 MediaPlayer和简单的多线程编程.使用 Handle 更新 UI,并设计成功一个简单的音乐播放器. 是不是很高大上呢~一起来学习~ 基础知识 Service作为Android四大组件之一,在每

远程调用服务里的方法service,进程间通信adil的学习

1当一个进程需要调用另外一个进程的方法时候,进程可以通过aidl文件以接口的方式将方法抛出.比如android没有对外提供挂电话的方法,若用户想要调用这个方法就必须与电话管理这个应用程序通信,调用挂电话的方法. 2.下面我就举例一个demo调用远程服务里的方法.为了验证service能否单独启动,这个demo启动了2个远程服务,一个有activity的一个只有service的.并且他们抛出的接口名字相同,正好学习一下同名的引用,发现一个java文件里只能import  1个同同名的类,若想调用另

Android开发实战之简单音乐播放器

最近开始学习音频相关.所以,很想自己做一个音乐播放器,于是,花了一天学习,将播放器的基本功能实现了出来.我觉得学习知识点还是蛮多的,所以写篇博客总结一下关于一个音乐播放器实现的逻辑.希望这篇博文对你的学习和生活有所帮助.效果图: **实现逻辑** 在市面上的音乐播放app,即时你关了.那么一样会在后台播放,所以播放的逻辑应该写在Service中.并且能够实现Service和Activity之间进行通信.那么Service是四大组件之一,所以在使用的时候一定不要忘了在配置文件中声明一下. <ser

Android Service学习之本地服务

Service是在一段不定的时间运行在后台,不和用户交互应用组件.每个Service必须在manifest中 通过来声明.可以通过contect.startservice和contect.bindserverice来启动. Service和其他的应用组件一样,运行在进程的主线程中.这就是说如果service需要很多耗时或者阻塞的操作,需要在其子线程中实现. service的两种模式(startService()/bindService()不是完全分离的): 本地服务 Local Service