Android的进程间通信(六)之 Binder的客户端发送数据到服务端的流程

上一篇文章,介绍了Binder 内核层的注册代码以及ServiceManager初始化的过程。
而我们平时在使用进程间通信时,只需要关注的“客户端”,“服务端”等概念。今天,就让我们来看一下,我们平时在使用AIDL的背后,是如何涉及到Binder内核和ServiceManager的。

AIDL的使用过程:
第一:我们定义一个AIDL文件

interface IAIDLService {
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,double aDouble, String aString);
}

第二:在我们的页面上,通过 bindService 的方式启动服务,并且在onServiceConnected方法中,把IBinder接口通过IAIDLService.Stub.asInterface(service)转化成IAIDLService接口,然后就可以通过IAIDLService接口调用对应的接口即可。

bindService(intent, new ServiceConnection() {
  @Override
   public void onServiceConnected(ComponentName name, IBinder service) {
       IAIDLService iaidlService = IAIDLService.Stub.asInterface(service);
       try {
           iaidlService.basicTypes(1,2,true,4,5,"6");
       } catch (RemoteException e) {
           e.printStackTrace();
       }
   }

   @Override
   public void onServiceDisconnected(ComponentName name) {
   }
}, Context.BIND_AUTO_CREATE);

第三:在我们的服务端,需要实现 IBinder onBind(Intent intent) 方法,然后实例化new IAIDLService.Stub() 并且再onBind中返回回去。注意:Service要配置运行在另外一个进程。

private IAIDLService.Stub binder = new IAIDLService.Stub() {
    @Override
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
        Log.e("TAG","basicTypes:anInt:" + anInt + " aLong:" + aLong + " aBoolean:" + aBoolean + " aFloat:" + aFloat
            + " aDouble:" + aDouble + " aString:" + aString);
    }
};

这样,我们就实现了从客户端进程发送数据到服务端进程了。
平时,我们使用的AIDL是经过层层封装,那么背后它的原理是什么样子的呢?

我们首先看到 IAIDLService iaidlService = IAIDLService.Stub.asInterface(service);这一行代码。IAIDLService是一个接口,所以我们先要知道,IAIDLService的具体实现是谁。所以,我们看一下asInterface方法。

public static com.bzl.a929demo.service.aidl.IAIDLService asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
        return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof com.bzl.a929demo.service.aidl.IAIDLService))) {
        return ((com.bzl.a929demo.service.aidl.IAIDLService) iin);
    }
    return new com.bzl.a929demo.service.aidl.IAIDLService.Stub.Proxy(obj);
}

这里主要做了客户端和服务端是否在同一个进程的判断,如果不是,就创建IAIDLService.Stub.Proxy(obj)类。也就是说 IAIDLService 的具体实现是 IAIDLService.Stub.Proxy。

我们在客户端发送数据的时候,是调用了 iaidlService.basicTypes 这个方法,看一下 Proxy.basicTypes这个方法的具体实现。

@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) 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.writeInt(anInt);
        _data.writeLong(aLong);
        _data.writeInt(((aBoolean) ? (1) : (0)));
        _data.writeFloat(aFloat);
        _data.writeDouble(aDouble);
        _data.writeString(aString);
        boolean _status = mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
        if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().basicTypes(anInt, aLong, aBoolean, aFloat, aDouble, aString);
            return;
        }
        _reply.readException();
    } finally {
        _reply.recycle();
        _data.recycle();
    }
}

这里的最要操作,就是把数据装载到Parcel中,然后通过 mRemote.transact 方法发送出去。那么,mRemote又是谁呢?我们来到Proxy的构造方法。

Proxy(android.os.IBinder remote) {
    mRemote = remote;
}

可以看到,mRemote 就是在bindService的onServiceConnected回调方法中IBinder service接口。到这里,我们便遇到第一个难题:IBinder接口是在启动服务时系统回调的。我们如果要从代码里分析出来IBinder的具体实现是谁,还需要去走一遍启动服务的流程。

我们这里采用debug的方式来看出IBinder的具体实现。
在这里插入图片描述
从debug的结果上来看,mRemote的具体是BinderProxy,所以,我们来到BinderProxy.transact()这个方法。

public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
	省略代码。。。。
    try {
        return transactNative(code, data, reply, flags);
    } finally {
        if (transactListener != null) {
            transactListener.onTransactEnded(session);
        }

        if (tracingEnabled) {
            Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
        }
    }
}

transact 调用了 transactNative方法,transactNative是一个本地方法,我们看一下对应的JNI实现。
我们在 android_util_Binder 中,找到了 transactNative的实现。

