日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > Android >内容正文

Android

Android Binder 系统学习笔记(一)Binder系统的基本使用方法

發(fā)布時(shí)間:2024/7/19 Android 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android Binder 系统学习笔记(一)Binder系统的基本使用方法 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1.什么是RPC(遠(yuǎn)程過(guò)程調(diào)用)

Binder系統(tǒng)的目的是實(shí)現(xiàn)遠(yuǎn)程過(guò)程調(diào)用(RPC),即進(jìn)程A去調(diào)用進(jìn)程B的某個(gè)函數(shù),它是在進(jìn)程間通信(IPC)的基礎(chǔ)上實(shí)現(xiàn)的。RPC的一個(gè)應(yīng)用場(chǎng)景如下:

A進(jìn)程想去打開(kāi)LED,它會(huì)去調(diào)用led_open,然后調(diào)用led_ctl,但是如果A進(jìn)程并沒(méi)有權(quán)限去打開(kāi)驅(qū)動(dòng)程序呢?

假設(shè)此時(shí)有一個(gè)進(jìn)程B由權(quán)限去操作LED驅(qū)動(dòng)程序,那么進(jìn)程A可以通過(guò)如下方式來(lái)操作LED驅(qū)動(dòng):

①封裝數(shù)據(jù),即A進(jìn)程首先把想要調(diào)用的B進(jìn)程的某個(gè)函數(shù)的(事先約定好的)代號(hào)等信息封裝成數(shù)據(jù)包

②A進(jìn)程把封裝好了的數(shù)據(jù)包通過(guò)IPC(進(jìn)程間通信)發(fā)送給B進(jìn)程

③B取出數(shù)據(jù)之后,通過(guò)從數(shù)據(jù)包里解析出來(lái)的函數(shù)的代號(hào)來(lái)調(diào)用它自己相應(yīng)的led_open或led_ctl函數(shù)

整個(gè)過(guò)程的結(jié)果好像A程序直接來(lái)操縱LED一樣,這就是所謂的RPC。整個(gè)過(guò)程涉及到了IPC(進(jìn)程間通信)的三大要素,源、目的和數(shù)據(jù)。在這個(gè)例子里面,源就是進(jìn)程A,目的是進(jìn)程B,數(shù)據(jù)實(shí)際上就是一個(gè)雙方約定好了數(shù)據(jù)格式的buffer。

2.Binder系統(tǒng)實(shí)現(xiàn)的RPC

Binder系統(tǒng)采用的是CS架構(gòu),提供服務(wù)的進(jìn)程稱為server進(jìn)程,訪問(wèn)服務(wù)的進(jìn)程稱為client進(jìn)程,server進(jìn)程和client進(jìn)程的通信需要依靠?jī)?nèi)核中的Binder驅(qū)動(dòng)來(lái)進(jìn)行。同時(shí)Binder系統(tǒng)提供了一個(gè)上下文的管理者servicemanager, server進(jìn)程可以向servicemanager注冊(cè)服務(wù),然后client進(jìn)程可以通過(guò)向servicemanager查詢服務(wù)來(lái)獲取server進(jìn)程注冊(cè)的服務(wù)。

回到上面的例子,A進(jìn)程想操作LED,它可以通過(guò)將B進(jìn)程的某個(gè)函數(shù)的(事先約定好的)代號(hào)通過(guò)IPC發(fā)給B進(jìn)程,通過(guò)B進(jìn)程來(lái)間接的操作LED,但是如果A進(jìn)程不知道可以通過(guò)哪個(gè)進(jìn)程來(lái)間接的操作LED呢,它應(yīng)該將封裝好了的數(shù)據(jù)包發(fā)送給哪個(gè)進(jìn)程呢?這就引入了Binder系統(tǒng)的大管家servicemanager。首先B進(jìn)程向servicemanager注冊(cè)LED服務(wù),然后我們的A進(jìn)程就可以通過(guò)向servicemanager查詢LED服務(wù),就會(huì)得到一個(gè)handle,這個(gè)handle就是指向進(jìn)程B的,這樣進(jìn)程A就知道把數(shù)據(jù)包(約定好數(shù)據(jù)格式的buffer)發(fā)送給哪個(gè)進(jìn)程就可以間接的操作LED了。在這個(gè)例子中進(jìn)程B就是server進(jìn)程,進(jìn)程A是client進(jìn)程。

