Android开发艺术探索笔记(7)- IPC方式
来源:互联网 发布:php环境配置教程 百度 编辑:程序博客网 时间:2024/06/05 09:43
之前说的多进程模式和Binder都是为了这节做的铺垫(这个铺垫好长呀~~)。那么Android的IPC方式有多少种呢?
(1)Bundle
(2)文件同享
(3)Messenger
(4)AIDL
(5)ContentProvider
(6)Socket
Bundle
好吧,这个也算是一种IPC??Bundle的确是实现了Parcelable接口,但是也只是一种数据集合类型,如形同Map。我们都忘了,Intent这个的存在。我们可以通过Intent去启动一个程序Activity,Service,通知一个程序Receiver。而Bundle可以通过setData到Intent中,这就达到IPC的效果了。(这下认了吧!!)
文件同享
一个普通文件可以由所有进程读和写,一个进程去写如些数据,如对象序列化到文件,另一个进程就可以去读取这个文件的内容,如将文件内容反序列化成对象,这也可以达到IPC的目的。不过要注意的是,一个文件并发读/写的问题。之前说过Android的SharedPreferences是采用xml文件保存键值对,底层也是一个文件。之前也说过为什么多进程读/写SharedPreferences会造成数据混乱,可能说的不是很明白。现在说的明白点,文件并发性读写是一个问题。还有个严重的问题是,程序内存会还保存了一份SharedPreferences的缓存,所以读写数据变得不可靠。所以不建议在多进程使用SharedPreferences。
Messenger
Messenger被意为信使(表示之前没见过)。它底层其实就是AIDL,只不过在AIDL的基础上做了一定的封装,使得我们用AIDL变得方便。AIDL的原理在上一节已经了解过了,这里不做详解。说一下怎么用。
Messenger有两个构造函数:Messenger(Binder binder)和Messenger(Handler handler)。
(1)服务端先写新建一个Service类,在Service类中定义一个Handler,用于创建Messenger对象。在onBinder返回创建好的Messenger实例的Binder即可。
(2)客户端绑定服务,在onServiceConnected方法内,用Binder参数新建Messenger对象,新建Message(不是Messenger哦)用于传输数据,使用Bundle作为保存数据的容器,调用Messenger.send(Message)方法传输数据到服务端。此时服务端的定义的Handler就会收到数据,在handleMessage方法内处理客户端发过来的数据。书中说明了一点我很在意的是,为什么不用object来传数据(对象)呢?吼吼,原来object属性不能用于跨进程传输。算了,还有Bundle可以用,虽然麻烦了点…
服务端可是可以反馈数据给客户端的。Message有一个属性叫replyTo,这个意思也很明显啦。我们在客户端也用定义一个Handler来创建一个Messenger对象(注意,此时有两个Messenger对象,一个是用Binder创建的,用户发送数据到服务端。另一个用Handler创建的,用户接收数据时调用)。然后将由Handler创建的Messenger赋值给Message的replyTo属性。在服务端收到replyTo这个对象后,也要像客户端一样,新建Message,添加数据,利用replyTo属性的对象传输数据给客户端。可能说的不是很清楚,看图:
具体代码,请看书!!
AIDL
之前整一节都在说AIDL,连Messenger的底层也是AIDL,作者又在这里说AIDL,我倒觉得有点重复了。详细使用可以参考Android中的AIDL进程间通信这个栗子。因为常用,所以我把重要代码copy过来,以便查看。
CalculateInterface.aidl
对于aidl的方法变量,书中还说了aidl支持的6种数据类型:
(1)基本类型(int,float,double…)
(2)String和CharSequence
(3)List:只支持ArrayList,要求加进List的对象都实现Parcelable接口
(4)Map:只支持hashMap,要求加进List的对象都实现Parcelable接口
(5)Parcelable:所有实现Parcelable接口对象
(6)ADIL:所有ADIL接口也可以在ADIL中使用(接下来会有栗子)
package com.example.aidl.calculate;interface CalculateInterface { double doCalculate(double a, double b);}
服务端
public class CalculateService extends Service { // ... @Override public IBinder onBind(Intent arg0) { // TODO Auto-generated method stub return mBinder; } // ... private final CalculateInterface.Stub mBinder = new CalculateInterface.Stub() { @Override public double doCalculate(double a, double b) throws RemoteException { // TODO Auto-generated method stub Log.e("Calculate", "远程计算中"); Calculate calculate = new Calculate(); double answer = calculate.calculateSum(a, b); return answer; } }; // ...}
客户端
public class CalculateClient extends Activity { // ... private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { // TODO Auto-generated method stub mService = null; } @Override public void onServiceConnected(ComponentName name, IBinder service) { // TODO Auto-generated method stub mService = CalculateInterface.Stub.asInterface(service); } }; // ... @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.main); Intent intent = new Intent("com.example.calculate.CalculateService"); bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); etNum1 = (EditText) findViewById(R.id.et_num_one); etNum2 = (EditText) findViewById(R.id.et_num_two); tvResult = (TextView) findViewById(R.id.tv_result); btnCalculate = (Button) findViewById(R.id.btn_cal); btnCalculate.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub try { double num1 = Double.parseDouble(etNum1.getText().toString()); double num2 = Double.parseDouble(etNum2.getText().toString()); String answer = "计算结果:" + mService.doCalculate(num1, num2); tvResult.setTextColor(Color.BLUE); tvResult.setText(answer); } catch (RemoteException e) { } } }); } // ...}
作者还说到一种场景,就是我们需要服务器发生某事件后能及时通知客户端。这种类似于观察者模式,只不过跨进程的。
实现的原理和我们写观察模式的程序一样。我们多定义一个AIDL文件,里面定义一个接口,如
INewBookListener.aidl
interface INewBookListener { void onNewBook(in Book newBook);}
在服务端的aidl添加两个方法,如
IBookManager.adil
interface IBookManager { // ... void addBook(in Book book); void addRegister(INewBookListener listener); void removeRegister(INewBookListener listener);}
然后服务端写Service时,获取Binder的时候,实现这两个方法。
public class BookManagerService extends Service { /** * 因为进程传输的对象都是序列化的,所以add的对象和要remove的对象虽然数据一样,但是不是同一个对象,所以无法真正移除要remove的对象 **/ // private CopyOnWriteArrayList<INewBookListener> listenerList = new CopyOnWriteArrayList<INewBookListener>(); /** * RemoteCallbackList虽然名为List,但实际上是一个Map,保存的键是连接的Binder。所以我们移除时可以准确移除要remoew的对象,而且做了同步处理 **/ private RemoteCallbackList<INewBookListener> listenerList = new RemoteCallbackList<INewBookListener>(); // ... private BookManager.Stub binder = new IBookManager.Stub() { // ... /** * listenerList.beginBroadcast()和listenerList.finishBroadcast()必须成对出现,及时只是获取listenerList的size **/ @Override public void addBook(Book book) { int count = listenerList.beginBroadcast(); for (int i = 0; i < count; i++) { INewBookListener listener = listenerList.getBroadcastItem(i); listener.onNewBook(book); } listenerList.finishBroadcast(); } @Override public void addRegister(INewBookListener listener) { listenerList.register(listener); } @Override public void removeRegister(INewBookListener listener) { listenerList.unregister(listener); } } // ...}
客户端绑定时调用,调用IBookManager的register方法把INewBookListener添加服务器的listenerList中。当客户端destroy时,调用IBookManager的unregister方法就可以服务端listenerList中移除。不知道你有没有注意到,我们传输的是一个AIDL接口,也证实了AIDL接口也可以作为AIDL的数据类型。客户端获取INewBookListener接口的Java对象如下:
private INewBookListener bookLisetner = new INewBookListener.Stub() { @Override public void onNewBook(Book newBook) { // TODO }}
ContentProvider
我们都知道ContentProvider作为四大组件之一,用于同享数据。在一个程序中定义类继承ContentProvider,然后再公开出来,让别的程序可以读写自己数据。但是你知道,其实他的底层也是Binder,相对与ADIL,使用起来很简单。因为本章作者关心的是IPC,所以没对ContentProvider的原理没怎么说,我看到后面章节有说,现在我也不care。
具体怎么使用?简单来说,自定义ContentProvider类,实现里面6个方法,然后在AndroidMenifest.xml文件公开自己访问地址。
具体6个方法是:onCreate、insert、update、query、delete、getType。getType用来返回的是URI请求的MIME类型。听说有点复杂,如果不太关心的话,直接返回null或者*/ *就行。
怎么对外公开自己呢?很简单,在AndroidMenifest.xml文件添加provider标签,必须指定android:authoritise属性,这个就是对外访问的路径,并且是唯一的。一般这个属性值加上包名作为前缀。还可以加上android:permission作为权限控制。其实权限控制还分readPremission和writePermission。
举个栗子:
<provider android:name="com.johan.project.BookProvider" android:authoritise="com.johan.project.provider" android:permission="com.johan.project.PROVIDER" />
客户端怎么访问呢?既然是四大组件,Activity当然体用了建议的访问方式。
public class MainActivity extends Activity { @Override public void onCreate(Bundle saveInstanceState) { Uri uri = new Uri("content://com.johan.project.provider"); getContentResolver().query(uri, null, null, null, null); }}
这样就可以访问到服务端的数据了。
Socket
Socket一般用于网络通信,当然也可以用于进程间通信。只是没想到而已。这里只是做一下记录。
作者还给出各种IPC方式的优缺点和使用场景,如图:
作者下一节说到的Binder池,我不打算做笔记,做到再说!!
- Android开发艺术探索笔记(7)- IPC方式
- 《Android开发艺术探索》Android中的IPC方式--未完
- Android开发艺术探索笔记_第二章 IPC机制
- android开发艺术探索笔记 IPC机制上
- Android开发艺术探索--第二章IPC机制(3)之Android中的IPC方式
- Android IPC机制学习(Android艺术开发探索)
- Android开发艺术探索学习笔记(2)--IPC机制(1)
- 《Android开发艺术探索》之学习笔记(二)IPC机制
- android开发艺术探索笔记--第二章IPC(InterProcess Communication)
- 第二章IPC机制(Android开发艺术探索)
- IPC机制 - Android开发艺术探索读书笔记(第二章)
- 【Android开发艺术探索】IPC机制(一)
- 【Android开发艺术探索】IPC机制(二)
- IPC机制(Android开发艺术探索读书笔记)
- Android开发艺术探索读书笔记-IPC机制
- IPC机制--开发艺术探索(一)
- IPC机制--开发艺术探索(二)
- 《Android开发艺术探索》笔记(1)
- 第十四周5
- spring @component的作用
- 排序和统计记数
- 多态的强制转换
- Elasticsearch CURL操作方法
- Android开发艺术探索笔记(7)- IPC方式
- 微信小程序开发—项目实战之聊天机器人
- cocoapods的安装和使用以及版本升级遇到的问题
- spring MVC的使用
- Swift JSON
- sass安装 scss安装
- 泛型总结
- spring相关jar下载
- AIDL