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. 使用AIDL远程调用服务中的方法

    AIDL:android interface define language(接口定义语言) 作用:方便远程调用其他服务中的方法 注意:安卓四大组件都要在清单文件注册 aidl创建图: AIDL的全称 ...

  2. Android -- service 利用广播调用服务的方法

    1. 实现原理,在Service里面注册一个广播接收者, 想要调用的时候app发送出广播, 后台的service里面的广播接收者接收到广播,并调用service里面的方法. 2. 示例代码 MainA ...

  3. Android实现简单音乐播放器(MediaPlayer)

    Android实现简单音乐播放器(MediaPlayer) 开发工具:Andorid Studio 1.3 运行环境:Android 4.4 KitKat 工程内容 实现一个简单的音乐播放器,要求功能 ...

  4. Android实现简单音乐播放器(startService和bindService后台运行程序)

    Android实现简单音乐播放器(MediaPlayer) 开发工具:Andorid Studio 1.3运行环境:Android 4.4 KitKat 工程内容 实现一个简单的音乐播放器,要求功能有 ...

  5. 如何从零开始实现一个soa远程调用服务基础组件

    说起soa远程调用基础组件,最著名的莫过于淘宝的dubbo了,目前很多的大型互联网公司都有一套自己的远程服务调用分布式框架,或者是使用开源的(例如dubbo),或者是自己基于某种协议(例如hessia ...

  6. 架构设计:远程调用服务架构设计及zookeeper技术详解(下篇)

    一.下篇开头的废话 终于开写下篇了,这也是我写远程调用框架的第三篇文章,前两篇都被博客园作为[编辑推荐]的文章,很兴奋哦,嘿嘿~~~~,本人是个很臭美的人,一定得要截图为证: 今天是2014年的第一天 ...

  7. 架构设计:一种远程调用服务的设计构思(zookeeper的一种应用实践)

    在深入学习zookeeper我想先给大家介绍一个和zookeeper相关的应用实例,我把这个实例命名为远程调用服务.通过对这种应用实例的描述,我们会对zookeeper应用场景会有深入的了解. 远程调 ...

  8. spring boot / cloud (八) 使用RestTemplate来构建远程调用服务

    spring boot / cloud (八) 使用RestTemplate来构建远程调用服务 前言 上周因家里突发急事,请假一周,故博客没有正常更新 RestTemplate介绍: RestTemp ...

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

    在Android 实现简单音乐播放器(一)中,我介绍了MusicPlayer的页面设计. 现在,我简单总结一些功能实现过程中的要点和有趣的细节,结合MainActivity.java代码进行说明(写出 ...

随机推荐

  1. Ecshop ajax 局部刷新购物车功能

    1.比如我们category.dwt 里有 <a href='flow.php'><SPAN id='cart_count_all'>{insert name='cart_in ...

  2. wifi详解(一)

    1        WLAN技术 WLAN是英文WirelessLAN的缩写,就是无线局域网的意思.无线以太网技术是一种基于无线传输的局域网技术,与有线网络技术相比,具有灵活.建网迅速.个人化等特点.将 ...

  3. 构建属于自己的ORM框架之二--IQueryable的奥秘

    上篇文章标题乱起,被吐槽了,这次学乖了. 上篇文章中介绍了如何解析Expression生成对应的SQL语句,以及IQueryable的一些概念,以及我们所搭建的框架的思想等.但还没把它们结合并应用起来 ...

  4. C++标准库开发心得

    最近放弃MFC,改用C++标准库开发产品.毕竟MFC用熟了,马上改用STL还不太习惯.下面列出下总结的改用STL遇到的问题和解决办法: 1.清除空格 remove_if(iterBegin, iter ...

  5. UE 使用技巧

    一.关于正则表达式的使用 删除空行: 替换 %[ ^t]++^p 为 空串 替换回车换行符:替换^p 为 空串 删除行尾空格: 替换 [ ^t]+$ 为 空串 删除行首空格: 替换 %[ ^t]+ 为 ...

  6. 《Python CookBook2》 第一章 文本 - 检查字符串中是否包含某字符集合中的字符 && 简化字符串的translate方法的使用

    检查字符串中是否包含某字符集合中的字符  任务: 检查字符串中是否出现了某个字符集合中的字符 解决方案: 方案一: import itertools def containAny(seq,aset): ...

  7. login:用户登陆的意思

    login:用户登陆的意思 在思科的设备上有两种登录方式: 一种是本地方式,使用console口: 一种是远程方式(或者叫做网络方式):使用的是telnet等 1.默认情况下,思科的远程访问是禁止的. ...

  8. BS与CS的联系与区别

    C/S是Client/Server的缩写.服务器通常采用高性能的PC.工作站或小型机,并采用大型数据库系统,如Oracle.Sybase.InFORMix或SQL Server.客户端需要安装专用的客 ...

  9. Windows server 2008 上部署 MVC (NopCommerce 3.4)网站

    自己用开源框架做了个商城,该框架是基于mvc4的,本地编译通过,运行一切正常,关于发布遇到了好几个问题. 本地: IIS7.5. VS2013 总结后发现只需要设置两个问题,就不会有那些古怪的问题:什 ...

  10. [Hive - LanguageManual] Describe

    Describe Describe Database Describe Table/View/Column Display Column Statistics Describe Partition D ...