小小的總結(jié)一下,在 Binder系統(tǒng)中主要涉及到4個(gè)東西,一個(gè)是我們的A進(jìn)程也就是client進(jìn)程,一個(gè)是B進(jìn)程也就是我們的server進(jìn)程。client進(jìn)程怎么知道要向哪一個(gè)server進(jìn)程發(fā)送數(shù)據(jù)呢,中間就引入了Binder系統(tǒng)的大管家servicemanager。client進(jìn)程、server進(jìn)程和servicemanager之間的通信是建立在內(nèi)核binder驅(qū)動(dòng)的基礎(chǔ)上的,它們四個(gè)的關(guān)系如下圖所示

?

3.Binder系統(tǒng)的簡(jiǎn)單應(yīng)用(基于Android內(nèi)核,拋開(kāi)Android系統(tǒng)框架)

在Android源碼里面有一些C語(yǔ)言寫的binder應(yīng)用程序

frameworks/native/cmds/servicemanager/bctest.c frameworks/native/cmds/servicemanager/binder.c frameworks/native/cmds/servicemanager/binder.h frameworks/native/cmds/servicemanager/service_manager.c

我們可以參照這些程序,基于Android內(nèi)核,在Linux上實(shí)現(xiàn)一個(gè)Binder RPC的程序來(lái)理解使用Binder實(shí)現(xiàn)進(jìn)程間通信的整個(gè)函數(shù)調(diào)用過(guò)程。

我們首先把a(bǔ)ndroid源碼frameworks/native/cmds/servicemanager目錄下的內(nèi)容拷貝到我們自己的工程中,然后基于bctest.c來(lái)實(shí)現(xiàn)我們的server和client程序,因?yàn)槲覀兪敲撾xAndroid系統(tǒng)來(lái)實(shí)現(xiàn)的,所以還需要將依賴的頭文件拷貝到工程中,然后對(duì)service_manager.c和binder.c做一些修改,去掉一些不必要的內(nèi)容。最后我們還需要寫一個(gè)Makefile文件來(lái)構(gòu)建整個(gè)工程,工程結(jié)構(gòu)如下圖所示。

3.1.Server進(jìn)程

首先實(shí)現(xiàn)Server程序,它實(shí)現(xiàn)兩個(gè)函數(shù),sayhello和sayhello_to,并通過(guò)binder系統(tǒng)將向ServiceManager注冊(cè)服務(wù),然后循環(huán)的從binder驅(qū)動(dòng)讀取client進(jìn)程發(fā)過(guò)來(lái)請(qǐng)求數(shù)據(jù),并且通過(guò)這些請(qǐng)求數(shù)據(jù)調(diào)用自己相應(yīng)的sayhello和sayhello_to函數(shù)。整個(gè)過(guò)程如下圖所示。

接著我們就來(lái)分析以下具體的代碼

/*test_server.h*/#ifndef _TEST_SERVER_H #define _TEST_SERVER_H /*事先約定好的Server進(jìn)程的相應(yīng)函數(shù)的代號(hào)*/ #define HELLO_SVR_CMD_SAYHELLO 0 #define HELLO_SVR_CMD_SAYHELLO_TO 1 #endif // _TEST_SERVER_H

?

