Android 进程间通信实现原理分析
发布时间:2022-01-05 10:27:35
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。
下面将要讲到的这个例子来自:http://www.cnblogs.com/lonkiss/archive/2012/10/23/2735548.html
下面用一个客户端Activity操作服务端Service播放音乐的实例演示AIDL的使用。
服务端代码结构(左) 客户端代码结构(右)被标记的就是需要动手的。
服务端
新建一个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类。代码如下
/**
* 播放音乐的服务
*/
public class PlayerService extends Service {
public static final String TAG = "PlayerService";
private MediaPlayer mplayer;
@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;
}
// 实现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 boolean onUnbind(Intent intent) {
if (mplayer != null) {
mplayer.release();
}
Log.i(TAG,"service onUnbind");
return super.onUnbind(intent);
}
}
服务编写好以后,按照惯例在AndroidManifest.xml中加入声明,代码如下
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="pandafang.demo.playerserver"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="15" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<service android:name=".PlayerService" android:process=":remote">
<intent-filter >
<action android:name="com.example.playerserver.PlayerService"/>
</intent-filter>
</service>
</application>
</manifest>
需要加入的只是<service>...</service>那段,要注意的是 android:process=":remote" 和 intent-filter 。
运行服务端到设备上,准备给客户端调用
客户端
新建一个android application project,命名为PlayerClient。将服务端放有aidl文件的包直接copy到客户
端src目录下,保留包中的aidl文件,其他删除。
编写MainActivity.java 代码如下
/**
* 客户端控制界面
*/
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中注册声明了
运行客户端到设备,按下按钮可以播放/停止 效果如图
如果想对更加详细的实现原理进行研究,可以参见这篇文章:
http://www.cnblogs.com/over140/archive/2011/03/08/1976890.html


猜你喜欢
- 前言Feign是Netflix开源的声明式HTTP客户端,致力于让编写http client更加简单,Feign可以通过声明接口自动构造请求
- 一、树概念及结构1.1 树的概念树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因 为
- 在AndroidMenifest.xml中,常常会有下面的语句: <uses-sdk android:minSdkVersion=&q
- 一. 编写.cs文件注:要想编译dll中注释可用,则代码中的注释要用“ /// ” 来进行注释,否则
- 本人使用Android开发有一段时间了,但是本身没有系统学,而且多年专注服务端开发,总觉得因为项目需要接触Android移动端开发只是暂时的
- 本文实例为大家分享了C# DateTime预设可选的日期范围的相关代码,可以选择本年度、本季度、本月等,供大家参考,具体内容如下效果:大家在
- 本文实例为大家分享了java与微信小程序实现websocket长连接的具体代码,供大家参考,具体内容如下背景:需要在小程序实现地图固定坐标下
- spring配置不扫描service层原因我将contoller给springmvc进行扫描,然后其余所有交给spring扫描然后发现ser
- 下拉刷新对于一个app来说是必不可少的一个功能,在早期大多数使用的是chrisbanes的PullToRefresh,或是修改自该框架的其他
- 在 Spring Boot 中做权限管理,一般来说,主流的方案是 Spring Security ,但是,仅仅从技术角度来说,也可以使用 S
- 前言本节将介绍如何设置和使用MongoDB 驱动程序,通过 java实现与MongoDB服务端的通信功能,用户可以在此基础上进行各种Java
- ⛳️ 基本类型做形式参数(零散参数的数据接收)1、基本数据类型要求前台页面的表单输入框的name属性值与对应控制器方法中的形式参数名称与类型
- 题目给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。示例 1:输入: 123输出: 321 示例
- 本文实例讲述了Java使用备忘录模式实现过关类游戏功能。分享给大家供大家参考,具体如下:一.模式定义备忘录模式,在不破坏封闭的前提下,捕获一
- 本文实例为大家分享了Java实现坦克大战小游戏的具体代码,供大家参考,具体内容如下创作背景:n年前的学期末课题设计,从b站上学的,一个代码一
- 为此Android在appcompat-v7库中提供了一个NotificationCompat类来处理新老版本的兼容问题,我们在编写通知功能
- 一、表创建一、表创建//创建一个空表DataTable dt = new DataTable();//创建一个名为"Table_N
- 前端采用layui框架,讲解多文件上传的完整实现功能。前端html重点代码如下:<div class="layui-form
- 一 悬浮窗口特点:① 窗口一般较小,有时为不规则背景;② 置顶显示;③ 窗口支持拖动;④ 一般用于程序状态显示,比如显示下载进度;⑤ 一般支
- 一、Hash加密,使用HashAlgorithm哈希算法类的派生类(MD5、SHA1等)特点:只能加密,不可逆。可对目标信息生成一段特定长度