本文主要包含三个内容
1 什么是IPC
2 什么是Binder
3 通过AIDL来理解Binder机制
一 、什么是IPC
IPC (Inner process communication)指进程间通信,既然是进程间通信,我们经常会说到进程和线程,那么进程和线程有什么区别呢,线程是CPU调度的最小单元,而进程一般指一个执行单元,在移动设备上可以说是一个应用,一个进程可以包含多个线程。
在Android系统中一个应用默认只有一个进程,每个进程都有自己独立的资源和内存空间,其它进程不能任意访问当前进程的内存和资源,系统给每个进程分配的内存会有限制。如果一个进程占用内存超过了这个内存限制,就会报OOM的问题,很多涉及到大图片的频繁操作或者需要读取一大段数据在内存中使用时,很容易报OOM的问题,为了彻底地解决应用内存的问题,Android引入了多进程的概念,它允许在同一个应用内,为了分担主进程的压力,将占用内存的某些页面单独开一个进程,比如Flash、视频播放页面,频繁绘制的页面等。Android多进程使用很简单,只需要在AndroidManifest.xml的声明四大组件的标签中增加”android:process”属性即可,process分私有进程和全局进程,以“:”号开头的属于私有进程,其他应用组件不可以和他跑在同一个进程中;不以“:”号开头的属于全局进程,其他应用可以通过ShareUID的方式和他跑在同一个进程中;
多进程模式会出现一些问题:
1 静态成员和单利模式完全失效
2 线程同步机制完全失效(线程同步一般是锁的一个object的对象,因为在不同进程中,所以锁就没有意义)
3 SharedPreferences的可靠性下降 (不同进程同时向SharedPreferences写入数据,会出现问题,就像子线程不能刷新UI一个道理)
4 Application多次创建(一个进程对应一个Application对象) 为了避免这些问题,Android引入了Binder。
二、什么是Binder
Binder是Android中的一个类,它实现了IBinder接口。
从IPC角度来说,Binder是Android中的一种跨进程通信方式,Binder还可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder,该通信方式在Linux中没有。
从Android Framework角度来说,Binder是ServiceManager连接各种Manager(ActivityManager、WindowManager等等)和相 应ManagerService的桥梁。
从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过Binder对象,客户端就可以获取服务端提供的服务或者数据。这里的服务包括普通服务和基于AIDL的服务。
从字面上理解binder是"粘结剂"的意思,那么google的工程师为什会以"粘结剂"来命名binder呢?这是因为binder是基于C-S架构,而在这个模型中存在着四个角色,如下:
1 client 客户端进程 使用服务端的进程
2 Server 服务端进程 提供服务的进程
3 ServiceManager 用来管理Server进程的注册与查询,类似于路由器。
4 Binder驱动 连接Server进程、Client进程和ServerManager进程的桥梁,类似于网线
Client、Server 和 Service Manager 属于进程的用户空间,不可进行进程间交互。Binder驱动在内核空间中,能持有Server服务端进程的Binder实体,并给Client客户端提供Binder实体的引用。
三 通过AIDL来理解Binder机制
什么是AIDL?
为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现。与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言(Interface Definition Language,IDL)来公开服务的接口。我们知道4个Android应用程序组件中的3个(Activity、BroadcastReceiver和ContentProvider)都可以进行跨进程访问,另外一个Android应用程序组件Service同样可以。因此,可以将这种可以跨进程访问的服务称为AIDL(Android Interface Definition Language)服务。
首先模拟创建一个Server 和 Client 并且都实现AIDL接口,
服务端代码
package com.ctv.remoteserver;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import androidx.annotation.Nullable;
public class RemoteService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
class MyBinder extends IMyAidlInterface.Stub{
@Override
public String getString() throws RemoteException {
return "from service";
}
}
}
客户端代码
package com.ctv.remoteclient;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.View;
import android.widget.Toast;
import com.ctv.remoteserver.IMyAidlInterface;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
private IMyAidlInterface iMyAidlInterface;
public void bind(View view) {
Intent intent = new Intent("com.ctv.remoteservice");
intent.setPackage("com.ctv.remoteserver");
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(iBinder);
Toast.makeText(MainActivity.this,"onServiceConnected",0).show();
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
}, Context.BIND_AUTO_CREATE);
}
public void getText(View view) {
try {
Toast.makeText(this,iMyAidlInterface.getString(),0).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
AIDL接口,定义个getString()方法,
// IMyAidlInterface.aidl
package com.ctv.remoteserver;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
String getString();
}
编译生成的IMyAidlInterface.java文件
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.ctv.remoteserver;
// Declare any non-default types here with import statements
public interface IMyAidlInterface extends android.os.IInterface
{
/** Default implementation for IMyAidlInterface. */
public static class Default implements com.ctv.remoteserver.IMyAidlInterface
{
@Override public java.lang.String getString() throws android.os.RemoteException
{
return null;
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.ctv.remoteserver.IMyAidlInterface
{
private static final java.lang.String DESCRIPTOR = "com.ctv.remoteserver.IMyAidlInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.ctv.remoteserver.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static com.ctv.remoteserver.IMyAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.ctv.remoteserver.IMyAidlInterface))) {
return ((com.ctv.remoteserver.IMyAidlInterface)iin);
}
return new com.ctv.remoteserver.IMyAidlInterface.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
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getString:
{
data.enforceInterface(descriptor);
java.lang.String _result = this.getString();
reply.writeNoException();
reply.writeString(_result);
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.ctv.remoteserver.IMyAidlInterface
{
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 java.lang.String getString() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getString, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getString();
}
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public static com.ctv.remoteserver.IMyAidlInterface sDefaultImpl;
}
static final int TRANSACTION_getString = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
public static boolean setDefaultImpl(com.ctv.remoteserver.IMyAidlInterface impl) {
if (Stub.Proxy.sDefaultImpl == null && impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.ctv.remoteserver.IMyAidlInterface getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
public java.lang.String getString() throws android.os.RemoteException;
}
我们可以看到编译生成的IMyAidlInterface.java 文件,里面有两个内部类,Stub 和 Proxy,他们也是基于C/S架构,Stub相当于服务端,Proxy相当于客户端,当我们调用bindServer绑定RemoteService的时候,RemoteService首先会在本地创建一个IBinder的引用,并且同时在内核控件也创建一个IBinder的引用
当我们在客户端调用getString()方法的时候,客户端这边的proxy会向IBinder中写入数据,然后IBinder再向服务端的stub写入数据,然后服务端这边将结果返回给IBinder,IBinder再返回给客户端这边的stub,这样就完成了一次跨进程通信。