/*test_server.c*//* Copyright 2008 The Android Open Source Project*/ #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <linux/types.h> #include<stdbool.h> #include <string.h> #include <private/android_filesystem_config.h> #include "binder.h" #include "test_server.h" int svcmgr_publish(struct binder_state *bs, uint32_t target, const char *name, void *ptr) {int status;unsigned iodata[512/4];struct binder_io msg, reply;bio_init(&msg, iodata, sizeof(iodata), 4);bio_put_uint32(&msg, 0); // strict mode headerbio_put_string16_x(&msg, SVC_MGR_NAME);bio_put_string16_x(&msg, name);bio_put_obj(&msg, ptr);/*遠(yuǎn)程調(diào)用ServiceManager的do_add_service函數(shù)*/if (binder_call(bs, &msg, &reply, target, SVC_MGR_ADD_SERVICE))return -1;status = bio_get_uint32(&reply);binder_done(bs, &msg, &reply);return status; } void sayhello(void) {static int cnt = 0;fprintf(stderr, "say hello : %d\n", cnt++); } int sayhello_to(char *name) {static int cnt = 0;fprintf(stderr, "say hello to %s : %d\n", name, cnt++);return cnt; } int hello_service_handler(struct binder_state *bs,struct binder_transaction_data *txn,struct binder_io *msg,struct binder_io *reply) {/* 根據(jù)txn->code知道要調(diào)用哪一個(gè)函數(shù)* 如果需要參數(shù), 可以從msg取出* 如果要返回結(jié)果, 可以把結(jié)果放入reply*//* sayhello* sayhello_to*/uint16_t *s;char name[512];size_t len;uint32_t handle;uint32_t strict_policy;int i;// Equivalent to Parcel::enforceInterface(), reading the RPC// header with the strict mode policy mask and the interface name.// Note that we ignore the strict_policy and don't propagate it// further (since we do no outbound RPCs anyway).strict_policy = bio_get_uint32(msg);switch(txn->code) {case HELLO_SVR_CMD_SAYHELLO:sayhello();return 0;case HELLO_SVR_CMD_SAYHELLO_TO:/* 從msg里取出字符串 */s = bio_get_string16(msg, &len);if (s == NULL) {return -1;}for (i = 0; i < len; i++)name[i] = s[i];name[i] = '\0';/* 處理 */i = sayhello_to(name);/* 把結(jié)果放入reply */bio_put_uint32(reply, i);break;default:fprintf(stderr, "unknown code %d\n", txn->code);return -1;}return 0; } int main(int argc, char **argv) {int fd;struct binder_state *bs;uint32_t svcmgr = BINDER_SERVICE_MANAGER;uint32_t handle;int ret;/*打開(kāi)并映射binder驅(qū)動(dòng)*/bs = binder_open(128*1024);if (!bs) {fprintf(stderr, "failed to open binder driver\n");return -1;}/* 向ServiceManager注冊(cè)服務(wù) */ret = svcmgr_publish(bs, svcmgr, "hello", (void *)123);if (ret) {fprintf(stderr, "failed to publish hello service\n");return -1;}ret = svcmgr_publish(bs, svcmgr, "goodbye", (void *)124);if (ret) {fprintf(stderr, "failed to publish goodbye service\n");} #if 0while (1){/* read data *//* parse data, and process *//* reply */} #endif/*通過(guò)我們傳入的hello_service_handler循環(huán)處理從binder驅(qū)動(dòng)讀出的數(shù)據(jù)*/binder_loop(bs, hello_service_handler);return 0; }

接著我們來(lái)分析一下這個(gè)binder_loop函數(shù),它主要實(shí)現(xiàn)了3個(gè)功能

1.讀數(shù)據(jù)

2.解析并處理數(shù)據(jù)

3.回復(fù)

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 表明下面的ioctl不會(huì)發(fā)起寫操作,只不過(guò)發(fā)起讀操作bwr.write_size = 0;bwr.write_consumed = 0;bwr.write_buffer = 0;readbuf[0] = BC_ENTER_LOOPER;binder_write(bs, readbuf, sizeof(uint32_t));for (;;) {bwr.read_size = sizeof(readbuf);bwr.read_consumed = 0;bwr.read_buffer = (uintptr_t) readbuf;/*通過(guò)ioctl從binder驅(qū)動(dòng)中讀數(shù)據(jù)*/res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);if (res < 0) {ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));break;}//讀到數(shù)據(jù)之后調(diào)用binder_parse解析數(shù)據(jù),如果傳入func參數(shù)還會(huì)處理數(shù)據(jù)res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);if (res == 0) {ALOGE("binder_loop: unexpected reply?!\n");break;}if (res < 0) {ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));break;}} }

