IPC机制,含义为进程间通讯或者跨进程通讯,是指两个进程之间进行数据交换的过程。android
任何一个操做系统,线程是CPU能够操做的最小单元,同时线程是一种有限的系统资源。而进程通常指一个执行单元,在PC和移动设备上指一个程序或者一个应用。一个进程能够包含多个线程,所以进程和线程是包含与被包含的关系。数组
正常状况下,在Android中多进程是指一个应用中存在多个进程的状况,所以这里不讨论两个应用之间的多进程状况。首先,在Android中使用多进程只有一种方法,那就是给四大组件(Activity、Service、Receiver、ContentProvider)在AndroidMenifest中指定android:process属性,除此以外没有其余办法,也就是说咱们没法给一个线程或者一个实体类指定其运行时所在的进程。其实还有另外一种很是规的多进程方法,那就是经过JNI在native层去fork一个新的进程,可是这种方法属于特殊状况,也不是经常使用的建立多进程的方式,所以咱们暂时不考虑这种方式。缓存
<activity android:name="com.ryg.chapter_2.MainActivity" android:configChanges="orientation|screenSize" android:label="@string/app_name" android:launchMode="standard" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity <activity android:name="com.ryg.chapter_2.SecondActivity" android:configChanges="screenLayout" android:label="@string/app_name" android:process=":remote" /> <activity android:name="com.ryg.chapter_2.ThirdActivity" android:configChanges="screenLayout" android:label="@string/app_name" android:process="com.ryg.chapter_2.remote" />
上述代码中就出现了三个进程,上面的示例分别为SecondActivity和ThirdActivity指定了process属性,而且它们的属性值不一样,这意味着当前应用又增长了两个新进程。安全
可是,实际使用中多进程是有不少问题须要处理的,例如如下问题:网络
简单来讲,第(1),(2)问题都是由于多进程的内存块不一样,因此数据共享也会出现问题;第三个是由于出现多进程,那么对SharedPreferences同时读写,会出现并发
数据不一样步的安全问题;第(4),每一次开启多进程,至关于重启了一个应用程序,因此application必定会重走一次。app
首先,Serializable接口是一个空接口,做用是用来处理序列化的,为对象提供标准的序列化和反序列化操做。ide
让一个对象实现序列化,只须要这个类实现Serializable接口并声明一个serialVersionUID便可,实际上,甚至这个serialVersionUID也不是必需的,咱们不声明这个serialVersionUID一样也能够实现序列化,可是会对反序列化形成必定不肯定因素。高并发
例如:性能
public class User implements Serializable { private static final long serialVersionUID = 519067123721295773L; public int userId; public String userName; public boolean isMale; ... }
实际上,进行对象的序列化和反序列化也很是简单,只须要采用ObjectOutputStream和ObjectInputStream便可轻松实现:
//序列化过程 User user = new User(0,"jake",true); ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream("cache.txt")); out.writeObject(user); out.close(); //反序列化过程 ObjectInputStream in = new ObjectInputStream( new FileInputStream("cache.txt")); User newUser = (User) in.readObject(); in.close();
所谓的序列化,就是把实现了Serializable接口的User对象写到文件中就能够快速恢复了,恢复后的对象newUser和user的内容彻底同样,可是二者并非同一个对象。
那么如今,serialVersionUID的做用就体现出来了,这个serialVersionUID是用来辅助序列化和反序列化过程的,原则上序列化后的数据中的serialVersionUID只有和当前类的serialVersionUID相同才可以正常地被反序列化。
serialVersionUID的详细工做机制是这样的:序列化的时候系统会把当前类的serialVersionUID写入序列化的文件中(也多是其余中介),当反序列化的时候系统会去检测文件中的serialVersionUID,看它是否和当前类的serialVersionUID一致,若是一致就说明序列化的类的版本和当前类的版本是相同的,这个时候能够成功反序列化;不然就说明当前类和序列化的类相比发生了某些变换,好比成员变量的数量、类型可能发生了改变,这个时候是没法正常反序列化的。
Parcelable也是一个接口,只要实现这个接口,一个类的对象就能够实现序列化并能够经过Intent和Binder传递。
public class User implements Parcelable { public int userId; public String userName; public boolean isMale; public Book book; public User(int userId,String userName,boolean isMale) { this.userId = userId; this.userName = userName; this.isMale = isMale; } public int describeContents() { return 0; } public void writeToParcel(Parcel out,int flags) { out.writeInt(userId); out.writeString(userName); out.writeInt(isMale ? 1 : 0); out.writeParcelable(book,0); } public static final Parcelable.Creator<User> CREATOR = new Parcelable. Creator<User>() { public User createFromParcel(Parcel in) { return new User(in); } public User[] newArray(int size) { return new User[size]; } }; private User(Parcel in) { userId = in.readInt(); userName = in.readString(); isMale = in.readInt() == 1; book = in.readParcelable(Thread.currentThread().getContextClass- Loader()); } }
这里先说一下Parcel,Parcel内部包装了可序列化的数据,能够在Binder中自由传输。从上述代码中能够看出,在序列化过程当中须要实现的功能有序列化、反序列化和内容描述。
Parcelable和Serializable和区别
Serializable是Java中的序列化接口,其使用起来简单可是开销很大,序列化和反序列化过程须要大量I/O操做。而Parcelable是Android中的序列化方式,所以更适合用在Android平台上,它的缺点就是使用起来稍微麻烦点,可是它的效率很高,这是Android推荐的序列化方式,所以咱们要首选Parcelable。Parcelable主要用在内存序列化上,经过Parcelable将对象序列化到存储设备中或者将对象序列化后经过网络传输也都是能够的,可是这个过程会稍显复杂,所以在这两种状况下建议你们使用Serializable。
四大组件中的三大组件(Activity、Service、Receiver)都是支持在Intent中传递Bundle数据的,因为Bundle实现了Parcelable接口,因此它能够方便地在不一样的进程间传输。基于这一点,当咱们在一个进程中启动了另外一个进程的Activity、Service和Receiver,咱们就能够在Bundle中附加咱们须要传输给远程进程的信息并经过Intent发送出去。固然,咱们传输的数据必须可以被序列化,好比基本类型、实现了Parcellable接口的对象、实现了Serializable接口的对象以及一些Android支持的特殊对象
共享文件也是一种不错的进程间通讯方式,两个进程经过读/写同一个文件来交换数据,好比A进程把数据写入文件,B进程经过读取这个文件来获取数据。
可是,咱们知道,经过文件共享这种方式来共享数据对文件格式是没有具体要求的,好比能够是文本文件,也能够是XML文件,只要读/写双方约定数据格式便可。经过文件共享的方式也是有局限性的,好比并发读/写的问题。
固然,SharedPreferences是个特例,众所周知,SharedPreferences是Android中提供的轻量级存储方案,它经过键值对的方式来存储数据,在底层实现上它采用XML文件来存储键值对,每一个应用的SharedPreferences文件均可以在当前包所在的data目录下查看到。通常来讲,它的目录位于/data/data/package name/shared_prefs目录下,其中package name表示的是当前应用的包名。从本质上来讲,SharedPreferences也属于文件的一种,可是因为系统对它的读/写有必定的缓存策略,即在内存中会有一份SharedPreferences文件的缓存,所以在多进程模式下,系统对它的读/写就变得不可靠,当面对高并发的读/写访问,Sharedpreferences有很大概率会丢失数据,所以,不建议在进程间通讯中使用SharedPreferences。
Messenger是一种轻量级的IPC方案,它的底层实现是AIDL。
注意:这里的Messenger,不一样于handler,handler是没法在进程间通讯的。
实现一个Messenger有以下几个步骤,分为服务端和客户端。
服务端
咱们须要在服务端建立一个Service来处理客户端的链接请求,同时建立一个Handler并经过它来建立一个Messenger对象,而后在Service的onBind中返回这个Messenger对象底层的Binder便可。
public class MessengerActivity extends Activity { private static final String TAG = " MessengerActivity"; private Messenger mService; private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className,IBinder service) { mService = new Messenger(service); Message msg = Message.obtain(null,MyConstants.MSG_FROM_CLIENT); Bundle data = new Bundle(); data.putString("msg","hello,this is client."); msg.setData(data); try { mService.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } public void onServiceDisconnected(ComponentName className) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_messenger); Intent intent = new Intent(this,MessengerService.class); bindService(intent,mConnection,Context.BIND_AUTO_CREATE); } @Override protected void onDestroy() { unbindService(mConnection); super.onDestroy(); } }
客户端
客户端进程中,首先要绑定服务端的Service,绑定成功后用服务端返回的IBinder对象建立一个Messenger,经过这个Messenger就能够向服务端发送消息了,发消息类型为Message对象。
若是须要服务端可以回应客户端,就和服务端同样,咱们还须要建立一个Handler并建立一个新的Messenger,并把这个Messenger对象经过Message的replyTo参数传递给服务端,服务端经过这个replyTo参数就能够回应客户端。
public class MessengerActivity extends Activity { private static final String TAG = " MessengerActivity"; private Messenger mService; private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className,IBinder service) { mService = new Messenger(service); Message msg = Message.obtain(null,MyConstants.MSG_FROM_CLIENT); Bundle data = new Bundle(); data.putString("msg","hello,this is client."); msg.setData(data); try { mService.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } public void onServiceDisconnected(ComponentName className) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_messenger); Intent intent = new Intent(this,MessengerService.class); bindService(intent,mConnection,Context.BIND_AUTO_CREATE); } @Override protected void onDestroy() { unbindService(mConnection); super.onDestroy(); } }
上面的Messenger来进行进程间通讯的方法,若是大量的消息同时发送到服务端,服务端仍然只能一个个处理,若是有大量的并发请求,那么用Messenger就不太合适了。
因此,咱们可能须要跨进程调用服务端的方法,这种情形用Messenger就没法作到了,可是咱们可使用AIDL来实现跨进程的方法调用。
这里,就不写出AIDL的实现方式了,这个示例网络资源不少。
ContentProvider是Android中提供的专门用于不一样应用间进行数据共享的方式,从这一点来看,它天生就适合进程间通讯。和Messenger同样,ContentProvider的底层实现一样也是Binder。
咱们经过Socket来实现进程间的通讯。Socket也称为“套接字”,是网络通讯中的概念,它分为流式套接字和用户数据报套接字两种,分别对应于网络的传输控制层中的TCP和UDP协议。TCP协议是面向链接的协议,提供稳定的双向通讯功能,TCP链接的创建须要通过“三次握手”才能完成,为了提供稳定的数据传输功能,其自己提供了超时重传机制,所以具备很高的稳定性;而UDP是无链接的,提供不稳定的单向通讯功能,固然UDP也能够实现双向通讯功能。在性能上,UDP具备更好的效率,其缺点是不保证数据必定可以正确传输,尤为是在网络拥塞的状况下。
总之,上面六种均可以实现进程间通讯的,具体能够根据本身的需求来选择合适的方式。