大佬就是强!意外收获字节跳动内部资料——《Android-Binder机制实现原理》,从入门到精通,干货满满(1)

最后就是启动 Binder 循环了。它的逻辑也没有想象中得复杂,就是启动了 for 循环,

// platform/framework/native/cmds/servicemanager.c
void binder_loop(struct binder_state *bs, binder_handler func)
{
int res;
struct binder_write_read bwr;
uint32_t readbuf[32];

bwr.write_size = 0;
bwr.write_consumed = 0;
bwr.write_buffer = 0;

readbuf[0] = BC_ENTER_LOOPER;
// 将 BC_ENTER_LOOPER 命令发送给 binder 驱动,内部调用 ioctl 函数
binder_write(bs, readbuf, sizeof(uint32_t));

for (;😉 {
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (uintptr_t) readbuf;
// 使用 iotcl 函数读取
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
if (res < 0) {
break;
}
// 解析
res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
if (res == 0) {
break;
}
if (res < 0) {
break;
}
}
}

从上面看出,函数将会在 binder_write() 中将命令发送给 Binder 驱动,以启动循环。其实内部也是调用 ioctl 函数实现的。然后程序会启动一个循环来不断读取、解析。这是服务器很典型的操作了。

当然,我们上面分析的是 ServiceManager 中向 Binder 写命令的过程,而驱动如何解析呢?当然是在驱动中实现了,详细的过程可以查看 Binder 驱动部分的源码。

4.4 Binder 的跨进程通信过程

下面我们以 AMS 作为例子来讲解下 Binder 跨进程通信的实现过程。首先,当我们调用 startActivity() 方法的时候,最终将会进入 ActivityManager 以获取 AMS,

// platform/framework/base/core/java/android/app/ActivityManager.java
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;

这里会使用 ServiceManger 来按名称查找 AMS,查找到 Binder 对象之后将其转换成 AMS 就可以使用了。之前,我们也说过用来查找 AMS 的 SeerviceManager 本身也是一种服务。所以,它这里的方法也是通过 Binder 来实现的。那么,我们就从这里的 getService() 方法入手。

// platform/framework/base/core/java/android/os/ServiceManager.java
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return Binder.allowBlocking(rawGetService(name));
}
} catch (RemoteException e) { /* … */ }
return null;
}

这里会先尝试从缓存当中取 Binder,取不到的话就从远程进行获取。这里使用 rawGetService() 方法来从远程获取 Binder,代码如下,

// platform/framework/base/core/java/android/os/ServiceManager.java
private static IBinder rawGetService(String name) throws RemoteException {
final IBinder binder = getIServiceManager().getService(name);
// …
return binder;
}

// platform/framework/base/core/java/android/os/ServiceManager.java
private static IServiceManager getIServiceManager() {
if (sServiceManager != null) {
return sServiceManager;
}
sServiceManager = ServiceManagerNative
.asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
return sServiceManager;
}

rawGetService() 方法中会使用 ServiceManagerNativegetService() 方法从远程获取 Binder. 这里的 ServiceManagerNative 本质上只是一个代理类,它实际的逻辑是由 BinderInternal.getContextObject() 返回的 Binder 实现的。

也许你已经晕了,怎么那么多 Binder……我来说明下。当要查找 AMS 的时候实际上是一个跨进程的调用过程,也就是实际的查找的逻辑是在另一个进程实现,因此需要 Binder 来通信。而查找 AMS 的远程对象实际上就是我们上面所说的 ServiceManager (Native 层的而不是 Java 层的,Java 层的 ServiceManager 是一个代理类,是用来从远程获取服务的)。

因此,按照上面的描述,BinderInternal.getContextObject() 返回的就应该是远程的 Binder 对象。于是方法进入 Native 层,

// platform/framework/base/core/jni/android_util_Binder.cpp
static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
sp b = ProcessState::self()->getContextObject(NULL);
return javaObjectForIBinder(env, b);
}

这里的 ProcessState::self() 是否熟悉呢?你是否还记得在上一篇文章中,我们介绍 Android 系统启动过程的时候介绍过它。我们曾经使用它来开启 Binder 的线程池。这里的 self() 方法其实是用来获取一个单例对象的。我们可以直接由 getContextObject() 进入 getStrongProxyForHandle() 方法。从下面的方法中我们可以看出,这里调用了 BpBindercreate() 方法创建了一个 BpBinder 实例并返回,也就是我们的 ServiceManager.