看一下我們是怎么處理數(shù)據(jù)的,注意我們傳入的binder_handler這個(gè)參數(shù),它是一個(gè)函數(shù)指針

int binder_parse(struct binder_state *bs, struct binder_io *bio,uintptr_t ptr, size_t size, binder_handler func) {int r = 1;uintptr_t end = ptr + (uintptr_t) size;while (ptr < end) {uint32_t cmd = *(uint32_t *) ptr;ptr += sizeof(uint32_t); #if TRACEfprintf(stderr,"%s:\n", cmd_name(cmd)); #endifswitch(cmd) {case BR_NOOP:break;case BR_TRANSACTION_COMPLETE:break;case BR_INCREFS:case BR_ACQUIRE:case BR_RELEASE:case BR_DECREFS: #if TRACEfprintf(stderr," %p, %p\n", (void *)ptr, (void *)(ptr + sizeof(void *))); #endifptr += sizeof(struct binder_ptr_cookie);break;//我們收到的命令是BR_TRANSACTIONcase BR_TRANSACTION: {struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;if ((end - ptr) < sizeof(*txn)) {ALOGE("parse: txn too small!\n");return -1;}binder_dump_txn(txn);if (func) {unsigned rdata[256/4];struct binder_io msg;struct binder_io reply;int res;//接收到數(shù)據(jù)之后,構(gòu)造一個(gè)binder_iobio_init(&reply, rdata, sizeof(rdata), 4);bio_init_from_txn(&msg, txn);//調(diào)用我們的處理函數(shù)res = func(bs, txn, &msg, &reply);//處理完之后發(fā)送一個(gè)replybinder_send_reply(bs, &reply, txn->data.ptr.buffer, res);}ptr += sizeof(*txn);break;}case BR_REPLY: {struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;if ((end - ptr) < sizeof(*txn)) {ALOGE("parse: reply too small!\n");return -1;}binder_dump_txn(txn);if (bio) {bio_init_from_txn(bio, txn);bio = 0;} else {/* todo FREE BUFFER */}ptr += sizeof(*txn);r = 0;break;}case BR_DEAD_BINDER: {struct binder_death *death = (struct binder_death *)(uintptr_t) *(binder_uintptr_t *)ptr;ptr += sizeof(binder_uintptr_t);death->func(bs, death->ptr);break;}case BR_FAILED_REPLY:r = -1;break;case BR_DEAD_REPLY:r = -1;break;default:ALOGE("parse: OOPS %d\n", cmd);return -1;}}return r; }

3.2.Client進(jìn)程

Client進(jìn)程和Server進(jìn)程的大致流程差不多,它首先打開(kāi)和映射binder驅(qū)動(dòng),然后向ServiceManager查詢服務(wù),最后通過(guò)查詢服務(wù)時(shí)ServiceManager返回的handle遠(yuǎn)程調(diào)用Server進(jìn)程的函數(shù),主要流程如下所示。

下面我們就分析一下具體的源碼

/*test_client.c*//* Copyright 2008 The Android Open Source Project*/ #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <linux/types.h> #include<stdbool.h> #include <string.h> #include <private/android_filesystem_config.h> #include "binder.h" #include "test_server.h" uint32_t svcmgr_lookup(struct binder_state *bs, uint32_t target, const char *name) {uint32_t handle;unsigned iodata[512/4];struct binder_io msg, reply;bio_init(&msg, iodata, sizeof(iodata), 4);bio_put_uint32(&msg, 0); // strict mode headerbio_put_string16_x(&msg, SVC_MGR_NAME);bio_put_string16_x(&msg, name);/*遠(yuǎn)程調(diào)用ServiceManager的do_find_service函數(shù)*/if (binder_call(bs, &msg, &reply, target, SVC_MGR_CHECK_SERVICE))return 0;handle = bio_get_ref(&reply);if (handle)binder_acquire(bs, handle);binder_done(bs, &msg, &reply);return handle; } struct binder_state *g_bs; uint32_t g_handle; void sayhello(void) {unsigned iodata[512/4];struct binder_io msg, reply;/* 構(gòu)造binder_io */bio_init(&msg, iodata, sizeof(iodata), 4);bio_put_uint32(&msg, 0); // strict mode header/* 放入?yún)?shù) *//* 調(diào)用binder_call遠(yuǎn)程調(diào)用Server的sayhello函數(shù)*/if (binder_call(g_bs, &msg, &reply, g_handle, HELLO_SVR_CMD_SAYHELLO))return ;/* 從reply中解析出返回值 */binder_done(g_bs, &msg, &reply);} int sayhello_to(char *name) {unsigned iodata[512/4];struct binder_io msg, reply;int ret;/* 構(gòu)造binder_io */bio_init(&msg, iodata, sizeof(iodata), 4);bio_put_uint32(&msg, 0); // strict mode header/* 放入?yún)?shù) */bio_put_string16_x(&msg, name);/* 調(diào)用binder_call遠(yuǎn)程調(diào)用Server的sayhello_to函數(shù) */if (binder_call(g_bs, &msg, &reply, g_handle, HELLO_SVR_CMD_SAYHELLO_TO))return 0;/* 從reply中解析出返回值 */ret = bio_get_uint32(&reply);binder_done(g_bs, &msg, &reply);return ret;} /* ./test_client hello* ./test_client hello <name>*/ int main(int argc, char **argv) {int fd;struct binder_state *bs;uint32_t svcmgr = BINDER_SERVICE_MANAGER;uint32_t handle;int ret;if (argc < 2){fprintf(stderr, "Usage:\n");fprintf(stderr, "%s hello\n", argv[0]);fprintf(stderr, "%s hello <name>\n", argv[0]);return -1;}/*打開(kāi)binder驅(qū)動(dòng)*/bs = binder_open(128*1024);if (!bs) {fprintf(stderr, "failed to open binder driver\n");return -1;}g_bs = bs;/* 向ServiceManager查詢hello服務(wù) */handle = svcmgr_lookup(bs, svcmgr, "hello");if (!handle) {fprintf(stderr, "failed to get hello service\n");return -1;}g_handle = handle;/* send data to server */if (argc == 2) {sayhello();} else if (argc == 3) {ret = sayhello_to(argv[2]);fprintf(stderr, "get ret of sayhello_to = %d\n", ret); }binder_release(bs, handle);return 0; }

這里需要注意的一點(diǎn)是,不管我們的Server進(jìn)程還是Client進(jìn)程,他們?cè)谶h(yuǎn)程調(diào)用其他進(jìn)程的函數(shù)的時(shí)候,都是通過(guò)binder_call這個(gè)函數(shù)來(lái)實(shí)現(xiàn)的,下面我們就來(lái)分析一下這個(gè)函數(shù)。

int binder_call(struct binder_state *bs,struct binder_io *msg, struct binder_io *reply,uint32_t target, uint32_t code) {int res;/*構(gòu)造參數(shù)*/struct binder_write_read bwr;struct {uint32_t cmd;struct binder_transaction_data txn;} __attribute__((packed)) writebuf;unsigned readbuf[32];if (msg->flags & BIO_F_OVERFLOW) {fprintf(stderr,"binder: txn buffer overflow\n");goto fail;}writebuf.cmd = BC_TRANSACTION;writebuf.txn.target.handle = target;writebuf.txn.code = code;writebuf.txn.flags = 0;writebuf.txn.data_size = msg->data - msg->data0;writebuf.txn.offsets_size = ((char*) msg->offs) - ((char*) msg->offs0);writebuf.txn.data.ptr.buffer = (uintptr_t)msg->data0;writebuf.txn.data.ptr.offsets = (uintptr_t)msg->offs0;bwr.write_size = sizeof(writebuf);bwr.write_consumed = 0;bwr.write_buffer = (uintptr_t) &writebuf;hexdump(msg->data0, msg->data - msg->data0);for (;;) {bwr.read_size = sizeof(readbuf);bwr.read_consumed = 0;bwr.read_buffer = (uintptr_t) readbuf;/*調(diào)用ioctl發(fā)送數(shù)據(jù)*/res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);if (res < 0) {fprintf(stderr,"binder: ioctl failed (%s)\n", strerror(errno));goto fail;}/*解析返回的數(shù)據(jù)*/res = binder_parse(bs, reply, (uintptr_t) readbuf, bwr.read_consumed, 0);if (res == 0) return 0;if (res < 0) goto fail;} fail:memset(reply, 0, sizeof(*reply));reply->flags |= BIO_F_IOERROR;return -1; }

