Binder机制深入理解

目录

0x01.为什么要使用Binder

0x02.ServiceManager充当的角色

0x03.Client端和Server端的通讯过程(Native层)   首先看Client端:

a.打开驱动, mmap

b.获得BpServiceManager

c.getService引用

d.获得BpHelloServie对象

e.调用Service的方法

0x04.Client端和Server端的通讯过程(Native层)   Server端:

a.打开驱动, mmap

b.获得BpServiceManager

c.addService

d.循环体


本文不打算深入到linux内核中的binder驱动,只分析binder驱动之上的native层和Java层。所以我们认为,binder驱动的作用就是充当路由器,为Client和server端的通讯提供桥梁。

0x01.为什么要使用Binder

我们都知道,Binder是Android进程间通讯的主要方式。

问:Android为什么要选择Binder机制呢?

答:a.当然是Binder机制的高效性。Binder通讯过程中,数据只要copy一次就可以了。注意是进程间的数据只copy了一次,Client和server端建立链接时,通讯协议还是copy了好几次的。

b.稳定性  基于C/S架构,稳定性毋庸置疑

c.安全性  传统的IPC没有任何安全措施,安全完全依赖上层来实现。传统的IPC无法获得可靠的用户ID和进程ID(UID/PID),从而无法可靠的获得对方的身份。Android 为每个安装好的 APP 分配了自己的 UID,故而进程的 UID 是鉴别进程身份的重要标志。传统的 IPC 只能由用户在数据包中填入 UID/PID,但这样不可靠,容易被恶意程序利用。可靠的身份标识只有由 IPC 机制在内核中添加。其次传统的 IPC 访问接入点是开放的,只要知道这些接入点的程序都可以和对端建立连接,不管怎样都无法阻止恶意程序通过猜测接收方地址获得连接。同时 Binder 既支持实名 Binder,又支持匿名 Binder,安全性高。

问:为什么数据只需要copy一次就能完成进程间的通讯?

答:关键是mmap() 函数。用户层调用mmap()函数,就会调用到驱动的binder_mmap函数,我们大致可以理解为,binder_mmap函数会为我们申请一块物理地址,然后建立用户空间和内核空间对这块地址的映射,这样用户空间和内核空间就都可以访问这段内存了。当client端需要传数据给server端时,只要copy_from_user一次到这段地址,server端既可以直接访问这段数据了。

这里盗一张图:

 

0x02.ServiceManager充当的角色

ServiceManager充当类似网络通讯中DNS的角色,Server端首先向ServiceManager注册自己,Client端则通过ServiceManager查询到这个服务,并且获得这个服务的一个类似fd=open()的句柄,然后Client端就可以通过这个句柄与Server端通讯了。需要注意的是,Client端和Server端与ServiceManager的交互,最终都是通过binder驱动完成的。

Binder 通信过程:

 

0x03.Client端和Server端的通讯过程(Native层)   首先看Client端:

先给出整体代码

int main(int argc, char **argv)
{
	int cnt;
	
	if (argc < 2){
        ALOGI("Usage:\n");
        ALOGI("%s <readfile>\n", argv[0]);
        ALOGI("%s <hello|goodbye>\n", argv[0]);
        ALOGI("%s <hello|goodbye> <name>\n", argv[0]);
        return -1;
	}

	/* getService */
	/* 打开驱动, mmap */
	sp<ProcessState> proc(ProcessState::self());

	/* 获得BpServiceManager */
	sp<IServiceManager> sm = defaultServiceManager();

	if (strcmp(argv[1], "hello") == 0)
	{

		sp<IBinder> binder =
		    sm->getService(String16("hello"));

		if (binder == 0)
		{
		    ALOGI("can't get hello service\n");
			return -1;
		}

		/* service肯定是BpHelloServie指针 */
		sp<IHelloService> service =
		    interface_cast<IHelloService>(binder);


		/* 调用Service的函数 */
		if (argc < 3) {
			service->sayhello();
			ALOGI("client call sayhello");
		}
		else {
			cnt = service->sayhello_to(argv[2]);
			ALOGI("client call sayhello_to, cnt = %d", cnt);
		}
	}
	else if (strcmp(argv[1], "readfile") == 0)
	{

		sp<IBinder> binder =
		    sm->getService(String16("hello"));

		if (binder == 0)
		{
		    ALOGI("can't get hello service\n");
			return -1;
		}

		/* service肯定是BpHelloServie指针 */
		sp<IHelloService> service =
		    interface_cast<IHelloService>(binder);


		/* 调用Service的函数 */
		int fd = service->get_fd();

		ALOGI("client call get_fd = %d", fd);

		char buf[500];
		int len;
		int cnt = 0;
		
		while (1)
		{
			/* 向 test_server 进程发出: Hello, test_server	*/
			len = sprintf(buf, "Hello, test_server, cnt = %d", cnt++);
			write(fd, buf, len);
		
			/* 读取数据(test_server进程发回的数据) */
			len = read(fd, buf, 500);
			buf[len] = '\0';
			ALOGI("%s\n", buf);
		
			sleep(5);
		}
	}
	else
	{

		sp<IBinder> binder =
		    sm->getService(String16("goodbye"));

		if (binder == 0)
		{
		    ALOGI("can't get goodbye service\n");
			return -1;
		}

		/* service肯定是BpGoodbyeServie指针 */
		sp<IGoodbyeService> service =
		    interface_cast<IGoodbyeService>(binder);


		/* 调用Service的函数 */
		if (argc < 3) {
			service->saygoodbye();
			ALOGI("client call saygoodbye");
		}
		else {
			cnt = service->saygoodbye_to(argv[2]);
			ALOGI("client call saygoodbye_to, cnt = %d", cnt);
		}
	}
	
	return 0;
}