// plaftorm/framework/native/libs/binder/ProcessState.cpp
sp ProcessState::getStrongProxyForHandle(int32_t handle)
{
sp result;
AutoMutex _l(mLock);
handle_entry* e = lookupHandleLocked(handle);
if (e != nullptr) {
IBinder* b = e->binder;
if (b == nullptr || !e->refs->attemptIncWeak(this)) {
// …
// 调用 BpBinder
b = BpBinder::create(handle);
e->binder = b;
if (b) e->refs = b->getWeakRefs();
result = b;
} else {
result.force_set(b);
e->refs->decWeak(this);
}
}

当我们拿到了 ServiceManager 的 Binder 之后就可以调用它的 getService() 方法来获取服务了,

// platform/framework/base/core/java/android/os/ServiceManagerNative.java
public IBinder getService(String name) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IServiceManager.descriptor);
data.writeString(name);
mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
IBinder binder = reply.readStrongBinder();
reply.recycle();
data.recycle();
return binder;
}

这里的 mRemote 就是之前返回的 BpBinder,这里调用它的 transact() 方法,并传入了一个方法标记 GET_SERVICE_TRANSACTION.

// platform/framework/native/libs/binder/BpBinder.cpp
status_t BpBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
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 的 self() 方法先获取一个单例的对象,然后调用它的 transact() 方法继续方法的执行。

// platform/framework/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::transact(int32_t handle, uint32_t code,
const Parcel& data, Parcel* reply, uint32_t flags)
{
status_t err;
// …
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, nullptr);
// …
if ((flags & TF_ONE_WAY) == 0) { // OneWay 类型的调用,同步的
// …
if (reply) {
// 等待相应
err = waitForResponse(reply);
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
IF_LOG_TRANSACTIONS() {
TextOutput::Bundle _b(alog);
if (reply) alog << indent << *reply << dedent << endl;
else alog << “(none requested)” << endl;
}
} else { // 异步的
err = waitForResponse(nullptr, nullptr);
}
return err;
}

上面会调用 writeTransactionData() 方法用来将数据写入到 Parcel 中。然后将会进入 waitForResponse() 方法处理与 ServiceManager 交互的结果。而真实的交互发生的地方位于 talkWithDriver() 方法,

// platform/framework/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
if (mProcess->mDriverFD <= 0) {
return -EBADF;
}

binder_write_read bwr;
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;

bwr.write_size = outAvail;
bwr.write_buffer = (uintptr_t)mOut.data();

if (doReceive && needRead) {
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer = (uintptr_t)mIn.data();
} else {
bwr.read_size = 0;
bwr.read_buffer = 0;
}

if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;

bwr.write_consumed = 0;
bwr.read_consumed = 0;
status_t err;
do {
// 通过 ioctl 读写操作,与 Binder Driver 进行交互
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
err = -errno;
if (mProcess->mDriverFD <= 0) {
err = -EBADF;
}
} while (err == -EINTR);
// …
return err;
}

binder_write_read 结构体用来与 Binder 设备交换数据的结构, 通过 ioctl 与 mDriverFD 通信,是真正与 Binder 驱动进行数据读写交互的过程。先向service manager进程发送查询服务的请求(BR_TRANSACTION)。然后,service manager 会在之前开启的循环中监听到,并使用 svcmgr_handler() 方法进行处理。

// platform/framework/native/cmds/servicemanager.c
int svcmgr_handler(struct binder_state *bs,
struct binder_transaction_data *txn,
struct binder_io *msg,
struct binder_io *reply)
{
// …
switch(txn->code) {
case SVC_MGR_GET_SERVICE:
case SVC_MGR_CHECK_SERVICE:
s = bio_get_string16(msg, &len);
if (s == NULL) {
return -1;
}
handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);
if (!handle)
break;
bio_put_ref(reply, handle);
return 0;
case SVC_MGR_ADD_SERVICE: // …
case SVC_MGR_LIST_SERVICES: // …
}
return 0;
}

显然,这里会从 binder_transaction_data 中取出 code,即 SVC_MGR_GET_SERVICE,然后使用 do_find_service() 方法查找服务。然后再 binder_send_reply() 应答发起者将结果返回即可。

4.5 Binder 高效通信的原因

上面我们梳理了 Binder 通信的过程,从上面我们似乎并没有看到能证明 Binder 高效的证据。那么 Binder 究竟靠什么实现高效的呢?

实际上,Binder 之所以高效,从我们上面的代码还真看不出来。因为,我们上面的代码并没有涉及 Binder 驱动部分。正如我们之前描述的那样,ServiceManager、客户端和服务器实际是靠 Binder 驱动这个中间媒介进行交互的。而 Binder 高效的地方就发生在 Binder 驱动部分。