其中第一個(gè)參數(shù)用來(lái)描述當(dāng)前binder的狀態(tài),是調(diào)用binder_open時(shí)返回的,第二個(gè)參數(shù)是要發(fā)送的數(shù)據(jù),第三個(gè)參數(shù)用來(lái)保存返回的數(shù)據(jù),第四非參數(shù)是數(shù)據(jù)發(fā)送的目的地,即向誰(shuí)發(fā)送數(shù)據(jù),第五個(gè)參數(shù)是要調(diào)用的遠(yuǎn)程的函數(shù)的約定好的代號(hào)。

3.3.ServiceManager進(jìn)程

分析完了Server進(jìn)程和Client進(jìn)程,緊接著就要來(lái)分析我們的大管家ServiceManager進(jìn)程了,我們的Client進(jìn)程想使用sayhello函數(shù)的時(shí)候,是不知道sayhello函數(shù)是屬于哪一個(gè)進(jìn)程的,有了我們的大管家之后,Client進(jìn)程才能通過(guò)它來(lái)查找到Server進(jìn)程。在Server進(jìn)程向ServiceManager注冊(cè)服務(wù)和Client進(jìn)程向ServiceManager查詢服務(wù)的時(shí)候,ServiceManager相對(duì)而言都是Server進(jìn)程。下面就來(lái)分析一下這個(gè)大管家。 它首先也是打開(kāi)和映射binder驅(qū)動(dòng),然后告訴binder驅(qū)動(dòng),我就是大管家,最后循環(huán)接收Server進(jìn)程和Client進(jìn)程的請(qǐng)求,它的主要流程如下圖所示。

