非实时,通知性的方式

第一种方式就是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方式简介教程的更多相关文章

  1. Android项目依赖库管理方式简介

    在实际的android项目开发过程中,我们一般都会用一些现有的第三方库来实现我们的需求,避免重复造轮子.普遍使用到的,例如:网络请求库.图片处理库.界面UI库(自定义View.动画效果等).各种第三方 ...

  2. Android中的IPC机制

    Android IPC简介 IPC是Inter-Process Communication的缩写,含义就是进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程.那么什么是进程,什么是线程,进程 ...

  3. 【Android实战开发】3G技术和Android发展简介

    随着移动设备的不断普及和发展,相关软件的开发也越来越受到人们的关注,其中要提及的就是Android开发.本系列博客主要为大家介绍Android的开发,可能会有人问:现在互联网上已经有很多的Androi ...

  4. 《Android NFC 开发实战详解 》简介+源码+样章+勘误ING

    <Android NFC 开发实战详解>简介+源码+样章+勘误ING SkySeraph Mar. 14th  2014 Email:skyseraph00@163.com 更多精彩请直接 ...

  5. 【转】Android中实现IPC的几种方式详细分析及比较

    1.使用Bundle   ----> 用于android四大组件间的进程间通信android的四大组件都可使用Bundle传递数据  所以如果要实现四大组件间的进程间通信 完全可以使用Bundl ...

  6. Android实战简易教程-第三十九枪(第三方短信验证平台Mob和验证码自己主动填入功能结合实例)

    用户注冊或者找回password时通常会用到短信验证功能.这里我们使用第三方的短信平台进行验证实例. 我们用到第三方短信验证平台是Mob,地址为:http://mob.com/ 一.注冊用户.获取SD ...

  7. Android实战简易教程-第四十枪(窃听风云之短信监听)

    近期在做监听验证码短信自己主动填入的功能,无意间想到了一个短信监听的办法. 免责声明:短信监听本身是一种违法行为,这里仅仅是技术描写叙述.请大家学习技术就可以.(哈哈) 本实例是基于bmob提供的后台 ...

  8. Android进阶笔记18:选用合适的IPC方式

    1. 相信大家都知道Android进程间通信方式很多,比如AIDL.Messenger等等,接下来我就总结一下这些IPC方式优缺点. 2. IPC方式的优缺点和适用场景 3. 附加:使用Intent实 ...

  9. Android进阶笔记15:选用合适的IPC方式

    1. 相信大家都知道Android进程间通信方式很多,比如AIDL.Messenger等等,接下来我就总结一下这些IPC方式优缺点. 2. IPC方式的优缺点和适用场景 3. 附加:使用Intent实 ...

随机推荐

  1. 随机IP代理

    第一个例子就设置了一个代理IP,也是不靠谱的,最好的方式就是多设置几个,如第二个例子,通过http://www.youdaili.net/Daili/你可以找到很多代理IP, 抓取国内网站时尽量选取中 ...

  2. Windows调试工具入门—1

    NetRoc http://www.DbgTech.net 引子 Debugging Tools for Windows是微软发布的一套用于软件调试的工具包(后面如果没有指明,那么我会使用WinDbg ...

  3. 打造你自己ajax上传图片

    今天,我们需要的图片上传插件,但是,互联网不提供符合他们的需要和易于使用的.所以我写了自己. 方法1,只使用jquery代码,.代码例如以下 <p> <label>上传图片&l ...

  4. 【Windows Phone设计与用户体验】关于移动产品的Loading用户体验的思考

    作为一款运行在移动端上的产品,必定会有一些耗时的操作.为了具有良好的用户体验,Loading效果是必不可少的,而什么形式的Loading才会有良好的用户体验? Loading形式简单分为两类: 一.遮 ...

  5. 对于stackoverflow的中文翻译的相关问题

    我们非常多朋友都给我留言说.希望我翻译一下stackoverflow的问题以及答案,首先我也非常愿意为大家翻译,在能够帮助大家的同一时候,对我本人的技能的提升有优点:可是工作量实在太大,所以我不可能翻 ...

  6. Java-WebSocket 项目的研究(三) WebSocketClient 类 具体解释

    通过之前两篇文章 Java-WebSocket 项目的研究(一) Java-WebSocket类图描写叙述 Java-WebSocket 项目的研究(二) 小试身手:client连接server并发送 ...

  7. Ubuntu 8.04下安装DB2方法

    參考文献: How-to: Ubuntu 7.10 Server x86 32-bit and DB2 Express-C v9.5 DB2 v9.7 Infomation Center 场景:在IB ...

  8. 通用型CRM还是行业型CRM?-定制为王

    大数据时代,怎样利用工具摆脱繁杂的数据管理之苦,洞察有价值的销售信息,是每一个管理者的迫切须要.Zoho  CRM问世10年来,见证了一个个行业客户怎样在CRM帮助下实现了效率和业绩提升.相同,广泛的 ...

  9. net 面向接口框架

    Asp.net 面向接口框架之应用程序上下文作用域组件 在团队中推广面向接口开发两年左右,成果总体来说我还是挺满意的,使用面向接口开发的模块使用Unity容器配置的功能非常稳定,便于共享迁移(另一个项 ...

  10. 恢复gvim的ctl+v可视模式设置

    set nocompatiblesource $VIMRUNTIME/vimrc_example.vim"source $VIMRUNTIME/mswin.vim (注释此行)behave ...