IPC简介
IPC是Inter-Process Communication的缩写,含义为进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程。
Android中的多进程模式
通过给四大组件指定android:process属性,可以轻易开启多进程模式。进程名以“:”开头的进程属于当前应用的私有进程;不以“:”开头的进程属于全局进程,其它应用通过ShareUID方式可以和它跑在同一个进程中。
我们知道Android系统会为每个应用分配唯一的UID,具有相同的UID的应用才能共享数据。而两个应用通过ShareUID跑在同一个全局进程是有要求的,需要两个应用有相同的ShareUID并且签名相同才可以,这时的两个应用,就像一个应用的两个部分。注意:UID跟ShareUID不一样。
多进程模式的运行机制
当一个应用开启了多进程模式,就相当于两个不同的应用采用了ShareUID模式。
IPC基础概念
Serializable接口
Serializable是Java所提供的一个序列化接口,为对象提供了标准的序列化和反序列化操作;序列化过程中会将数据先写进文件,反序列化时会读取文件数据,因此序列化和反序列化过程需要大量的I/O操作。
//序列化
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 user = (User)int.readObject();
in.close();
注意:反序列化过程是有要求的,serialVersionUID必须一样才能反序列化成功。serialVersionUID的详细工作机制是这样的:序列化的时候系统会把当前类的serialVersionUID写入序列化的文件上中(也可以是其它中介),当反序列化的时候系统会检测文件中的serialVersionUID,看它是否和当前类的serialVersionUID一致,如果一致就说明序死化的类的版本和当前类的版本是相同的,这个时候可以成功反序列化。
一般来说,我们应该手动指定serialVersionUID的值,这样就可以避免反序列化失败;如果不手动指定serialVersionUID的值,当增加或删除某些成员变量时,系统就会重新计算当前类的hash值并把它赋值给serialVersionUID,这时反序列化就会失败。
//手动添加serialVersion
private static final long serialVersionUID = 1L;
Parcelable接口
Parcelable是Android所提供的一个序列化接口,为对象提供了序列化、反序列化和内容描述,序列化和反序列化都只是将数据保存到内存中,因此效率高,大概是Serializable的10倍。
序列化由writeToParcel方法完成,最终是通过Parcel中的一系列write方法来完成;
反序列化由CREATOR来完成,其内部标明了如何创建序列化对象和数组,并通过Parcel的一系列read方法来完成反序列化过程;
内容描述由describeContents方法来完成,几乎所有情况下这个方法都应该返回0,仅当当前对象存在文件描述符时,此方法才返回1;
当当前对象内存在另一个可序列化对象时,需要传递当前线程的上下文类加载器。
Intent
Intent是Parcelable的子类,因此它本身就可以在进程间传输,同时它也是一种进程间通信的桥梁,Intent的工作机制比较复杂,这里就不详细讲解了。
Bundle
Bundle是Parcelable的子类,因此Bundle也可以在进程间传输。
Serializable和Parcelable的选择:
一般情况下我们建议使用Parcelable,但在以下两种情况还是使用Serializable比较好:
1、需要将对象序列化后保存到存储设备;
2、需要将对象序列化后通过网络传输;
这两种情况使用Parcelable也是可以的,但使用起来会比较复杂,而Serializable本身就对数据做I/O操作,因此使用起来简单。
Binder
Binder比较特别,可以理解为一个虚拟的物理设备,它的设备驱动是/dev/binder,它主要为两个进程搭建数据通信的桥梁,是ServiceManager连接各种Manager和相应MessagerService的桥梁。
Android开发中,Binder主要用在Service中。
Android中的IPC方式
1、使用Bundle
Bundle实现了Parcelable接口,所以它可以方便地在不同的进程间传输。
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
Bundle bundle = new Bundle();
bundle.putString("key","value");
intent.putExtras(bundle);
FirstActivity.startActivity(intent);
2、使用文件共享
两个进程通过读/写同一个文件来交换数据,比如A进程把数据写入文件,B进程通过读取这个文件来获取数据。由于Android系统基于Linux,使得其并发读/写文件可以没有限制地进行。所以如果并发读/写,那么我们读出来的内容有可能不是最新的。SharedPreferences是个特例,系统对它的读/写有一定的缓存策略,即在内存中会有一份SharePreferences文件的缓存,因此在多进程模式下,系统对它的读/写变得不可靠,因此,不建议在进程间通信中使用SharedPreferences。
MyConstants.java
public static final String CHAPTER_PATH = Environment.getExternalStorageDirectory() + File.separator + "cache";
public static final String CACHE_FILE_PATH = CHAPTER_PATH + File.separator + "cache.xml";
FirstActivity.java
//A进程中的FirstActivity:写入数据到文件
private void persistToFile(){
new Thread(new Runnable(){
@Override
public void run(){
User user = new User(1,"hello",false);
File dir = new File(MyConstants.CHAPTER_PATH);
if(!dir.exists()){
dir.mkdirs();
)
File cachedFile = new File(MyConstants.CACHE_FILE_PATH);
ObjectOutputStream objectOutputStream = null;
try{
objectOutputStream = new ObjectOutputStream(new FileOutputStream(cacheFile));
objectOutputStream.writeObject(user);
}catch(IOException e){
e.printStackTrace();
}finally{
if(objectOutputStream != null){
objectOutputStream.close();
}
}
}
}).start();
}
SecondActivity.java
//B进程中的BecondActivity:读文件数据
private void ercoverFromFile(){
new Thread(new Runnable(){
@Override
public void run(){
User user = null;
File cachedFile = new File(MyConstants.CACHE_FILE_PATH);
if(cachedFile.exists()){
ObjectInputStream objectInputStream = null;
try{
objectInputStream = new ObjectInputStream(new FileInputStream(cachedFile));
user = (User)objectInputStream.readObject();
}catch(IOException e){
e.printStackTrace();
}catch(ClassNotFoundException e){
e.printStackTrace();
}finally{
if(objectInputStream != null){
objectInputStream.close();
}
}
}
}
}).start();
)
使用3、Messenger
Messenger是一种轻量级的IPC方案,它的底层实现是AIDL。通过它可以在不同的进程中传递Message对象。
MyConstants.java
public static final CLIENT_MSG_KEY = "client_msg";
public static final SERVICE_MSG_KEY = "service_msg";
public static final int MSG_FROM_CLIENT = 1;
public static final int MSG_FORM_SERVICE = 2;
客户端:
public class MessengerActivity extends Activity {
private Messenger mService;
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MyConstants.MSG_FORM_SERVICE:
//接收服务端传来的数据
String message = msg.getData().getString(MyConstants.SERVICE_MSG_KEY);
break;
default:
super.handleMessage(msg);
break;
}
}
}
private final Messenger mMessenger = new Messenger(new MessengerHandler());
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//与服务端连接成功后,获取服务端的Messenger
mService = new Messenger(service);
//通过服务端的Messenger将数据和客户端的Messenger传递给服务端
Message msg = Message.obtain(null, MyConstants.MSG_FROM_CLIENT);
Bundle data = new Bundle();
data.putString(MyConstants.MSG_KEY, "hello");
msg.setData(data);
msg.replyTo = mMessenger;
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent(this, MessengerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
unbindService(mConnection);
super.onDestroy();
}
}
服务端:MessengerService.java
public class MessengerService extends Service {
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MyConstants.MSG_FROM_CLIENT:
//接收客户端传来的数据
String message = msg.getData().getString(MyConstants.CLIENT_MSG_KEY);
//获取客户端的Messenger,并将数据传递给客户端
Messenger client = msg.replyTo;
Message relpyMessage = Message.obtain(null, MyConstants.MSG_FORM_SERVICE);
Bundle bundle = new Bundle();
bundle.putString(MyConstants.SERVICE_MSG_KEY,"welcome");
relpyMessage.setData(bundle);
try {
client.send(relpyMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
break;
}
}
}
private final Messenger mMessenger = new Messenger(new MessengerHandler());
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
}
4、使用AIDL
Book.java
public class Book implements Parcelable{
public int bookId;
public String bookName;
public Book(int bookId,String bookName){
this.bookId = bookId;
this.bookName = bookName;
}
......
}
Book.aidl
parcelable Book;
IOnNewBookArrivedListener.aidl
interface IOnNewBookArrivedListener{
void onNewBookArrived(in Book book);
}
IBookManager.aidl
interface IBookManager{
List<Book> getBookList();
void addBook(in Book book);
void registerListener(IOnNewBookArrivedListener listener);
void unregisterList(IOnNewBookArrivedListener listener);
}
客户端:BookManagerActivity.java
BookManagerActivity extents Activity{
private IBookManager mBookManager;
private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub(){
@Override
public void onNewBookArrived(Book newBook) throws RemoteException{
//当服务端有数据更新时,会回调这个方法
//接收服务端的数据
}
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//连接成功后,获取服务端的IBookManager
mBookManager = IBookManager.Stub.asInterface(service);
//调用aidl方法,主动获取服务端的数据
List<Book> bookList = mBookManager.getBookList();
//注册
mBookManager.registerListener(mOnNewBookArrivedListener);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mBookManager = null;
}
}
};
@Override
protected void onCreate(Bundle saveInstanceState){
Intent intent = new Intent(this,BookManagerService.class);
bindService(intent,mConnection,Context.BIND_AUTO_CREATE);
}
@Override void onDestroy(){
if(mBookManager != null && mBookManager.asBinder().isBinderAlive()){
try{
mBookManager.unregisterListener(mOnNewBookArrivedListener);
}catch(RemoteException e){
e.printStackTrace();
}
}
unnindService(mConnection);
super.onDestroy();
}
}
服务端:BookManagerService.java
public class BookManagerService extends Service{
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();
private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList();
//实现AIDL里的方法
private Binder mBinder = new IBookManager.Stub(){
@Override
public List<Book> getBookList() throws RemoteException{
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException{
mBookList.add(book);
}
@Override
public void registerListener(IOnNewBookArrivedListener listener){
mListenerList.register(listener);
}
@Override
public void unregisterList(IOnNewBookArrivedListener listener){
mListenerList.unregister();
}
}
@Override
public void onCreate(){
super.onCreate();
//初始化数据,并通知有消息更新
onNewBookArrived(new Book(1,"Android"));
}
@Override
public IBinder onBind(Intent intent){
return mBinder;
}
/**
* 通知消息变化
*/
private void onNewBookArrived(Book book) throws RemoteException{
mBookList.add(book);
final int N = mListenerList.beginBroadcast();
for(int i = 0 ; i < N ; i++){
IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);
if(listener != null){
try{
listener.onNewBookArrived(book);
}catch(RemoteException e){
e.printStackTrace();
}
}
}
mListenerList.finishBroadcast();
}
}
5、使用ContentProvider(内容提供者)
ContentProvider是Android中提供的专门用于不同应用间进行数据共享的方式。
android:authorities是ContentProvider的唯一标识,通过这个属性外部应用就可以访问我们的BookProvider,
因此,android:authorities必须是唯一的,建议在命名的时候加上包名作为前缀。
服务端:BookContentProvide.java
public class BookContentProvide extends ContentProvider {
@Override
public boolean onCreate() {
return false;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
//用来返回一个Uri请求所对应的MIME类型(媒体类型),比如图片、视频等,这个媒体类型还是有点复杂的,
// 如果我们的应用不关注这个选项,可以直接在这个方法中返回null或者“*/*”
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
//增
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strings) {
//删
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) {
//改
return 0;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] strings, @Nullable String s, @Nullable String[] strings1, @Nullable String s1) {
//查
return null;
}
}
客户端:
Uri uri = Uri.parse("Content://" + authorities); //authorities是ContentProvider的android:authorities属性值
context.getContentResolver().query(uri, null, null, null, null);
6、使用Socket(套接字)
Socket是网络通信中的概念,它分为流式套接字和用户数据报套接字,分别对应于网络的传输控制层中的TPC和UDP协议。
TPC协议是面向连接的协议,提供稳定的双向通信功能,TPC连接的建立需要经过“三次握手”才能完成,为了提供稳定的数据传输功能,其本身提供了超时重传机制,因此具有很高的稳定性;
UDP是无连接的,提供不稳定的单向通信功能,UDP也可以实现双向通信功能。在性能上,UDP具有更好的效率,其缺点是不保证数据一定能正确传输,尤其是在网络阻塞的情况下