?

緊接著我們就來(lái)分析一下它的main函數(shù),和其他一些主要的函數(shù)

int main(int argc, char **argv) {struct binder_state *bs;/*打開(kāi)binder驅(qū)動(dòng)*/bs = binder_open(128*1024);if (!bs) {ALOGE("failed to open binder driver\n");return -1;}/*告訴驅(qū)動(dòng),我是大管家*/if (binder_become_context_manager(bs)) {ALOGE("cannot become context manager (%s)\n", strerror(errno));return -1;}svcmgr_handle = BINDER_SERVICE_MANAGER;/*進(jìn)入無(wú)限循環(huán),處理client端發(fā)來(lái)的請(qǐng)求*/binder_loop(bs, svcmgr_handler);return 0; }

分析一下binder_become_context_manager這個(gè)函數(shù),看一下是怎樣向驅(qū)動(dòng)注冊(cè)為大管家的

int binder_become_context_manager(struct binder_state *bs) {/*通過(guò)ioctl,傳遞BINDER_SET_CONTEXT_MGR指令*/return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0); }

整個(gè)流程的時(shí)序如下圖所示

總結(jié)一下,整個(gè)binder遠(yuǎn)程過(guò)程調(diào)用,就是首先大管家ServiceManager告訴binder驅(qū)動(dòng),我現(xiàn)在是大管家了,然后Server進(jìn)程和Client進(jìn)程通過(guò)這個(gè)大管家互相了解了之后,Client進(jìn)程就可以遠(yuǎn)程調(diào)用Server進(jìn)程的函數(shù)了。

?

?

參考文章: 韋東山老師的binder系統(tǒng)分析的視頻:www.100ask.org Gityuan的博客:http://gityuan.com/

?

?

轉(zhuǎn)載于:https://www.cnblogs.com/CoderTian/p/6158611.html

總結(jié)

以上是生活随笔為你收集整理的Android Binder 系统学习笔记(一)Binder系统的基本使用方法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。