就像图片描述的那样,当两个进程之间需要通信的时候,Binder 驱动会在两个进程之间建立两个映射关系:内核缓存区和内核中数据接收缓存区之间的映射关系,以及内核中数据接收缓存区和接收进程用户空间地址的映射关系。这样,当把数据从 1 个用户空间拷贝到内核缓冲区的时候,就相当于拷贝到了另一个用户空间中。这样只需要做一次拷贝,省去了内核中暂存这个步骤,提升了一倍的性能。实现内存映射靠的就是上面的 mmap() 函数。

4、Binder 的使用

4.1 代理模式

Binder 本质上只是一种底层通信方式,和具体服务没有关系。为了提供具体服务,Server 必须提供一套接口函数以便 Client 通过远程访问使用各种服务。这时通常采用代理设计模式:将接口函数定义在一个抽象类中,ServerClient 都会以该抽象类为基类实现所有接口函数。所不同的是 Server 端是真正的功能实现,而 Client 端是对这些函数远程调用请求的包装。为了简化这种设计模式,Android 中提供了 AIDL 供我们使用。下文中我们会介绍 AIDL 相关的内容以及它的一些基本的使用方式。

4.2 AIDL

AIDL (Android Interface Definition Language,Android 接口定义语言) 是一种文件格式,用来简化 Binder 的使用。当使用 Binder 的时候,只需要创建一个后缀名为 .aidl 的文件,然后像定义接口一样定义方法。定义完毕之后,使用工具 aidl.exe 即可生成 Binder 所需要的各种文件。当然,我们的 AS 已经为我们集成了 aidl.exe,所以,只需要在定义了 AIDL 文件之后,编译即可生成使用 Binder 时所需的文件。当然,不使用 AIDL,直接编写 Binder 所需的 java 文件也是可以的。

AIDL 是一种接口定义语言,它与 Java 中定义接口的方式有所区别。下面我们通过一个例子来说明 AIDL 的使用方式。

这里我们模拟一个笔记管理的类,通过在 Activity 中与一个远程的 Service 进行交互来实现 IPC 的效果。这里,我们先要定义数据实体 Note,它只包含两个字段,并且实现了 Parcelable。这里 Note 所在的目录是 me.shouheng.advanced.aidl,然后,我们需要在 src/main 建立一个同样的包路径,然后定义所需的 AIDL 文件:

// INoteManager.aidl
package me.shouheng.advanced.aidl;
import me.shouheng.advanced.aidl.Note;
interface INoteManager {
Note getNote(long id);
void addNote(long id, String name);
}

// Note.aidl
package me.shouheng.advanced.aidl;
parcelable Note;

注意,在 INoteManager 文件中,我们定义了远程服务所需的各种方法。这里只定义了两个方法,一个用来获取指定 id 的笔记,一个用来向远程服务中添加一条笔记记录。

这样定义完了之后,我们可以对项目进行编译,这样就可以 build 目录下面得到为我们生成好的 INoteManager 类文件。以后,我们就可以使用这个文件中生成类和方法来进行远程通信。但在使用该接口之前,我们还是先来看一下其中都生成了些什么东西:

package me.shouheng.advanced.aidl;

public interface INoteManager extends android.os.IInterface {

// 交给远程来实现具体的业务逻辑
public static abstract class Stub extends android.os.Binder implements me.shouheng.advanced.aidl.INoteManager {

private static final java.lang.String DESCRIPTOR = “me.shouheng.advanced.aidl.INoteManager”;

public Stub() {
this.attachInterface(this, DESCRIPTOR);
}

// 使用代理包装远程对象
public static me.shouheng.advanced.aidl.INoteManager asInterface(android.os.IBinder obj) {
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof me.shouheng.advanced.aidl.INoteManager))) {
return ((me.shouheng.advanced.aidl.INoteManager)iin);
}
// 返回代理对象
return new me.shouheng.advanced.aidl.INoteManager.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_getNote: {
data.enforceInterface(DESCRIPTOR);
long _arg0;
_arg0 = data.readLong();
// 使用模板方法来实现业务
me.shouheng.advanced.aidl.Note _result = this.getNote(_arg0);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_addNote: {
data.enforceInterface(DESCRIPTOR);
long _arg0;
_arg0 = data.readLong();
java.lang.String _arg1;
_arg1 = data.readString();
// 使用模板方法来实现业务
this.addNote(_arg0, _arg1);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}

// 代理对象,包装了远程对象,内部调用远程对象获取远程的服务信息
private static class Proxy implements me.shouheng.advanced.aidl.INoteManager {

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 me.shouheng.advanced.aidl.Note getNote(long id) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
me.shouheng.advanced.aidl.Note _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeLong(id);
// 实际内部调用远程对象,在另一个进程实现业务逻辑
mRemote.transact(Stub.TRANSACTION_getNote, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = me.shouheng.advanced.aidl.Note.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}

@Override
public void addNote(long id, java.lang.String name) 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.writeLong(id);
_data.writeString(name);
// 实际内部调用远程对象,在另一个进程实现业务逻辑
mRemote.transact(Stub.TRANSACTION_addNote, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}

// 方法 id,用来标记当前调用的是哪个方法
static final int TRANSACTION_getNote = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addNote = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}

public me.shouheng.advanced.aidl.Note getNote(long id) throws android.os.RemoteException;

public void addNote(long id, java.lang.String name) throws android.os.RemoteException;
}