现在一步步分析:

a.打开驱动, mmap
 

sp<ProcessState> proc(ProcessState::self());
sp<ProcessState> ProcessState::self()
{
    Mutex::Autolock _l(gProcessMutex);
    if (gProcess != NULL) {
        return gProcess;
    }
    gProcess = new ProcessState;
    return gProcess;
}
ProcessState::ProcessState()
    : mDriverFD(open_driver())
    , mVMStart(MAP_FAILED)
    , mManagesContexts(false)
    , mBinderContextCheckFunc(NULL)
    , mBinderContextUserData(NULL)
    , mThreadPoolStarted(false)
    , mThreadPoolSeq(1)
{
    if (mDriverFD >= 0) {
        // XXX Ideally, there should be a specific define for whether we
        // have mmap (or whether we could possibly have the kernel module
        // availabla).
#if !defined(HAVE_WIN32_IPC)
        // mmap the binder, providing a chunk of virtual address space to receive transactions.
        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
        if (mVMStart == MAP_FAILED) {
            // *sigh*
            ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
            close(mDriverFD);
            mDriverFD = -1;
        }
#else
        mDriverFD = -1;
#endif
    }

    LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened.  Terminating.");
}

我们看到,这里确实调用了open_driver()和mmap

b.获得BpServiceManager

sp<IServiceManager> sm = defaultServiceManager();

我们看下defaultServiceManager()是怎么实现的。

sp<IServiceManager> defaultServiceManager()
{
    if (gDefaultServiceManager != NULL) return gDefaultServiceManager;
    
    {
        AutoMutex _l(gDefaultServiceManagerLock);
        while (gDefaultServiceManager == NULL) {
            gDefaultServiceManager = interface_cast<IServiceManager>(
                ProcessState::self()->getContextObject(NULL));
            if (gDefaultServiceManager == NULL)
                sleep(1);
        }
    }
    
    return gDefaultServiceManager;
}
template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
    return INTERFACE::asInterface(obj);
}

这是一个模板,展开如下:

inline sp<IServiceManager> interface_cast(const sp<IBinder>& obj)
{
    return IServiceManager::asInterface(getStrongProxyForHandle(0));
}

可以看到最终调用了IServiceManager::asInterface方法,输入参数是getStrongProxyForHandle(0),但我们并没有发现IServiceManager::asInterface方法

看下IServiceManager.cpp中有这样一句

IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager");

这是一句宏调用,展开如下:

#define DECLARE_META_INTERFACE(ServiceManager)                               \
    static const android::String16 descriptor;                          \
    static android::sp<IServiceManager> asInterface(                       \
            const android::sp<android::IBinder>& obj);                  \
    virtual const android::String16& getInterfaceDescriptor() const;    \
    IServiceManager();                                                     \
    virtual ~IServiceManager();                                            \


#define IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager")                       \
    const android::String16 IServiceManager::descriptor("android.os.IServiceManager");             \
    const android::String16&                                            \
            IerviceManager::getInterfaceDescriptor() const {              \
        return IServiceManager::descriptor;                                \
    }                                                                   \
    android::sp<IServiceManager> IServiceManager::asInterface(                \
            const android::sp<android::IBinder>& obj)                   \
    {                                                                   \
        android::sp<IServiceManager> intr;                                 \
        if (obj != NULL) {                                              \
            intr = static_cast<IServiceManager*>(                          \
                obj->queryLocalInterface(                               \
                        IServiceManager::descriptor).get());               \
            if (intr == NULL) {                                         \
                intr = new BpServiceManager(obj);                          \
            }                                                           \
        }                                                               \
        return intr;                                                    \
    }                                                                   \
    IServiceManager::IServiceManager() { }                                    \
    IServiceManager::~IServiceManager() { }                                   \


