IPC简介
IPC是Inter-Process Communication的缩写。含义为进程间通信或者跨进程通信。是指两个进程之间进行数据交换的过程。
线程:按照操作系统中的描述,线程是cpu调度的最小单元,同时线程是一种有限的系统资源。
进程:一般指一个执行单元,在pc和移动设备上指一个程序或者一个应用。
IPC不是android独有的,像window上就可以通过剪切板等进行进程间通信。android是基于linux内核的移动操作系统,它的进程间通信方式并不能完全继承制linux。它有自己的进程间通信的方式。android中最有特色的进程通信方式是Binder.另外还支持Scoket。
android中的多进程模式
1.开启多进程模式
一般情况下,android中的多进程指一个应用中存在多个进程的情况,暂不讨论多个应用之间的多进程情况。在android中使用多进程只有一种方法,就是在清单文件中对四大组件指定android:process属性。所以我们无法给一个线程或者一个实体类指定其运行的进程。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lvqueen.myartexplore">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".day01.FirstActivity"
android:process=":process1"
/>
<!--:process1是指在当前进程名前附加上当前的包名,是简写。属于当前应用的私有进程,其他应用的组件
不能和它跑在同一个进程中。
-->
<activity android:name=".day01.SecondActivity"
android:process="com.lvqueen.myartexplore.process1"
></activity>
<!--写完整路径的这种进程,属于全局进程。其他应用可以通过shareUID方式可以和它泡在同一个进程中-->
<!--android系统会为每一个应用分配UID具有相同UID的应用才能共享数据。两个应用通过ShareUID方式跑在同一个
进程中要求这两个应用有相同的ShareUID并且签名相同,这种情况下可以互相访问对方的私有数据data目录,组件信息,
还可以共享内存数据。或者说他们看起来像一个应用的两部分。
-->
</application>
</manifest>
⚠️注意:多进程造成的问题
(1)静态成员和单例模式完全失效:android为每一个应用(进程)分配了一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,这就导致了在不同的虚拟机中访问同一个类对象会产生多份副本。并且这两个副本是互不干扰的,在一个进程中修改一个属性指,只会影响当前进程。而对其他进程不会造成任何影响。
(2)线程同步机制完全失效:既然都不是一块内存了,那么不管是锁对象还是锁全局类都无法保证线程同步。因为不同进程锁的不适同一个对象。
(3)SharePreferences的可靠性下降:它不支持两个进程同时去执行写操作,否则会导致一定几率的数据丢失。因为share底层是通过读写XML文件来实现的,并发写显然是可能出现问题的,并发读都有可能出现问题。
(4)Application会多次创建:当一个组件跑在一个新的进程中时,由于系统要创建新的进程同时分配独立的虚拟机,所及这个过程其实就是启动一个应用的过程,因此相当于系统又把这个应用重新启动了一遍,既然重新启动,那么自然就会创建新的Appliction.
IPC基础概念介绍
1.Serializable:实现对象序列化。是java中的序列化接口,使用起来简单,但是花销很大,需要大量的i/o操作。序列化将对象转化为文件,反序列化将文件转换为对象。
使用方式:只要将java类简单的实现Serializable接口就可以了。
注意:在实现Serializable接口的实现类中,有一个SerialVersionUID(public static final long serialVersionUID = 1234567l;)
它用于辅助序列化和反序列化。进行序列化时,系统会把这个值写入文件当中,反序列化时会取出这个值和反序列化的类中的这个值做比对。只有序列化中的这个值和当前类的这个值相同,才能正常被反序列化,否则说明当前类和序列化的类相比发生了某些变化,无法完成正常反序列化。一般来说,我们应该手动指定它的值。如果不手动指定,系统会根据当前类计算一个hash值赋值给serialVersionUID,如果类删除或增加了某个成员变量,就会使这个值发生变法,导致不能正常反序列化了。如果手动指定,当应用升级删除或增加了某个变量后,程序能够最大限度的恢复数据。
/**
* 序列化,把对象直接写入文件中
*/
private void toSerialize() {
Student student = new Student();
try {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("cache.txt"));
out.writeObject(student);
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 反序列化,从文件中直接拿到对象
*/
private void deSerialize() {
try {
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("cache.txt"));
Student student = (Student) inputStream.readObject();
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
2.Parcelable:实现对象序列化》android中的序列化方式,效率高,但是用复杂。
public class Book implements Parcelable{
public int boolId;
public String bookName;
/**
* 进行反序列化,主要下面protected方法
* @param boolId
* @param bookName
*/
public Book(int boolId, String bookName) {
this.boolId = boolId;
this.bookName = bookName;
}
/**
* 从序列化后的对象中创建原始对象
* @param in
* @return
*/
protected Book(Parcel in) {
boolId = in.readInt();
bookName = in.readString();
// in.readParcelable(Thread.currentThread().getContextClassLoader())
}
/**
* 返回当前对象的内容描述,如果含有文件描述返回1,否则返回0,基本为0;
* @return
*/
@Override
public int describeContents() {
return 0;
}
/**
* 将当前对象写入序列化结构中,除了一版的数据类型,也可以序列化一个序列化的对象
* parcel.writeParcelable()序列化一个序列化的对象
* @param parcel
* @param i:值为1或0,标记为1时表示当前对象需要作为返回值返回,不能立即释放资源
* 基本上这个值都为0.
*/
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(boolId);
parcel.writeString(bookName);
// parcel.writeParcelable();
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
/**
* 从序列化后的对象中创建原始对象
* @param in
* @return
*/
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
/**
* 创建制定长度的原始对象数组
* @param size
* @return
*/
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
}
3.Binder的使用和上层原理。
从IPC角度来说Binder是android中的一种跨进程通信方式,Binder还可以理解为一种虚拟的物理设备,设备驱动是/dev/binder。
从android Framework角度来时,Binder是ServiceManager连接各种Manger和相应的ManagerService的桥梁。
从android应用层来说Binder是客户端和服务器端进行通信的媒介。
android开发中Binder主要用于Service中,包括AIDL(android interface definition language 安卓接口定义语言)和Messenger。普通Service中不涉及进程间通信。Messenger底层为AIDL。
4.aidl文件创建
(在android studio中)例如你要创建的aidl文件在com.exmple.test.myaildtest文件路径下,则选择main-jave-com.exmple.test.myaildtest然后右键-new -AIDL -AIDL File这时候会在与java文件同级创建一个aidl文件夹,下面有个com.exmple.test.myaildtest文件夹,里面有你创建的aidl文件,写上你要添加的一些方法。使用之前rebulid一下,在build-generated-source-aidl-debug-com.exmple.test.myaildtest会生成一个与你创建的aild文件同名的文件,这时候你就可以使用了。
与java文件夹同级下的.aidl文件
⚠️注意:当客户端发起远程请求时,由于当前线程会被挂起直至服务端进程返回数据,所以如果一个远程方法是很耗时的,那不能在ui线程中发起。另外由于服务端的binder方法运行在binder的线程池中,所以binder方法不管是否耗时,都应该采取同步的方式去实现,因为它已经运行在一个线程中了。
interface IBookManager {
List<String> getBookList();
void addBook(String book);
}
build下自动生成的文件
public interface IBookManager extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.lvqueen.myartexplore.aidllearn.IBookManager {
// Binder的唯一标示,一般用类名
private static final java.lang.String DESCRIPTOR = "com.lvqueen.myartexplore.aidllearn.IBookManager";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.lvqueen.myartexplore.aidllearn.IBookManager interface,
* generating a proxy if needed.
* 用于将服务端binder对象转换成客户端所需的aidl接口类型对象。如果客户端和服务器端运行在统一进程,返回服务端
* stub对象本身,否则返回stub.proxy
*/
public static com.lvqueen.myartexplore.aidllearn.IBookManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.lvqueen.myartexplore.aidllearn.IBookManager))) {
return ((com.lvqueen.myartexplore.aidllearn.IBookManager) iin);
}
return new com.lvqueen.myartexplore.aidllearn.IBookManager.Stub.Proxy(obj);
}
/**
* 返回当前binder对象
* @return
*/
@Override
public android.os.IBinder asBinder() {
return this;
}
/**
* 运行在服务器端的binder线程池中。当客户端发起跨进程请求时,远程请求会通过系统底层封装后由此方法
* 来处理,服务端通过code确定客户端手请求的目标方法。接着从data中取目标方法所需要的参数,执行目标方法。
* 之后向reply中写入返回值。如果onTransact返回false,客户端请求会失败。可以用来做权限验证。这样就不能
* 随便一个进程都能远程调用我们的服务了。
* @param code
* @param data
* @param reply
* @param flags
* @return
* @throws android.os.RemoteException
*/
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getBookList: {
data.enforceInterface(DESCRIPTOR);
java.util.List<java.lang.String> _result = this.getBookList();
reply.writeNoException();
reply.writeStringList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
this.addBook(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.lvqueen.myartexplore.aidllearn.IBookManager {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
/**
* 运行在客户端。创建方法说需要的输入型parcel对象_data输出型parcel对象_reply和返回值对象list;
* 然后把该方法的参数信息写入_data中。接着调用transact方法来发起RPC(远程过程调用)请求,同时当前
* 线程挂起,然后服务器端的ontransact方法会被调用。直到RPC过程返回后,当前线程继续执行,并从_reply
* 中取出RPC过程的返回结果,最后返回_reply中的数据。
*
* @return
* @throws android.os.RemoteException
*/
@Override
public java.util.List<java.lang.String> getBookList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<java.lang.String> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createStringArrayList();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* 运行咋看客户端,执行同上,没有返回值,所以不需要从_reply中取出返回值。
* @param book
* @throws android.os.RemoteException
*/
@Override
public void addBook(java.lang.String book) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(book);
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public java.util.List<java.lang.String> getBookList() throws android.os.RemoteException;
public void addBook(java.lang.String book) throws android.os.RemoteException;
}
关于进程间通信的博客
点击打开链接
点击打开链接
点击打开链接
点击打开链接