如果只是看这上面的生成的代码,也许你仍然无法了解这些生成的类究竟有什么作用。下面就让我们通过使用上面生成的类来说明 AIDL 的具体工作流程。

首先,我们要定义远程的服务,并在该服务中实现业务逻辑:

public class NoteService extends Service {

private CopyOnWriteArrayList notes = new CopyOnWriteArrayList<>();

// 当前服务运行于另一个进程,这里实现业务逻辑
private Binder binder = new INoteManager.Stub() {
@Override
public Note getNote(long id) {
return Observable.fromIterable(notes).filter(note -> note.id == id).singleOrError().blockingGet();
}

@Override
public void addNote(long id, String name) {
notes.add(new Note(id, name));
}
};

@Override
public void onCreate() {
super.onCreate();
notes.add(new Note(100, “Note 100”));
notes.add(new Note(101, “Note 101”));
}

// 将 binder 返回,客户端可以使用连接来获取并调用
@Nullable
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}

这里在 onCreate() 方法中创建了两条记录,并且创建了 INoteManager.Stub 的实例,并在 onBind() 方法中将其返回。然后,我们在一个 Activity 中启动该远程服务,并尝试从该服务中获取指定 id 的笔记记录。从期望的结果来看,它的功能有些类似于 ContentProvider,即用来向调用者提供数据。

下面是该 Activity 的实现。这里我们在 onCreate() 方法中启动上述服务。并将实例化的 ServiceConnection 作为参数启动该服务。在 ServiceConnection 的方法中,我们调用 INoteManager.StubasInterface(IBinder) 方法来讲 service 转换成 INoteManager,然后从其中获取指定 id 的笔记记录即可。

// 创建服务连接
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 返回代理对象
INoteManager noteManager = INoteManager.Stub.asInterface(service);
try {
// 使用代理对象
Note note = noteManager.getNote(100);
LogUtils.d(note);
} catch (RemoteException e) {
e.printStackTrace();
}
}

@Override
public void onServiceDisconnected(ComponentName name) { }
};

@Override
protected void doCreateView(Bundle savedInstanceState) {
Intent intent = new Intent(this, NoteService.class);
// 绑定服务
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

总之啊,家里没矿的同学们,如果你们想以后的日子过得好一些,多想想你们的业余时间怎么安排吧;

技术方面的提升肯定是重中之重,但是技术外的一些“软实力”也不能完全忽视,很多时候升职确实是因为你的技术足够强,但也与你的“软实力”密切相关

在这我也分享一份大佬自己收录整理的 Android学习PDF+架构视频+面试文档+源码笔记 ,还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料这些都是我闲暇还会反复翻阅并给下属员工学习的精品资料。在脑图中,每个知识点专题都配有相对应的实战项目,可以有效的帮助大家掌握知识点。

总之也是在这里帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习

相信自己,没有做不到的,只有想不到的

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!

最后

总之啊,家里没矿的同学们,如果你们想以后的日子过得好一些,多想想你们的业余时间怎么安排吧;

技术方面的提升肯定是重中之重,但是技术外的一些“软实力”也不能完全忽视,很多时候升职确实是因为你的技术足够强,但也与你的“软实力”密切相关

在这我也分享一份大佬自己收录整理的 Android学习PDF+架构视频+面试文档+源码笔记 ,还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料这些都是我闲暇还会反复翻阅并给下属员工学习的精品资料。在脑图中,每个知识点专题都配有相对应的实战项目,可以有效的帮助大家掌握知识点。

总之也是在这里帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习

[外链图片转存中…(img-Thnoe2Nk-1712390538335)]

[外链图片转存中…(img-aitpYWS7-1712390538335)]

相信自己,没有做不到的,只有想不到的

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
  • 26
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值