IPC机制
1. 为何要开启多进程
为何开启android应用要开启多进程,主要有以下几点: (1)单进程所分配的内存不够,需要更多的内存。在早期android系统只为一个单进程的应用分配了16M的可用内存,随着手机的硬件的提升和android系统的改进,虽然可分配内存越来越多,但仍旧可以通过开启多进程来获取更多的内存来处理自己App的业务 (2)独立运行的组件,比如个推,它的服务会另开一个进程。 (3)运行一些”不可见人”的操作,比如获取用户的隐私数据,比如双守护进程来防止被用户杀掉
2. 开启多进程
android:process=":remote" //进程名 用adb命令可以查看进程信息 adb shell ps
用Messenger进行进程间通信 :
Messenger可以在不同进程中传递Message对象,我们在Message中加入我们想要传的数据就可以在进程间的进行数据传递了。 服务端:
public class MyService extends Service {
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
Log.d("TAG", "onBind: ");
// TODO: Return the communication channel to the service.
//Messenger创建一个IBinder,服务端通过onBind()方法使其返回客户端
return new Messenger(handler).getBinder();
}
@Override
public void onCreate() {
super.onCreate();
Log.d("TAG", "onCreate: ");
}
private Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what){
//1 客户端传来的消息
case 1:
//接收到客户端的消息
Log.d("TAG", "我是服务端,收到你的消息: "+msg.getData().get("lala"));
//返回给客户端的消息
Message msgToClient = Message.obtain(null,2);
//得到客户端传来的Messenger对象
Messenger clientMessenger = msg.replyTo;
try {
clientMessenger.send(msgToClient);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
};
}
AndroidMainfest.xml
<service
android:name=".MyService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="haha"/>
</intent-filter>
</service>
客户端
public class MainActivity extends AppCompatActivity {
private Messenger mMessenger;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//绑定服务
Intent intent = new Intent("haha");
intent.setPackage("com.thundersoft.ipcdemo");
bindService(intent,conn,BIND_AUTO_CREATE);
}
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("TAG", "已经绑定服务: ");
//客户端使用IBinder将Messenger(引用服务的Handler)实例化,
mMessenger = new Messenger(service);
Message message = Message.obtain(null,1);
Bundle bundle = new Bundle();
bundle.putString("lala","你好,我是客户端,请问你收到信息了吗?");
message.setData(bundle);
//将Messenger传递给服务端
message.replyTo=new Messenger(mHandler);
try {
mMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private Handler mHandler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 2:
Log.d("TAG", "服务端返回的消息: ");
break;
}
}
};
}
注意:在客户端的AndroidMainfest.xml中导入服务的包 :
<queries>
<package android:name="com.thundersoft.ipcdemo"/>
</queries>
总结: (1) <服务端>Messenger创建一个IBinder,服务端通过onBind()方法使其返回客户端 (2)<客户端>客户端使用IBinder将Messenger(引用服务的Handler)实例化,然后使用后者将Message对象发送给服务端。
(3)服务端收到了客户端的消息,服务端回应客户端。 首先在handleMessage回调中收到客户端信息时,我们调用Message.replyTo得到客户端传过来的Messenger对象,创建消息并通过Messenger发送给客户端。
: 是通过各自的messenger来进行通信的,所以思想是想法获得对方的messenger
通过Binder的onTransact完成跨进程通信
我们 android studio 新建两个工程(两个 moudle 也可以,这里目的为创建两个运行在不同进程的app),一个Server,一个Client,而后,在Server中,创建类继承android.os.Binder,重写方法onTransact(int code, Parcel data, Parcel reply, int flags),注意这四个参数; (1) code:方法标识符,因为Client端对Server端的所有调用都会走到Server端的这个方法,所以理所应当Client端应该传递一个参数过来用以表示要调用哪个方法,注意这个int类型的标识必须介于 FIRST_CALL_TRANSACTION 和 LAST_CALL_TRANSACTION之间,所以我们给方法分配code的时候最好使用FIRST_CALL_TRANSACTION+n 这种方式 (2)data :Client传递过来的序列化数据包,Parcel类型 (3)reply: 如果Client端调用时需要返回值,Server通过这个对象将返回值传递回去,同样Parcel类型 (4)flag 用来区分这个调用是普通调用还是单边调用,普通调用时,Client端线程会阻塞,直到从Server端接收到返回值(所以如果Client端是主线程调用,其调用的Server端不宜做耗时操作,这会让造成Client的ANR),若flag==IBinder.FLAG_ONEWAY,则这次调用是单边调用,Client在传出数据后会立即执行下一段代码,此时两端异步执行,单边调用时函数返回值必须为void (也就是异步调用必须舍弃返回值,要返回值就必须阻塞等待)
那我们先来看一下Parcel 的用法 : (1)Parcel的获取: Parcel parcle = Parcel.Obtain(); <Parcel的初始化也是由其对象池进行初始化的,Parcel是一个容器> (2)向Parcel中传入一个Int型的数据 :parcel.writeString(String val); (3)在完成了数据的写入之后,就需要进行数据的序列化:parcel.marshall(); (4)在经过上一步的处理之后,返回了一个byte数组,主要的IPC相关的操作主要就是围绕此byte数组进行的。 (5)parcel的销毁 :parcel.recycle(); (6)再进行了IPC的操作之后,一般读取出来的就是之前序列化的byte数组,所以,首先要进行一个反序列化操作,即如下的操作: parcel.unmarshall(byte[] data, int offest, int length); 其中的参数分别是这个byte数组,以及读取偏移量,以及数组的长度。 (7)parcel.readInt(); (8)parcel.recycle(); 详细请参考 链接: https://blog.csdn.net/rainbowchou/article/details/54294394?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163288163116780261933448%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=163288163116780261933448&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduend~default-2-54294394.first_rank_v2_pc_rank_v29&utm_term=Parcel&spm=1018.2226.3001.4187. Service :
public class Stub extends android.os.Binder {
//用于标识调用的Binder
private static final java.lang.String DESCRIPTOR = "MyBinder";
//方法标识,这里我们准备两个方法,一个无参,一个有参
private static final int TRANSACTION_method0 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
private static final int TRANSACTION_method1 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case TRANSACTION_method0: {
//Store or read an IBinder interface token
//验证Binder标识
data.enforceInterface(DESCRIPTOR);
//调用实现方法
this.method0();
reply.writeNoException();
return true;
}
case TRANSACTION_method1: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
//按写入的顺序读取数据
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.method1(_arg0, _arg1);
reply.writeNoException();
//向Client写回返回值
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
//Server端真正实现业务的两个方法
public void method0() throws RemoteException {
Log.e("Server", Process.myPid() + " " + Process.myTid() + " " + Process.myUid() + " " + "method0");
}
public int method1(int a, int b) throws RemoteException {
Log.e("Server", Process.myPid() + " " + Process.myTid() + " " + Process.myUid() + " " + "method1" + " " + a + " " + b);
return a + b;
}
}
绑定服务否返回Binder对象: 通信的桥梁
@Override
public IBinder onBind(Intent intent) {
return new Stub();
}
client端 : (1)绑定服务
//定义常量
//注意两个工程中对应的标识符必须相同
static final String DESCRIPTOR = "MyBinder";
//方法标识
static final int TRANSACTION_method0 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_method1 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.xxx.server", "com.xxx.server.ServerService"));
boolean b = bindService(intent, conn, BIND_AUTO_CREATE);
Log.e("Client", " "+b);
}
```
(2)bindService 的第二个参数,ServiceConnnection,在这个回调中我们取得IBinder对象,这个对象是Server端在Client中的一个代理对象
```
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder iBinder) {
//这个IBinder对象iBinder,可以调用c++层Binder代理并最终通过Binder驱动传递数据
Parcel _data0 = Parcel.obtain();//申请传递参数的Parcel对象(从Parcel池中取出)
Parcel _reply0 = Parcel.obtain();//申请接收返回值的Parcel对象,相当于数据载体
Parcel _data1 = Parcel.obtain();
Parcel _reply1 = Parcel.obtain();
try {
//调用第一个方法
//写入Binder标识,以便服务端验证
_data0.writeInterfaceToken(DESCRIPTOR);
//传入方法标识,以便服务端知道我们要调用哪个方法,注意最后一个参数,就是上面提到的 //flag,如果我们传入IBinder.FLAG_ONEWAY,则这次调用为单边调用,这个方法会立即返 //回,不会等服务端方法返回
iBinder.transact(TRANSACTION_method0, _data0, _reply0, 0);
_reply0.readException();
//调用第二个方法
_data1.writeInterfaceToken(DESCRIPTOR);
//按顺序写入参数
_data1.writeInt(1);
_data1.writeInt(2);
//从下面这行代码开始本线程会阻塞,直到服务端进程中调用的方法完成计算返回后这个线程继 //续运行,计算的返回值放入_reply1中
iBinder.transact(TRANSACTION_method1, _data1, _reply1, 0);
_reply1.readException();
int i = _reply1.readInt();//从reply中读取返回值,这里我们就得到了服务端计算后的结果
} catch (RemoteException e) {
e.printStackTrace();
} finally {
//回收Parcel
_data0.recycle();
_reply0.recycle();
_data1.recycle();
_reply1.recycle();
}
Log.e("Client", Process.myPid() + " " + Process.myTid() + " " + Process.myUid() + " " + "method0");
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
|