#define CHECK_INTERFACE(interface, data, reply)                         \
    if (!data.checkInterface(this)) { return PERMISSION_DENIED; }       \

所以我们可以看到,sm=gDefaultServiceManager就是一个BpServiceManager对象。

c.getService引用

sp<IBinder> binder = sm->getService(String16("hello"));

由上一步sm=gDefaultServiceManager就是一个BpServiceManager对象,可知,sm->getService是BpServiceManager::getService()

class BpServiceManager : public BpInterface<IServiceManager>
{
public:
    BpServiceManager(const sp<IBinder>& impl)
        : BpInterface<IServiceManager>(impl)
    {
    }

    virtual sp<IBinder> getService(const String16& name) const
    {
        unsigned n;
        for (n = 0; n < 5; n++){
            sp<IBinder> svc = checkService(name);
            if (svc != NULL) return svc;
            ALOGI("Waiting for service %s...\n", String8(name).string());
            sleep(1);
        }
        return NULL;
    }

    virtual sp<IBinder> checkService( const String16& name) const
    {
        Parcel data, reply;
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);
        remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);
        return reply.readStrongBinder();
    }
}

可见BpServiceManager::getService最终调用了BpServiceManager::checkService,而BpServiceManager::checkService最终调用了remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);

我们看下remote是怎么得来的。

BpServiceManager的构造函数

 BpServiceManager(const sp<IBinder>& impl)
        : BpInterface<IServiceManager>(impl)
    {
    }

会先初始化他的父类BpInterface<IServiceManager>(impl)

template<typename INTERFACE>
inline BpInterface<INTERFACE>::BpInterface(const sp<IBinder>& remote)
    : BpRefBase(remote)
{
}

然后是BpInterface的父类BpRefBase

BpRefBase::BpRefBase(const sp<IBinder>& o)
    : mRemote(o.get()), mRefs(NULL), mState(0)
{
    extendObjectLifetime(OBJECT_LIFETIME_WEAK);

    if (mRemote) {
        mRemote->incStrong(this);           // Removed on first IncStrong().
        mRefs = mRemote->createWeak(this);  // Held for our entire lifetime.
    }
}

在BpRefBase类中有

inline  IBinder*        remote()                { return mRemote; }

看来,remote()->transact相当于mRemote->transact。追溯mRemote=getStrongProxyForHandle(0)

sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;

    AutoMutex _l(mLock);

    handle_entry* e = lookupHandleLocked(handle);

    if (e != NULL) {
        // We need to create a new BpBinder if there isn't currently one, OR we
        // are unable to acquire a weak reference on this current one.  See comment
        // in getWeakProxyForHandle() for more info about this.
        IBinder* b = e->binder;
        if (b == NULL || !e->refs->attemptIncWeak(this)) {
            if (handle == 0) {
                // Special case for context manager...
                // The context manager is the only object for which we create
                // a BpBinder proxy without already holding a reference.
                // Perform a dummy transaction to ensure the context manager
                // is registered before we create the first local reference
                // to it (which will occur when creating the BpBinder).
                // If a local reference is created for the BpBinder when the
                // context manager is not present, the driver will fail to
                // provide a reference to the context manager, but the
                // driver API does not return status.
                //
                // Note that this is not race-free if the context manager
                // dies while this code runs.
                //
                // TODO: add a driver API to wait for context manager, or
                // stop special casing handle 0 for context manager and add
                // a driver API to get a handle to the context manager with
                // proper reference counting.

                Parcel data;
                status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, NULL, 0);
                if (status == DEAD_OBJECT)
                   return NULL;
            }

            b = new BpBinder(handle); 
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            // This little bit of nastyness is to allow us to add a primary
            // reference to the remote proxy when this team doesn't have one
            // but another team is sending the handle to us.
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }

    return result;
}

可知,这个mRemote是BpBinder对象,mRemote->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::self()->transact,这里就和binder驱动打交道了。我们不分析驱动的代码,默认binder驱动帮我们找到了hello这个服务。

继续看checkService这个方法的返回值:

return reply.readStrongBinder();

