IPC是一种概念:进程间的通信,有很多实现方法,例如android系统内的binder、contentProvider等
- 一个
进程
中可以有多个线程
,也可以只有一个线程,在android系统中,这唯一的线程就是主线程,即UI线程
,用来操作界面元素
- android系统对每个应用都有内存限制,如果需要扩大内存就可以通过创建新进程来获取更大内存
一个应用内的多进程 --- android:process
android系统中,一个应用内使用多进程方法,给四大组件定义属性android:process
,还有一种就是native层的fork新进程
- 实例
<activity android:name=".MainActivity"
android:excludeFromRecents="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".secondActivity"
android:process=":remote">
<intent-filter>
<action android:name="secondactivity" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name=".thirdActivity"
android:process="com.app.typicallifecycle.remote">
<intent-filter>
<action android:name="thirdactivity" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
结果:成功创建出两个新进程:
代表当前应用的私有进程,其他应用组件不可以和它跑在一个进程中没用到:
代表是全局进程,可以通过ShareUID的方式跑在一个进程中(还需要签名相同)
系统会为每一个应用分配一个UID,通过UID可以共享应用数据
u0_a30 9930 182 927684 42572 ffffffff b764535b S com.app.typicallifecycle
u0_a30 9956 182 931808 40108 ffffffff b764535b S com.app.typicallifecycle:remote
u0_a30 9969 182 936668 39852 ffffffff b764535b S com.app.typicallifecycle.remote
多进程的运行机制的一些问题
- 实例:
- 定一个有静态变量的类
public class userProcess {
public static int number=0;
}
- 在MainActivity中自加1,然后日志输出、接着去sencondactivity中输出这个静态变量,看结果,sencondactivity中的值依然是0
11-30 17:33:42.524 10189-10189/? E/xxxxx: number的值为1
11-30 17:33:45.244 10215-10215/com.app.typicallifecycle:remote E/xxxxx: number的值为0
为什么会出现这种情况?
- Android系统为每个应用(进程)分配一个虚拟机
- 每个虚拟机使用的内存地址是不一样的,所以依靠内存地址进行数据共享不会成功的。也就是这个两个进程中都有一个userProcess 类,他们互不干扰
多进程常出现的问题:
- 静态类、单例模式失效(如上)
- SharedPreference可靠性下降:SharedPreference是不支持两个进程同时的写操作的(底层是对xml文件的读写,并发读写是会出问题的)
- Application重复创建:为组件创建新进程,首先创建虚拟机、然后创建相应的应用,然后启动组件。具体实现如下
将这个myApplication 放入AndroidManifest文件,作为应用的实现类
public class myApplication extends Application {
private static Context context;
private int processID;
@Override
public void onCreate() {
context = getApplicationContext();
processID = Process.myPid();
Log.e("process", String.valueOf(processID));
super.onCreate();
}
开启,活动的时候,这个应用被创建了多次
11-30 18:09:10.814 10762-10762/com.app.typicallifecycle E/process: 10762
11-30 18:09:11.894 10788-10788/com.app.typicallifecycle:remote E/process: 10788
11-30 18:09:14.584 10801-10801/com.app.typicallifecycle.remote E/process: 10801
多进程一些方法
即使多进程有一些问题,当时依然可以被克服,这就是Ibinder
、Intent
,先引入基础,序列化和反序列化
Serializable
用法:随意声明一个类继承Serializable
接口,它内部的属性就可以实例化
public class userProcess implements Serializable {
private static final long serialVersionUID=111;
public String fater;
public String getFater() {
return fater;
}
public void setFater(String fater) {
this.fater = fater;
}
}
具体序列化和反序列化过程
//序列化过程
userProcess u1 = new userProcess();
u1.setFater("father007");
try {
File file = new File("/sdcard/cache.txt");
ObjectOutputStream objoutput = new ObjectOutputStream(new FileOutputStream(file));
objoutput.writeObject(u1);
objoutput.close();
} catch (IOException e) {
e.printStackTrace();
}
//反序列化过程
try {
FileInputStream filein = new FileInputStream(new File("/sdcard/cache.txt"));
ObjectInputStream objIN = new ObjectInputStream(filein);
userProcess u1 = (userProcess) objIN.readObject();
Log.e("userprocess", u1.getFater());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
serialVersionUID
:需要的注意一个属性,是一个类的HASH值。
- 序列化和反序列化过程中的标识码,如果这个值在序列化和反序列化的过程中不一样,是不能成功反序列化的
- 这个UID是保证这样一种情况的,当序列化完一个对象到本地后,过了一段时间要进行反序列化的时候,由于这个被序列化的类属性发生了更改,如果是默认标识码的话,这个hash值会更改,就会失败,但是如果手动设置好固定的serialVersionUID,就可以尽量避免这种情况,但是如果类的改动过大,还是出问题的。
例如:
序列化之前的类:
public class userProcess implements Serializable {
private static final long serialVersionUID=111;
public String fater;
public String getFater() {
return fater;
}
public void setFater(String fater) {
this.fater = fater;
}
}
序列化完后,对这个类的属性进行了更改,多了一个son属性,但是其他没什么改变,这个小改动,并且没有改变被序列化的属性,是不会影响反序列化的
private static final long serialVersionUID=110;
public static int number=0;
public String fater;
public String son;
但是如果改动的是fater这个属性的化就会出问题,因为这个属性是被序列化的属性,如果类属性更改,就会出现不匹配,报出异常
public int fater;
Parcelable
另一种序列化的接口
,实现序列化之后可以通过Intnet、Binder传输序列化对象。他的具体是实现是:将序列化和反序列化都封装在一个接口中
简单的实例
public class userProcess implements Parcelable {
private String father;
private int son;
private Boolean mom;
public String getFather() {
return father;
}
public int getSon() {
return son;
}
public Boolean getMom() {
//描述信息,基本上都是返回0
@Override
public int describeContents() {
return 0;
}
/*
先实例化对象,然后序列化对象
*/
public userProcess(String f, int s, Boolean m){
father=f;
son=s;
mom=m;
}
//序列化过程
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(father);
dest.writeInt(son);
//bool型数据用int存储
dest.writeInt(mom ? 1 : 0);
}
//反序列化过程
public static final Creator<userProcess> CREATOR = new Creator<userProcess>() {
@Override
public userProcess createFromParcel(Parcel in) {
return new userProcess(in);
}
@Override
public userProcess[] newArray(int size) {
return new userProcess[size];
}
};
protected userProcess(Parcel in) {
father=in.readString();
son=in.readInt();
mom=in.readInt()==1;
}
}
Serializable和Parcelable的区别:Serializable是java提供的序列化接口,存在许多I/O操作,消耗比Parcelable大,建议使用于本地持久化也就是往本地存数据
等方面。而Parcelable是android提供的,使用虽然比serializable复杂点,但是更高效一些
Binder
从哪来::继承iBinder接口,是其实现类
到哪去::1. 在android framwork里,它是连接各种manager的桥梁 2.在Application层:他是客户端和服务端通信的媒介(这里的客户端和服务端可以看作同一App内的两个进程,也可以看作同一厂家的不同App),服务端返回一个binder给客户端,通过binder让其访问服务端的资源和服务 ,主要应用于同一个App内的跨进程通信
,下面从AIDL看binder
这里引入了一个概念AIDL:android接口定义语言
为什么使用它?
官网解释:注:只有允许不同应用的客户端用 IPC 方式访问服务,并且想要在服务中处理多线程时,才有必要使用 AIDL。 如果您不需要执行跨越不同应用的
并发
IPC,就应该通过实现一个 Binder 创建接口;或者,如果您想执行 IPC,但根本不需要处理多线程,则使用 Messenger 类来实现接口
简单的实例:
先右键new 一个AIDL file,下面我只定义了一个方法,用来获取PID:int getPID();
// IMyAidlInterface.aidl
package com.app.typicallifecycle;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
int getPID();
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}
然后点击菜单build->rebuild project就可以手动利用gradle在build/generated/source/aidl/包名/IMyAidlInterface.java方法,构建相应接口的java文件并且会在内部自动填写int getPID();
。这个文件内部有一个子类,是继承Binder类的,即IMyAidlInterface.sub这个类的实例就是具体客户端和服务端传递数据、服务的传输工具
public static abstract class Stub extends android.os.Binder implements com.app.typicallifecycle.IMyAidlInterface
ibinder = IMyAidlInterface.Stub.asInterface(service);
:主要这一步,将ibinder对象转成IMyAidlInterface类型对象,否则跨进程的时候会报错,不跨进程不会出错,因为不跨进程,服务启动之后就在这个进程中,直接返回一个IMyAidlInterface.Sub类,当然没问题了。但是如果服务开启在另一个进程中,返回的是一个BinderProxy类,是不能强转程IMyAidlInterface.Sub类的,所以需要asInterface方法来转化
客户端关键代码
private ServiceConnection myserviceconnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//连接上服务的回调函数
Log.e("xxxxx", name.getClassName());
ibinder = IMyAidlInterface.Stub.asInterface(service);
try {
int pid = ibinder.getPID();
Log.e("xxxxx", "pid等于"+ pid);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
//断开连接的回调函数
}
};
bindService(new Intent("AIDLservice"), myserviceconnection, BIND_AUTO_CREATE);
记住别忘了在AndroidManifest.xml中注册服务组件
稍微复杂的实例:
引入自定义类Book类,也就是说需要过进程传输这个对象。引入这个自定义类之前我们需要知道,AIDL文件有很多系统默认的基础类是可以序列化的,所以我们这个自定义类也需要序列化,并且生成一个aidl文件,供给其他aidl文件使用
- 先自定Book类
package com.app.typicallifecycle;
import android.os.Parcel;
import android.os.Parcelable;
public class Book implements Parcelable {
private int BookID;
private String BookName;
public Book(int bookid, String bookname){
BookID = bookid;
BookName = bookname;
}
//序列化
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(BookID);
dest.writeString(BookName);
}
//反序列化
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
protected Book(Parcel in) {
BookID = in.readInt();
BookName = in.readString();
}
@Override
public int describeContents() {
return 0;
}
}
- 生成aidl文件,让其他aidl接口文件使用
必须:New->file
,不要new一个AIDL文件,android studio会识别会接口文件,在generated目录下生成java文件
package com.app.typicallifecycle;
parcelable Book;
- 接口文件:将需要给客户端使用的功能接口,数据接口都在这定义
这里有个点需要注意:in Book book
,从客户端传参数到服务端,数据流向是从客户端到服务端,用in。如果是数据流向流向服务端就用out,即服务端数据更改,服务端也跟着更改,但是如果这是向服务端传数据,就是空对像。双向流通inout。
// BookManager.aidl
package com.app.typicallifecycle;
// Declare any non-default types here with import statements
import com.app.typicallifecycle.Book;
interface BookManager {
void addBooks(in Book book);
Book getBook();
}
4.服务端
package com.app.typicallifecycle;
import com.app.typicallifecycle.Book;
public class AIDLService extends Service {
private final BookManager.Stub bookManager = new BookManager.Stub() {
@Override
public void addBooks(Book book) throws RemoteException {
}
@Override
public Book getBook() throws RemoteException {
return null;
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return bookManager;
}
}
这里需要注意一下,因为要用到Book这个自定义的类,但是这个类没有放在java包内,而且它默认是不检查main/aidl包内的java文件,需要手动加入,所以直接在build.gradle内android下加入如下代码即可:
sourceSets {
main {
java.srcDirs = ['src/main/java', 'src/main/aidl']
}
}
5.客户端:部分关键代码
private ServiceConnection myserviceconnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//连接上服务的回调函数
Log.e("xxxxx", name.getClassName());
ibinder = BookManager.Stub.asInterface(service);
Book book1 = new Book(007, "android艺术探索");
try {
ibinder.addBooks(book1);
Log.e("xxxxx", "上传完第一本书:艺术探索");
} catch (RemoteException e) {
e.printStackTrace();
}
try {
Book book;
book = ibinder.getBook();
Log.e("xxxxx", "下载完书籍:"+book);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
//断开连接的回调函数
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.e("xxxxx", "onCreate: 活动创建");
super.onCreate(savedInstanceState);
editText = findViewById(R.id.editText);
setContentView(R.layout.activity_main);
button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
bindService(new Intent("AIDLservice"), myserviceconnection, BIND_AUTO_CREATE);
}
});
}
主要分析一下BookManager.java文件,他是android studio自动生成的,也是AIDL的目的所在,就是为了方便我们来写这种接口,这个文件存在不同进程通信的逻辑所在
首先客户端请求绑定另外一个进程的服务,来获取上传书籍和下载书籍的功能。服务端返回一个Binder对象来作为双方通信的桥梁。服务端把所有向客户端开放的接口都放在了proxy
类下。将需要输入的参数有_data
传递,将需要返回的数据由_reply
传递,然后将请求通过transact
传递到服务端
private static class Proxy implements com.app.typicallifecycle.BookManager
{
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;
}
@Override public void addBooks(com.app.typicallifecycle.Book 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);
if ((book!=null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBooks, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public com.app.typicallifecycle.Book getBook() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.app.typicallifecycle.Book _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBook, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = com.app.typicallifecycle.Book.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
然后另一个进程上的服务端通过这个onTransact
在线程池中的方法,来处理客户端的请求,从code
获取客户端请求的方法、从data
获取参数、如果有返回值通过reply
返回,如果返回false表示请求失败,通过这个做了一个权限控制,有选择的允许访问
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_addBooks:
{
data.enforceInterface(descriptor);
com.app.typicallifecycle.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.app.typicallifecycle.Book.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addBooks(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getBook:
{
data.enforceInterface(descriptor);
com.app.typicallifecycle.Book _result = this.getBook();
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
然后就是将远程传递过来的binder对象转成客户端可以使用的binder对象通过asInterface
方法
/**
* Cast an IBinder object into an com.app.typicallifecycle.BookManager interface,
* generating a proxy if needed.
*/
public static com.app.typicallifecycle.BookManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.app.typicallifecycle.BookManager))) {
return ((com.app.typicallifecycle.BookManager)iin);
}
return new com.app.typicallifecycle.BookManager.Stub.Proxy(obj);
}
还有一个asbinder
方法用来获取当前binder
小结一下:其实上面的内容,不使用AIDL文件也可以实现,因为AIDL就是为了方便生成接口文件,如本例中的Manager.java,不过为了方便高效来说AIDL肯定更方便,手动写那么多代码也是挺累的
最后一个小点
由于跨进程通信,如果服务端出现问题终止了,客户端却不知到服务端的情况,有可能继续发起请求,但是这样有可能会影响客户端的运行,所以引入死亡代理这个概念。通过linkToDeath
和unlinkToDeath
,相关介绍可以看API上面的介绍linkToDeath是注册接收一个服务死亡时通知,unlinkToDeath是移除前面注册的死亡通知
绑定好远程服务,开启死亡代理service.linkToDeath(deathRecipient, 0);
private ServiceConnection myserviceconnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//连接上服务的回调函数
Log.e("xxxxx", name.getClassName());
bookManager = BookManager.Stub.asInterface(service);
try {
service.linkToDeath(deathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
Book book1 = new Book(007, "android艺术探索");
try {
bookManager.addBooks(book1);
Log.e("xxxxx", "上传完第一本书:艺术探索");
} catch (RemoteException e) {
e.printStackTrace();
}
try {
Book book;
book = bookManager.getBook();
Log.e("xxxxx", "下载完书籍:"+book);
} catch (RemoteException e) {
e.printStackTrace();
}
}
这是一个Ibinder下的接口
//死亡代理
private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
@Override
//如果服务端进程死掉,回调此方法
public void binderDied() {
Log.e("xxxxx","接受到死亡通知");
//断开连接的回调函数
if (bookManager==null)
return;
//移除之前的死亡通知
bookManager.asBinder().unlinkToDeath(deathRecipient, 0);
//重新绑定服务
bookManager=null;
bindService(new Intent("AIDLservice"), myserviceconnection, BIND_AUTO_CREATE);
}
};
杀死进程名为app.service.remote
的进程
ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> processinfolist = activityManager.getRunningAppProcesses();
for (int i=0; i<processinfolist.size(); i++){
String processName = processinfolist.get(i).processName;
if (processName.equals("app.service.remote")) {
int pid = processinfolist.get(i).pid;
Process.killProcess(pid);
}