Android实战技术:IPC方式简介教程
非实时,通知性的方式
第一种方式就是Intent,Intent可以非常方便的通讯,但是它是非实时的,无法进行实时的像函数调用那样的实时的通讯。

实时的函数调用
但是IPC的根本目的还是为了实现函数的调用,即使是传递数据也是要通过函数调用的方式,为什么呢?因为程序运行总是要知道状态,要有逻辑上的行为,因此必须通讯函数才能体现出行为。
IPC的机制除了进程,或者说不同的应用程序之间进行通讯,同时也能够让不同的组件之间进行像普通对象那样进行实时的调用。因为Android的组件都是由系统框架统一的进行构建和销毁,所以你就无法创建对象,因此,就没有办法像普通的对象那样进行组合或者聚合,从而也就没有办法进行直接调用。但是IPC的方式就可以让Activity/Service获取另外一个Service对象的引用,从而直接调用其上的方法。
还有,就是这些IPC方法总是产生客户/服务端模式的调用,也即是客户端组件(Activity/Service)持有服务端Service的组件,只能是客户端主动调用服务端的方法,服务端无法反过来调用客户端的方法,因为IPC的另一端Service无法获取客户端的对象。
文档对此的描述。
有三种方式可以进行IPC通讯:
1. 直接使用Binder对象
缺点是这种方式不能进行跨进程,跨应用程序的函数调用。只能实现在同一个进程之中,同一个应用程序之中的不同的组件之间通讯。
优点就是这种方式使用起来特别简单,对于公开出来的方法没有任何的限制,可以传递任何的对象,甚至是回调等等。
总体上来讲如果不需要跨进程,这是最好的实现方式,可以完全实现像普通对象之间的组合和聚合。但是这里,最好不要像Android文档中的示例那样,直接返回Service对象,因为Service对象会有很多Public的方法,如果直接返回Service对象会导致公开很多不必须的方法,一旦Client端调用这些方法,将导致奇怪的现象和Bug,一个方法就是用Proxy对Service对象进行封装,只公开需要的接口。
示例:
service:
public class BinderPrinterService extends Service {
private static final String TAG = "PlayerService";
private IBinder mBinder;
@Override
public void onCreate() {
mBinder = new ProxyService(this);
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public void print(String msg, TextView tv) {
try {
Log.e(TAG, "Preparing printer...");
tv.setText("Preparing printer...");
Thread.sleep(1000);
Log.e(TAG, "Connecting printer...");
tv.setText("Connecting printer...");
Thread.sleep(1000);
Log.e(TAG, "Printing.... " + msg);
tv.setText("Printing.... ");
Thread.sleep(1000);
Log.e(TAG, "Done");
} catch (InterruptedException e) {
}
tv.setText(msg);
Toast.makeText(this, "Printing is done.", Toast.LENGTH_SHORT).show();
}
}
class ProxyService extends Binder {
private BinderPrinterService mService;
public ProxyService(BinderPrinterService svc) {
mService = svc;
}
public void print(String msg, TextView tv) {
mService.print(msg, tv);
}
}
client:
public class BinderClientActivity extends Activity {
ProxyService mService;
private TextView mStatusPanel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.printer_activity);
setTitle("Binder client Activity");
mStatusPanel = (TextView) findViewById(R.id.status);
((Button) findViewById(R.id.play)).setText("Print via extending Binder");
}
@Override
protected void onStart() {
super.onStart();
doBindService();
}
private void doBindService() {
Intent intent = new Intent(this, BinderPrinterService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
doUnbindService();
}
private void doUnbindService() {
if (mService != null) {
unbindService(mConnection);
}
}
public void onButtonClick(View v) {
if (mService == null) {
return;
}
mService.print("Tree of liberty must be refreshed from time to time with blood of patroits and tyrants",
mStatusPanel);
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
mService = (ProxyService) service;
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
mService = null;
}
};
}
2. 使用Messenger对象
这是利用了消息循环队列来进行通讯的,也就是说服务端封装成了一个Handler,客户端向这个Handler发送Message,服务端再处理这个Message从而实现通讯。
优点,最突出的优点就是它可以保证线程上的安全,或者说时序上的安全。因为客户端是向Handler发送消息,而不是直接的函数调用,所以呢?Message都是按先后的顺序放到服务端的消息队列当中,然后再由服务端Service决定如何处理这些Message。因为直接的函数调用会导致被调用的函数也会出现在调用者的线程之中,也就是说被调用到的函数也会继续存在于调用者的调用栈中,因此有可能产生线程的问题。而Messenger方式,并不是直接的函数调用,而是仅向Service的Handler发送一个Message,然后调用者就此返回,其调用栈也就此停止,Service可以选择如何处理这一个Message。Messenger方式即可以在同一个进程之中,也可以跨进程实现真正的IPC。
但是它的缺点也是相当的明显的,就是它是发送一个Message对象,而不是直接的函数调用,所以非常的不直观,另外,Message对象也无法方便的携带过多的参数,如果超过一个对象,只能封装打包成一个对象然后再放到Message.obj中。需要注意的是,如果是在同一个进程之中,Message可以携带任何对象,但如果是跨进程,则Message.obj中存放的必须是实现了Parcelable接口的对象,否则无法实现IPC,会有Exception。还有一个缺点,就是Message对象的标识(Message.what)必须是Client端和Service端都定义一致,否则就无法通讯,这在调试的时候必须注意,因为这不会有编译或者运行时错误,但就是无法正常工作,是比较隐蔽的Bug。
示例:
Service:
public class MessengerPrinterService extends Service {
static final int MSG_PRINT = 1;
private static final String TAG = "PrinterService";
private Handler mServiceHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_PRINT:
print("Freedom is nothing but a chance to be better!", (TextView) msg.obj);
break;
default:
super.handleMessage(msg);
}
}
};
final Messenger mMessenger = new Messenger(mServiceHandler);
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
public void print(String msg, TextView tv) {
try {
Log.e(TAG, "Preparing printer...");
if (tv != null) {
tv.setText("Preparing printer...");
}
Thread.sleep(1000);
Log.e(TAG, "Connecting printer...");
if (tv != null) {
tv.setText("Connecting printer...");
}
Thread.sleep(1000);
Log.e(TAG, "Printing.... " + msg);
if (tv != null) {
tv.setText("Printing.... ");
}
Thread.sleep(1000);
Log.e(TAG, "Done");
} catch (InterruptedException e) {
}
if (tv != null ) {
tv.setText(msg);
}
Toast.makeText(this, "Messenger Printing is done.", Toast.LENGTH_LONG).show();
}
}
Local client(in the same application):
public class MessengerClientActivity extends Activity {
Messenger mService = null;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = new Messenger(service);
}
public void onServiceDisconnected(ComponentName className) {
mService = null;
}
};
public void onButtonClick(View v) {
if (mService == null) return;
// Create and send a message to the service, using a supported 'what' value
Message msg = Message.obtain(null, MessengerPrinterService.MSG_PRINT, 0, 0);
msg.obj = findViewById(R.id.status);
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.printer_activity);
setTitle("Messenger client Activity");
((Button) findViewById(R.id.play)).setText("Print via constructing Messenger");
}
@Override
protected void onStart() {
super.onStart();
bindService(new Intent(this, MessengerPrinterService.class), mConnection,
Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
if (mService != null) {
unbindService(mConnection);
}
}
}
远端的Client(在另外一个应用程序进程里面):
public class AnotherMessengerClientActivity extends Activity {
private static final int MSG_PRINT = 1;
Messenger mService = null;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = new Messenger(service);
}
public void onServiceDisconnected(ComponentName className) {
mService = null;
}
};
public void onButtonClick(View v) {
if (mService == null) return;
// Create and send a message to the service, using a supported 'what' value
Message msg = Message.obtain(null, MSG_PRINT, 0, 0);
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.printer_activity);
setTitle("Another Messenger client Activity");
((Button) findViewById(R.id.play)).setText("Print via constructing Messenger");
}
@Override
protected void onStart() {
super.onStart();
Intent intent = new Intent();
intent.setClassName("com.example.effectiveandroid", "com.example.effectiveandroid.MessengerPrinterService");
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
if (mService != null) {
unbindService(mConnection);
}
}
}
3. 使用AIDL进程通讯
这是最正统的IPC方式,实际上Messenger的跨进程通讯的底层也是通过AIDL来实现的。它的优点就是完全是为跨进程而设计的,接口由AIDL文件指定,Client端和Service端都通过AIDL所描述的接口来进行调用和实现,不容易出错。需要注意的是接口中的所有的对象都必须实现了Parcelable接口,即使仅仅是在同一个进程之中。它的缺点就是必须要在每一个想要通讯的地方定义一个完全一样的AIDL文件,否则IPC不会成功,而且它是严格的按照IPC的机制来的,所以即使是在同一进程之内,所有的接口的参数和返回值的对象必须是实现了Parcelable接口的,但Binder对象和Messenger就无此限制。需要一点:定义AIDL会严格按照IPC的方式进程即使是在同一个进程之中。所以,除非是真的要跨进程通讯,否则不要使用AIDL。
AIDL文件:
package com.example.effectiveandroid;
interface PrinterInterface {
void print(String msg);
}
AIDL Service:
public class AIDLPrinterService extends Service {
private static final String TAG = "AIDLPrinterService";
private Handler mHandler = new Handler();
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private PrinterInterface.Stub mBinder = new PrinterInterface.Stub() {
@Override
public void print(String msg) throws RemoteException {
AIDLPrinterService.this.print(msg);
}
};
public void print(String msg) {
try {
Log.e(TAG, "Preparing printer...");
Thread.sleep(1000);
Log.e(TAG, "Connecting printer...");
Thread.sleep(1000);
Log.e(TAG, "Printing.... " + msg);
Thread.sleep(1000);
Log.e(TAG, "Done");
} catch (InterruptedException e) {
}
mHandler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(AIDLPrinterService.this, "via AIDL Printing is done.", Toast.LENGTH_LONG).show();
}
});
}
}
Local client:
public class AIDLClientActivity extends Activity {
private static final String TAG = "PrinterClientActivity";
PrinterInterface mService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.printer_activity);
setTitle("Local AIDL client Activity");
((Button) findViewById(R.id.play)).setText("Print via AIDL");
}
@Override
protected void onStart() {
super.onStart();
doBindService();
}
private void doBindService() {
Intent intent = new Intent(this, AIDLPrinterService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
doUnbindService();
}
private void doUnbindService() {
if (mService != null) {
unbindService(mConnection);
}
}
public void onButtonClick(View v) {
if (mService == null) {
Log.e(TAG, "what the fucl service is not ready");
return;
}
try {
mService.print("This message is from local client via AIDL interface");
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
mService = PrinterInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
mService = null;
}
};
}
client in another application process:
public class AnotherAIDLClientActivity extends Activity {
private static final String TAG = "PrinterClientActivity";
PrinterInterface mService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.printer_activity);
setTitle("Another AIDL client Activity");
((Button) findViewById(R.id.play)).setText("Print via AIDL");
}
@Override
protected void onStart() {
super.onStart();
doBindService();
}
private void doBindService() {
Intent intent = new Intent();
intent.setClassName("com.example.effectiveandroid", "com.example.effectiveandroid.AIDLPrinterService");
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
doUnbindService();
}
private void doUnbindService() {
if (mService != null) {
unbindService(mConnection);
}
}
public void onButtonClick(View v) {
if (mService == null) {
Log.e(TAG, "what the fucl service is not ready");
return;
}
try {
mService.print("call PrinterService via AIDL from another application");
} catch (RemoteException e) {
e.printStackTrace();
}
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
mService = PrinterInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
mService = null;
}
};
}
结论
对比且总结了一下:如果是在同一个进程(应用程序)之中,且不涉及复杂的线程模式,直接使用Binder对象方式是最方便快捷的;如果是涉及跨进程操作,且不涉及复杂的线程模式就使用AIDL方式;无论是同一进程内还是跨进程,如果涉及比较复杂的线程模式就推荐使用Messenger的方式。
Android实战技术:IPC方式简介教程的更多相关文章
- Android项目依赖库管理方式简介
在实际的android项目开发过程中,我们一般都会用一些现有的第三方库来实现我们的需求,避免重复造轮子.普遍使用到的,例如:网络请求库.图片处理库.界面UI库(自定义View.动画效果等).各种第三方 ...
- Android中的IPC机制
Android IPC简介 IPC是Inter-Process Communication的缩写,含义就是进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程.那么什么是进程,什么是线程,进程 ...
- 【Android实战开发】3G技术和Android发展简介
随着移动设备的不断普及和发展,相关软件的开发也越来越受到人们的关注,其中要提及的就是Android开发.本系列博客主要为大家介绍Android的开发,可能会有人问:现在互联网上已经有很多的Androi ...
- 《Android NFC 开发实战详解 》简介+源码+样章+勘误ING
<Android NFC 开发实战详解>简介+源码+样章+勘误ING SkySeraph Mar. 14th 2014 Email:skyseraph00@163.com 更多精彩请直接 ...
- 【转】Android中实现IPC的几种方式详细分析及比较
1.使用Bundle ----> 用于android四大组件间的进程间通信android的四大组件都可使用Bundle传递数据 所以如果要实现四大组件间的进程间通信 完全可以使用Bundl ...
- Android实战简易教程-第三十九枪(第三方短信验证平台Mob和验证码自己主动填入功能结合实例)
用户注冊或者找回password时通常会用到短信验证功能.这里我们使用第三方的短信平台进行验证实例. 我们用到第三方短信验证平台是Mob,地址为:http://mob.com/ 一.注冊用户.获取SD ...
- Android实战简易教程-第四十枪(窃听风云之短信监听)
近期在做监听验证码短信自己主动填入的功能,无意间想到了一个短信监听的办法. 免责声明:短信监听本身是一种违法行为,这里仅仅是技术描写叙述.请大家学习技术就可以.(哈哈) 本实例是基于bmob提供的后台 ...
- Android进阶笔记18:选用合适的IPC方式
1. 相信大家都知道Android进程间通信方式很多,比如AIDL.Messenger等等,接下来我就总结一下这些IPC方式优缺点. 2. IPC方式的优缺点和适用场景 3. 附加:使用Intent实 ...
- Android进阶笔记15:选用合适的IPC方式
1. 相信大家都知道Android进程间通信方式很多,比如AIDL.Messenger等等,接下来我就总结一下这些IPC方式优缺点. 2. IPC方式的优缺点和适用场景 3. 附加:使用Intent实 ...
随机推荐
- jquery 设置select的默认值
<select id="sel" > <option value="s1" > aaaa </option> <opt ...
- Mojo Mysql utf-8字符集 需要{mysql_enable_utf8 => 1}
get '/api/log_mon/get_log' => sub{ my $c = shift; my $env = $c->param('env'); my $host = $c-&g ...
- System单元对所有与COM相关的声明就这么多,需要倒背如流
是首先是VM表,但是和COM相关的函数地址都废弃了,这几个VM函数具体放在哪里,还得在研究: { Virtual method table entries } vmtSelfPtr = -; vmtI ...
- java之jvm学习笔记十三(jvm基本结构)
java之jvm学习笔记十三(jvm基本结构) 这一节,主要来学习jvm的基本结构,也就是概述.说是概述,内容很多,而且概念量也很大,不过关于概念方面,你不用担心,我完全有信心,让概念在你的脑子里变成 ...
- VC++ 视频播放器 图文步骤记录
1.安装DirectShow9.0 SDK DirectShow9 SDK下载链接http://download.csdn.net/detail/jindou910101/5591169 2.运行Di ...
- ie浏览器提交参数和其它浏览器的区别
场景描述: 用户注册模块(ajax提交方式,post方法),在url后追加了一个参数,如:url+‘btnvalue=中文参数’,如此在非ie浏览器注册时,功能完好,但在ie下注册不成功.调式后发现在 ...
- Android实战简易教程-第九枪(BitmapFactory.Options对资源图片进行缩放)
我们知道,我们编写的应用程序都是有一定内存限制的.程序占用了过高的内存就easy出现OOM(OutOfMemory)异常.因此在展示高分辨率图片的时候,最好先将图片进行压缩,压缩后的图片大小应该和用来 ...
- 总结文件操作函数-文件夹(三)-C语言
获取.改变当前文件夹: 原型为: #include <unistd.h> //头文件 char *getcwd(char *buf, size_t size); //获取当前文件夹.相 ...
- UVA 10828 - Back to Kernighan-Ritchie(概率+高斯消元)
UVA 10828 - Back to Kernighan-Ritchie 题目链接 题意:给图一个流程图,有结点的流程,每次进入下一个流程概率是均等的,有q次询问,求出每次询问结点的运行期望 思路: ...
- 在CodeBlocks 开发环境中配置使用OpenCV (ubuntu系统)
CodeBlocks是一个开放源代码的全功能的跨平台C/C++集成开发环境.CodeBlocks由纯粹的C++语言开发完毕,它使用了蓍名的图形界面库wxWidgets.对于追求完美的C++程序猿,再也 ...