sp<IBinder> Parcel::readStrongBinder() const
{
    sp<IBinder> val;
    unflatten_binder(ProcessState::self(), *this, &val);
    return val;
}
status_t unflatten_binder(const sp<ProcessState>& proc,
    const Parcel& in, sp<IBinder>* out)
{
    const flat_binder_object* flat = in.readObject(false);
    
    if (flat) {
        switch (flat->type) {
            case BINDER_TYPE_BINDER:
                *out = static_cast<IBinder*>(flat->cookie);
                return finish_unflatten_binder(NULL, *flat, in);
            case BINDER_TYPE_HANDLE:
                *out = proc->getStrongProxyForHandle(flat->handle);
                return finish_unflatten_binder(
                    static_cast<BpBinder*>(out->get()), *flat, in);
        }        
    }
    return BAD_TYPE;
}

重点看*out = proc->getStrongProxyForHandle(flat->handle);

可知,这个out是getStrongProxyForHandle方法的返回值,也是一个BpBinder对象。还记得获取BpServiceManager时调用getStrongProxyForHandle方法的参数值吗,当时是0,这里是hello这个服务返回给Client端的handle值。

也就是说,sp<IBinder> binder = sm->getService(String16("hello"));这里的binder是一个BpBinder对象,构造参数是hello这个服务返回给Client端的handle值。

d.获得BpHelloServie对象

sp<IHelloService> service = interface_cast<IHelloService>(binder);

IMPLEMENT_META_INTERFACE(HelloService, "android.media.IHelloService");

 

看起来和获得BpServiceManager一样的处理方法

template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
    return INTERFACE::asInterface(obj);
}

相当于

inline sp<IHelloService> interface_cast(const sp<IBinder>& obj)
{
    return IHelloService::asInterface(binder);
}

展开IMPLEMENT_META_INTERFACE(HelloService, "android.media.IHelloService");如下:

#define DECLARE_META_HelloService(HelloService)                               \
    static const android::String16 descriptor;                          \
    static android::sp<IHelloService> asHelloService(                       \
            const android::sp<android::IBinder>& obj);                  \
    virtual const android::String16& getHelloServiceDescriptor() const;    \
    IHelloService();                                                     \
    virtual ~IHelloService();                                            \


#define IMPLEMENT_META_HelloService(HelloService, "android.media.IHelloService")                       \
    const android::String16 IHelloService::descriptor("android.media.IHelloService");             \
    const android::String16&                                            \
            IHelloService::getHelloServiceDescriptor() const {              \
        return IHelloService::descriptor;                                \
    }                                                                   \
    android::sp<IHelloService> IHelloService::asHelloService(                \
            const android::sp<android::IBinder>& obj)                   \
    {                                                                   \
        android::sp<IHelloService> intr;                                 \
        if (obj != NULL) {                                              \
            intr = static_cast<IHelloService*>(                          \
                obj->queryLocalHelloService(                               \
                        IHelloService::descriptor).get());               \
            if (intr == NULL) {                                         \
                intr = new BpHelloService(obj);                          \
            }                                                           \
        }                                                               \
        return intr;                                                    \
    }                                                                   \
    IHelloService::IHelloService() { }                                    \
    IHelloService::~IHelloService() { }                                   \


#define CHECK_HelloService(HelloService, data, reply)                         \
    if (!data.checkHelloService(this)) { return PERMISSION_DENIED; }       \

所以service = interface_cast<IHelloService>(binder);返回一个BpHelloService(binder)对象,对象的构造参数是第c步获得的IBinder类对象

e.调用Service的方法

service->sayhello();

由第d步可知,service 是一个BpHelloService(binder)对象,这里肯定调用到了BpHelloService对象的sayhello()方法

void sayhello(void)
	{
		/* 构造/发送数据 */

        Parcel data, reply;
        data.writeInt32(0);
		data.writeString16(String16("IHelloService"));

        remote()->transact(HELLO_SVR_CMD_SAYHELLO, data, &reply);
	}

这里的remote()=mRemote=第c步获得的BpBinder类对象

这样,remote()->transact一样是调用到了IPCThreadState::transact方法,直接和binder驱动打交道了。同样,我们不分析驱动层代码,我们直接认为,binder驱动帮我们把HELLO_SVR_CMD_SAYHELLO这条命令发送给了server端。

不过这里有个地方需要注意,就是

status_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)

虽然第c步和第e步都是通过同一个方法来和binder通讯,但最主要的一点是传入的handle不同,一个是handle=0,默认指servicemanager;一个是通过查询获得的server端的handler句柄,binder驱动通过不同的handle和servicemanager或者server端打交道。

0x04.Client端和Server端的通讯过程(Native层)   Server端:

首先给出全部代码

