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实 ...
随机推荐
- UVA11090 Going in Cycle!! (二分+SPFA推断有无负权)
Problem G: Going in Cycle!! Input: standard input Output: standard output You are given a weighted d ...
- 在Delphi中使用C++对象(两种方法,但都要改造C++提供的DLL)
Delphi是市场上最好的RAD工具,但是现在C++占据着主导地位,有时针对一个问题很难找到Delphi或Pascal的解决方案.可是却可能找到了一个相关的C++类.本文描述几种在Delphi代码中使 ...
- haproxy 中的http请求和https请求
use Mojolicious::Lite; use JSON qw/encode_json decode_json/; use Encode; no strict; use JSON; # /foo ...
- http调试工具Charles Proxy用法详解
Charles Proxy 通常称为Charles,Charles是目前最强大的http调试工具,在界面和功能上远强于Fiddler,同时是全平台支持,堪称圣杯级工具,不过在这里为您提供了Charle ...
- BT5 firefox Flash插件问题
今天在BT下安装了Nessus,好不容易安装好了,注册成功,本以为大功告成,但是在最后关头,却出现一个“未安装flash插件”错误,在bT下尝试着安装flahs插件,蛋碎一地,,,没能解决. 我的BT ...
- SQL Server定时自动抓取耗时SQL并归档数据脚本分享
原文:SQL Server定时自动抓取耗时SQL并归档数据脚本分享 SQL Server定时自动抓取耗时SQL并归档数据脚本分享 第一步建库 USE [master] GO CREATE DATABA ...
- 2014最新SSH框架面试题大收集
(1)Hibernate工作原理及为什么要用? 原理: 1.读取并解析配置文件 2.读取并解析映射信息,创建SessionFactory 3.打开Sesssion 4.创建事务Transati ...
- HDU 2255 奔小康,赚钱(KM算法模板)
解决问题的思路: 二部图,正确的匹配,卡费用流,使用KM算法. #include <cstring> #include <algorithm> #include <cst ...
- hdu1532(最大流)
传送门:Drainage Ditches 题意:给出n个河流,m个点,以及每个河流的流量,求从1到m点的最大流量. 分析:网络流入门题,第一次写按照白书上毫无优化的Ford_fulkerson算法,先 ...
- 【Qt for Android】OpenGL ES 绘制彩色立方体
Qt 内置对OpenGL ES的支持.选用Qt进行OpenGL ES的开发是很方便的,很多辅助类都已经具备.从Qt 5.0開始添加了一个QWindow类,该类既能够使用OpenGL绘制3D图形,也能够 ...