Android之IPC机制
IPC全称是Intent Process Communication即进程通信。
为什么需要进程通信呢?
android是基于linux内核的,linux中两个进程锁分配的虚拟机的地址是不一致的,是两个完全不同
的虚拟机,所以也就意味着两个进程运行不相互影响(进程隔离)。linux中对于进程之间的交流有着
限制,那么在Android中也一样。linux中的进程通信有管道,socket,那么Android中相对应的就有
Binder机制。
由于进程之间交流存在限制,所以需要进程通信,方便进程之间传递数据等信息。
Binder是什么?
binder的中文意思是粘合剂,也就意味者把两个进程粘合在一起,即所谓的进程通信。
1.直接来说,Binder是Android中的一个类,实现了IBinder接口。
2.从ICP角度说,Binder是Android中一种跨进程通信的机制。
3.从Android Framework角度说,它是ServiceManager连接各种Manager(比如ActivityManager
WindowManager等)和相应的ManagerService的桥梁。
4.从应用层来说,Binder是客户端和服务端进行通信的媒介。
在Android中,Binder主要用在Service中,包括AIDL和Messenger。(普通service不涉及进程通信)
通过AIDL来实现进程通信,理解了AIDL的模式对Binder通信的机制会更加容易认识。
在项目中新建一个AIDL文件。
// IProcess.aidl
package com.example.lu.okhttpdemo;
// Declare any non-default types here with import statements
interface IProcess {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
int add(int x, int y);
int min(int x, int y);
}
接着Make Project。然后会在build/generated/source/aidl生成一个相对应的文件。
我们先新建一个服务端Service
package com.example.lu.okhttpdemo;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;
public class MyService extends Service {
private static final String TAG = "service";
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind");
return mBinder;
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy");
super.onDestroy();
}
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG, "onUnbind");
return super.onUnbind(intent);
}
@Override
public void onRebind(Intent intent) {
Log.d(TAG, "onDRebind");
super.onRebind(intent);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onCreate() {
Log.d(TAG, "onCreate");
super.onCreate();
}
private final IProcess.Stub mBinder = new IProcess.Stub() {
@Override
public int add(int x, int y) throws RemoteException {
return x + y;
}
@Override
public int min(int x, int y) throws RemoteException {
return x - y;
}
};
}
在AndroidManifest文件中注册service
<service android:name=".MyService"
android:process=":remote">
<intent-filter>
<action android:name="com.example.lu.calc" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
这里我加了process属性,就是为service单独开启一个进程。用来模拟多进程通信。
客户端的activity代码
package com.example.lu.okhttpdemo;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class Main2Activity extends AppCompatActivity implements View.OnClickListener {
private IProcess iProcess;
private ServiceConnection serviceConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("client", "onServiceConnected");
iProcess = IProcess.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d("client", "onServiceDisConnected");
}
};
private Button button1, button2, button3, button4;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
button1 = findViewById(R.id.button1);
button2 = findViewById(R.id.button2);
button3 = findViewById(R.id.button3);
button4 = findViewById(R.id.button4);
button1.setOnClickListener(this);
button2.setOnClickListener(this);
button3.setOnClickListener(this);
button4.setOnClickListener(this);
}
public void bindService() {
Intent intent = new Intent("com.example.lu.calc");
intent.setPackage("com.example.lu.okhttpdemo");
bindService(intent, serviceConn, Context.BIND_AUTO_CREATE);
}
public void unbindService() {
if (serviceConn != null)
unbindService(serviceConn);
}
public void addInvoked() throws RemoteException {
if (iProcess != null) {
int addRes = iProcess.add(12, 12);
Toast.makeText(this, addRes + " ", Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(this, "异常,重新绑定", Toast.LENGTH_SHORT).show();
}
}
public void minInvoked() throws RemoteException {
if (iProcess != null) {
int addRes = iProcess.min(58, 12);
Toast.makeText(this, addRes + " ", Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(this, "异常,重新绑定", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button1:
bindService();
break;
case R.id.button2:
unbindService();
break;
case R.id.button3:
try {
addInvoked();
} catch (RemoteException e) {
e.printStackTrace();
}
break;
case R.id.button4:
try {
minInvoked();
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
}
相应的布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="@+id/button1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="BindService" />
<Button
android:id="@+id/button2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="UnbindService" />
<Button
android:id="@+id/button3"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="12+12" />
<Button
android:id="@+id/button4"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="50-12" />
</LinearLayout>
运行,查看结果
绑定服务,查看log日志:
成功,客户端连接到了另一个进程的service
08-09 05:02:07.856 3988-3988/? D/client: onServiceConnected
08-09 05:02:07.837 4024-4024/com.example.lu.okhttpdemo:remote D/service: onCreate
08-09 05:02:07.838 4024-4024/com.example.lu.okhttpdemo:remote D/service: onBind
其它的输出可以自己尝试。
分析AIDL自动生成的文件
我们在build/generated/source/aidl里面打开生成的文件。我这里是排了下版,方便浏览
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: C:\\Users\\74650\\AndroidStudioProjects\\OkHttpDemo\\app\\src\\main\\aidl\\com\\example\\lu\\okhttpdemo\\IProcess.aidl
*/
package com.example.lu.okhttpdemo;
// Declare any non-default types here with import statements
public interface IProcess extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.lu.okhttpdemo.IProcess
{
private static final java.lang.String DESCRIPTOR = "com.example.lu.okhttpdemo.IProcess";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.lu.okhttpdemo.IProcess interface,
* generating a proxy if needed.
*/
public static com.example.lu.okhttpdemo.IProcess asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.lu.okhttpdemo.IProcess))) {
return ((com.example.lu.okhttpdemo.IProcess)iin);
}
return new com.example.lu.okhttpdemo.IProcess.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@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_add:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_min:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.min(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.example.lu.okhttpdemo.IProcess
{
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 int add(int x, int y) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(x);
_data.writeInt(y);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public int min(int x, int y) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(x);
_data.writeInt(y);
mRemote.transact(Stub.TRANSACTION_min, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_min = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public int add(int x, int y) throws android.os.RemoteException;
public int min(int x, int y) throws android.os.RemoteException;
}
DESCRIPTOR Binder的唯一标识,一般为当前的Binder类名标识。
asInterface(android.os.IBinder obj)
该方法用于将服务度的Binder对象转换为客户端所需的AIDL接口类型的对象。如果客户端和服务端在同一进程中
那么返回的就是服务端的Stub对象本身,如果不同进程,那么返回的是系统封装后的Stub.proxy(代理)对象
由上面第一章图可以得知,proxy对象是由ServiceManager返回的。
asBinder()
返回当前Binder对象
onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) *重点*
该方法原型为Binder类中的
onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)方法
code参数代表客户端请求的目标方法,data中为目标方法所需的参数,就是客户端传递过来的参数,
reply为服务端返回给客户端的值,
flag为是否有返回值,0为有(双向),1为没有(单向)
Proxy这个类就是代理类
理解完这个AIDL生成的文件之后,我们接下来看看这两个进程是如何通信的。
Binder进程通信的机制
首先,我们的Activity(客户端)通过创建一个ServiceConnection来进行对Service(服务端)的连接
,如果连接成功,那么Service就会在onBind回调方法中返回一个IBinder对象,接着我们使用刚才
AIDL文件生成的类Stub的asInterface(android.os.IBinder obj) 方法,传进IBinder对象,得
到一个代理的对象,里面包含者Service(服务端)相关的调用方法,接着通过这个代理对象调用相关
方法来进行对应的操作。
接下来我们不适用AIDL文件来实现Binder。
手动实现Binder,不依赖AIDL
Service(服务端)代码
package com.example.lu.okhttpdemo;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
public class MyService extends Service {
private static final String TAG = "service";
private static final String DESCRIPTOR = "MyService";
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind");
return mBinder;
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy");
super.onDestroy();
}
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG, "onUnbind");
return super.onUnbind(intent);
}
@Override
public void onRebind(Intent intent) {
Log.d(TAG, "onDRebind");
super.onRebind(intent);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onCreate() {
Log.d(TAG, "onCreate");
super.onCreate();
}
private MyBinder mBinder = new MyBinder();
private class MyBinder extends Binder{
@Override
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
switch (code) {
case 0x110:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();//读取客户端发过来的第一个参数
int _arg1;
_arg1 = data.readInt();//读取客户端发过来的第二个参数
int _result = _arg0 + _arg1;
reply.writeNoException();
reply.writeInt(_result);//结果写进reply中,通过Binder传递回给客户端
return true;
}
case 0x111:
{
data.enforceInterface(DESCRIPTOR);
int _arg0 = data.readInt();
int _arg1 = data.readInt();
int _result = _arg0 - _arg1;
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
}
}
Activity(客户端)代码
package com.example.lu.okhttpdemo;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class Main2Activity extends AppCompatActivity implements View.OnClickListener {
private IBinder iBinder;
private ServiceConnection serviceConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("client", "onServiceConnected");
iBinder = service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d("client", "onServiceDisConnected");
}
};
private Button button1, button2, button3, button4;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
button1 = findViewById(R.id.button1);
button2 = findViewById(R.id.button2);
button3 = findViewById(R.id.button3);
button4 = findViewById(R.id.button4);
button1.setOnClickListener(this);
button2.setOnClickListener(this);
button3.setOnClickListener(this);
button4.setOnClickListener(this);
}
public void bindService() {
Intent intent = new Intent("com.example.lu.calc");
intent.setPackage("com.example.lu.okhttpdemo");
bindService(intent, serviceConn, Context.BIND_AUTO_CREATE);
}
public void unbindService() {
if (serviceConn != null)
unbindService(serviceConn);
}
public void addInvoked() throws RemoteException {
if (iBinder != null) {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
int _result;
try {
//写入传递的参数数值
_data.writeInterfaceToken("MyService");//通过该名字查找到相应的服务端
_data.writeInt(12);
_data.writeInt(12);
//通过transact方法传递参数,通过Binder调用相关代理方法
iBinder.transact(0x110, _data, _reply, 0);
_reply.readException();
//获取服务端返回的数值
_result = _reply.readInt();
Toast.makeText(this, _result + " ", Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
} finally {
_reply.recycle();
_data.recycle();
}
}
else {
Toast.makeText(this, "异常,重新绑定", Toast.LENGTH_SHORT).show();
}
}
public void minInvoked() throws RemoteException {
if (iBinder != null) {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken("MyService");
_data.writeInt(50);
_data.writeInt(12);
iBinder.transact(0x111, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
Toast.makeText(this, _result + " ", Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
} finally {
_reply.recycle();
_data.recycle();
}
}
else {
Toast.makeText(this, "异常,重新绑定", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button1:
bindService();
break;
case R.id.button2:
unbindService();
break;
case R.id.button3:
try {
addInvoked();
} catch (RemoteException e) {
e.printStackTrace();
}
break;
case R.id.button4:
try {
minInvoked();
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
}
运行结果一致,Binder进程通信可以不依靠AIDL。其实AIDL文件只是为了方便系统帮我们生成代码
节省一定的工作量。
代码参考自鸿洋前辈
图文参考自《Android开发艺术探索》