int main(void)
{

	int sockets[2];

	socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets);

    int bufferSize = SOCKET_BUFFER_SIZE;
    setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));

	/* 创建一个线程, 用于跟test_client使用socketpiar通信 */
	sp<MyThread> th = new MyThread(sockets[0]);
	th->run();  


	/* addService */

	/* while(1){ read data, 解析数据, 调用服务函数 } */

	/* 打开驱动, mmap */
	sp<ProcessState> proc(ProcessState::self());

	/* 获得BpServiceManager */
	sp<IServiceManager> sm = defaultServiceManager();

	sm->addService(String16("hello"), new BnHelloService(sockets[1]));
	sm->addService(String16("goodbye"), new BnGoodbyeService());

	/* 循环体 */
	ProcessState::self()->startThreadPool();
	IPCThreadState::self()->joinThreadPool();

	return 0;
}

a.打开驱动, mmap

sp<ProcessState> proc(ProcessState::self());

和0x03.a是一样的,不再分析

b.获得BpServiceManager

sp<IServiceManager> sm = defaultServiceManager();

和0x03.b是一样的,不再分析

c.addService

sm->addService(String16("hello"), new BnHelloService(sockets[1]));

d.循环体

ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();

先看startThreadPool

void ProcessState::spawnPooledThread(bool isMain)
{
    if (mThreadPoolStarted) {
        String8 name = makeBinderThreadName();
        ALOGV("Spawning new pooled thread, name=%s\n", name.string());
        sp<Thread> t = new PoolThread(isMain);
        t->run(name.string());
    }
}

调用了t->run,最终调用了

virtual bool threadLoop()
{
    IPCThreadState::self()->joinThreadPool(mIsMain);
    return false;
}

所以这两个语句都是最终调用了joinThreadPool方法,下面分析这个方法

void IPCThreadState::joinThreadPool(bool isMain)
{
    LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid());

    mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
    
    // This thread may have been spawned by a thread that was in the background
    // scheduling group, so first we will make sure it is in the foreground
    // one to avoid performing an initial transaction in the background.
    set_sched_policy(mMyThreadId, SP_FOREGROUND);
        
    status_t result;
    do {
        processPendingDerefs();
        // now get the next command to be processed, waiting if necessary
        result = getAndExecuteCommand();

        if (result < NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) {
            ALOGE("getAndExecuteCommand(fd=%d) returned unexpected error %d, aborting",
                  mProcess->mDriverFD, result);
            abort();
        }
        
        // Let this thread exit the thread pool if it is no longer
        // needed and it is not the main process thread.
        if(result == TIMED_OUT && !isMain) {
            break;
        }
    } while (result != -ECONNREFUSED && result != -EBADF);

    LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%p\n",
        (void*)pthread_self(), getpid(), (void*)result);
    
    mOut.writeInt32(BC_EXIT_LOOPER);
    talkWithDriver(false);
}

可见,最主要的是调用了getAndExecuteCommand

getAndExecuteCommand-->executeCommand-->const status_t error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,&reply, tr.flags);

reinterpret_cast<类型说明符>(表达式)

所以reinterpret_cast<BBinder*>(tr.cookie)的意思就是把tr.cookie强转成BBinder*类型的指针

很明显,这句的意思就是调用BBinder类的transact方法

status_t BBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    data.setDataPosition(0);

    status_t err = NO_ERROR;
    switch (code) {
        case PING_TRANSACTION:
            reply->writeInt32(pingBinder());
            break;
        default:
            err = onTransact(code, data, reply, flags);
            break;
    }

    if (reply != NULL) {
        reply->setDataPosition(0);
    }

    return err;
}

接着调用onTransact方法

再来看BnHelloService类,他是BnInterface的子类,BnInterface又是BBinder的子类,而BnHelloService重写了onTransact方法

所以这里的onTransact方法应该是BnHelloService::onTransact。BnHelloService::onTransact根据code值,最终调用到了BnHelloService类里提供的具体方法

这样,从Client端到Server端的调用流程,我们就分析完了。当然里面的分析有很多不完善的地方,欢迎大家指正

 

参考:

<1>https://zhuanlan.zhihu.com/p/35519585

<2>http://gityuan.com/2016/09/04/binder-start-service/ 彻底理解Android Binder通信架构

<3>http://zhoujinjian.cc/2018/01/01/Android-7-1-2-Android-N-Android-Binder%E7%B3%BB%E7%BB%9F%E5%88%86%E6%9E%90/index.html Android 7.1.2 (Android N) Android Binder 系统 分析

<4>韦东山Binder系统视频

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值