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实 ...
随机推荐
- 450A - Jzzhu and Children 找规律也能够模拟
挺水的一道题.规律性非常强,在数组中找出最大的数max,用max/m计算出倍数t,然后再把数组中的书都减去t*m,之后就把数组从后遍历找出第一个大于零的即可了 #include<iostream ...
- crontab linux
第1列分钟1-59第2列小时1-23(0表示子夜)第3列日1-31第4列月1-12第5列星期0-6(0表示星期天)第6列要运行的命令 下面是crontab的格式:分 时 日 月 星期 要运行的命令 这 ...
- niu人
金步国简历 金步国简历 基本资料 姓名 金步国 性别 男 年龄 30 籍贯 江苏 淮安 院校 同济大学 专业 土木工程 学历 本科肄业 工作经验 5年 期望地点 长江以南 期望薪水 18000/月 个 ...
- SilkTest Q&A 3
Q21:如何给testcase的属性赋值? A21: 1.确定你的testplan处于打开状态. 2.点击你准备赋属性值的testcase 3.点击TestPlan/detail菜单,testplan ...
- sharepoint 2010 显示和隐藏Ribbon区域条
在sharepoint 2010的页面中,我们在页面的最上方,有一条深灰色的Ribbon工具栏,如下图,这里可以通过下面的脚本,做一些脚本,来控制它的隐藏和显示. 最后把这些脚本,放在v4.maste ...
- extern 使用方法具体解释
在C语言中,修饰符extern用在变量或者函数的声明前,用来说明"此变量/函数是在别处定义的.要在此处引用".(extern能够置于变量或者函数前,以标示变量或者函数的定义在别的文 ...
- 进一步解 apt-get 的几个命令
用 apt-get 也非常久了,没多想它的实现,近期遇到 gstreamer 装不上的问题.才多看看了它 apt-get 就是从网上下载包,并安装到本地 手工下载 dpkg 包,而后 "dp ...
- CentOS下安装MySQL,Windows下使用Navicat for MySql连接
安装 查看有没有安装过: yum list installed mysql* rpm -qa | grep mysql* 查看有没有安装包: yu ...
- [Android学习笔记]使用getIdentifier()获取资源Id
使用getIdentifier()获取资源Id Android中可以使用getIdentifier()获取资源ID ex: 根据图片名称获取图片Id private int getImageResId ...
- Java 建立mysql数据库连接的语句
每次在面试时被问到jdbc的数据路链接过程都卡着,这次不怕了,背会了... 第一个,比较粗糙的 try{ Class.forName("com.mysql.jdbc.Driver&quo ...