static const JNINativeMethod gBinderProxyMethods[] = {
     /* name, signature, funcPtr */
    {"pingBinder",          "()Z", (void*)android_os_BinderProxy_pingBinder},
    {"isBinderAlive",       "()Z", (void*)android_os_BinderProxy_isBinderAlive},
    {"getInterfaceDescriptor", "()Ljava/lang/String;", (void*)android_os_BinderProxy_getInterfaceDescriptor},
    {"transactNative",      "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void*)android_os_BinderProxy_transact},
    {"linkToDeath",         "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath},
    {"unlinkToDeath",       "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath},
    {"getNativeFinalizer",  "()J", (void*)android_os_BinderProxy_getNativeFinalizer},
};

从这个结构体中,可以看到 transactNative 对应 android_os_BinderProxy_transact方法。看一下 android_os_BinderProxy_transact的实现。

static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
        jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
{
    省略代码 ...
    IBinder* target = getBPNativeData(env, obj)->mObject.get();
    省略代码 ...
    //printf("Transact from Java code to %p sending: ", target); data->print();
    status_t err = target->transact(code, *data, reply, flags);
    省略代码 ...
    return JNI_FALSE;
}

这个方法,主要有两行我们需要看的代码。
第一行是:
IBinder* target = getBPNativeData(env, obj)->mObject.get();
我们需要确定,IBinder的具体实现。所以,看一下getBPNativeData的代码。

BinderProxyNativeData* getBPNativeData(JNIEnv* env, jobject obj) {
    return (BinderProxyNativeData *) env->GetLongField(obj, gBinderProxyOffsets.mNativeData);
}

getBPNativeData 返回一个 BinderProxyNativeData 的结构体,这个结构体已经提前初始化并且存在JNIEnv里,在JNI层会缓存在gNativeDataCache 上。我们先看一下 BinderProxyNativeData 结构体的定义。

struct BinderProxyNativeData {
	// Both fields are constant and not null once javaObjectForIBinder returns this as
	// part of a BinderProxy.

	// The native IBinder proxied by this BinderProxy.
	sp<IBinder> mObject;

	// Death recipients for mObject. Reference counted only because DeathRecipients
	// hold a weak reference that can be temporarily promoted.
	sp<DeathRecipientList> mOrgue;  // Death recipients for mObject.
};

所以,我们需要知道,mObject是在哪里初始化的,就首先需要知道 gNativeDataCache 在哪里被初始化。

gNativeDataCache 在 javaObjectForIBinder 中被初始化,javaObjectForIBinder 在 android_os_BinderInternal_getContextObject中被调用,而 android_os_BinderInternal_getContextObject 是一个JNI方法,由Java层调用。
看一下 android_os_BinderInternal_getContextObject 的实现。

static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
    sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
    return javaObjectForIBinder(env, b);
}

mObject 就是这个 ProcessState::self()->getContextObject(NULL); 。重点看一下这个方法。

我们来到了 ProcessState.cpp 文件,找到getContextObject这个方法的实现。

sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
    return getStrongProxyForHandle(0);
}

可以看到这里获得一个 handle = 0 的IBinder 接口。

sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
	...... 
			if (handle == 0) {
				Parcel data;
				status_t status = IPCThreadState::self()->transact(
						0, IBinder::PING_TRANSACTION, data, NULL, 0);
				if (status == DEAD_OBJECT)
				   return NULL;
			}
			b = BpBinder::create(handle);
			e->binder = b;
			if (b) e->refs = b->getWeakRefs();
			result = b;
			
	.... 

	return result;
}

所以 ProcessState::self()->getContextObject(NULL) 返回的是一个 BpBinder
所以,在从java层到JNI的 android_os_BinderProxy_transact,本质上是 BpBinder->transact()。

看一下 BpBinder->transact这个方法的实现。

status_t BpBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
	// Once a binder has died, it will never come back to life.
	if (mAlive) {
		status_t status = IPCThreadState::self()->transact(
			mHandle, code, data, reply, flags);
		if (status == DEAD_OBJECT) mAlive = 0;
		return status;
	}
	return DEAD_OBJECT;
}

继续看一下 IPCThreadState的transact的实现。

status_t IPCThreadState::transact(int32_t handle,uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
	..... 
	err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
	.....
	if ((flags & TF_ONE_WAY) == 0) {
		.....
		if (reply) {
			err = waitForResponse(reply);
		} else {
			Parcel fakeReply;
			err = waitForResponse(&fakeReply);
		}
		.....
	} else {
		err = waitForResponse(NULL, NULL);
	}
	return err;
}

writeTransactionData 进行数据的封装,要记得 BC_TRANSACTION 这个type
waitForResponse 把数据发送出去,继续看一下这个方法的实现。

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult){
	..... 省略代码
	while (1) {
		..... 省略代码
		if ((err=talkWithDriver()) < NO_ERROR) break;
		..... 省略代码
	}
	..... 省略代码
}

waitForResponse 最主要调用了 talkWithDriver 方法,继续看 talkWithDriver 的实现。

status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    ..... 省略
    do {
        IF_LOG_COMMANDS() {
            alog << "About to read/write, write size = " << mOut.dataSize() << endl;
        }
#if defined(__ANDROID__)
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        else
            err = -errno;
#else
        err = INVALID_OPERATION;
#endif
        if (mProcess->mDriverFD <= 0) {
            err = -EBADF;
        }
        IF_LOG_COMMANDS() {
            alog << "Finished read/write, write size = " << mOut.dataSize() << endl;
        }
    } while (err == -EINTR);

    .... 省略
    return err;
}

到了这里,终于看到 ioctl 了。到了这里,就把数据发到了Binder内核。
Binder内核会把数据转发给对应的服务端,就不继续往下走了。

这一篇代码分析,里面可能存在一些错误,但是我的目的就是从代码层面去验证前面几篇的理论分析。但是好累啊,哈哈哈。

Binder的学习就告一段落。

------- 2021/09/17
Binder的 1M - 8K 大小限制,为什么要减去8K?
答案:
Modify the binder to request 1M - 2 pages instead of 1M. The backing store in the kernel requires a guard page, so 1M allocations fragment memory very badly. Subtracting a couple of pages so that they fit in a power of two allows the kernel to make more efficient use of its virtual address space.

总的来说,就是大小要是2次幂,更好的适应虚拟内存。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值