{"id":4898,"date":"2023-09-05T14:20:41","date_gmt":"2023-09-05T06:20:41","guid":{"rendered":"https:\/\/blog.coderfan.org\/?p=4898"},"modified":"2023-09-05T14:20:50","modified_gmt":"2023-09-05T06:20:50","slug":"android-native-service-registeration","status":"publish","type":"post","link":"https:\/\/blog.coderfan.org\/en\/android-native-service-registeration.html","title":{"rendered":"Android Native\u670d\u52a1\u6ce8\u518c\u6d41\u7a0b\u6982\u8ff0"},"content":{"rendered":"<div class='booster-block booster-read-block'>\n                <div class=\"twp-read-time\">\n                \t<i class=\"booster-icon twp-clock\"><\/i> <span>Read Time:<\/span>9 Minute, 22 Second                <\/div>\n\n            <\/div>\n<p class=\"has-text-align-justify\">\u5728Android\u7cfb\u7edf\u4e2d\uff0cJava Framework\u5c42\u7684\u8bf8\u591a\u670d\u52a1\u90fd\u901a\u8fc7Binder\u4e0eNative\u5c42\u7684\u670d\u52a1\u8fdb\u884c\u901a\u4fe1\uff1b\u57fa\u4e8e\u6b64\u518d\u5bf9\u5916\u63d0\u4f9b\u63a5\u53e3\u7ed9\u5404\u4e2aJava\u5e94\u7528\u3002\u5728\u8fd9\u79cd\u901a\u4fe1\u6a21\u5f0f\u4e0b\uff0c\u4e24\u7aef\u9075\u5b88\u7684\u662fC-S\u67b6\u6784\u3002\u4e00\u822c\u800c\u8a00Native\u670d\u52a1\u4f1a\u4f5c\u4e3aServer\u7aef\u800c\u5b58\u5728\u3002<\/p>\n\n\n\n<p class=\"has-text-align-justify\">Native\u670d\u52a1\u4f5c\u4e3aServer\u7aef\uff0c\u662f\u9700\u8981\u6ce8\u518c\u5230serviceManager\u6210\u4e3a\u670d\u52a1\u4e4b\u540e\u624d\u80fd\u88ab\u7cfb\u7edf\u4e2d\u7684\u5404\u4e2aClient\u8fdb\u884c\u83b7\u53d6\u3001\u8fde\u63a5\u4e0e\u4f7f\u7528\u7684\u3002\u672c\u7bc7\u535a\u5ba2\u5c06\u4ee5Android12\u7684\u6e90\u7801\u4e3a\u57fa\u7840\uff0c\u7b80\u5355\u5206\u6790\u4e00\u4e0bAndroid Native\u670d\u52a1\u7684\u6ce8\u518c\u6d41\u7a0b\u3002<\/p>\n\n\n\n<p>\u8fd9\u91cc\u6211\u4eec\u5148\u4ee5CameraService\u4e3a\u4f8b\u505a\u4e00\u4e2a\u5206\u6790\u8bb2\u89e3\u3002<\/p>\n\n\n\n<p class=\"has-text-align-justify\">CameraService\u5728Native\u5c42\u4e3b\u8981\u5206\u4e3a\u4e24\u4e2a\u90e8\u5206\uff1a\u4e00\u4e2a\u662f\u4ee5binary\u5f62\u5f0f\u800c\u5b58\u5728\u7684cameraserver\uff1b\u53e6\u5916\u4e00\u4e2a\u5219\u662f\u4ee5\u5171\u4eab\u5e93\u5f62\u5f0f\u800c\u5b58\u5728\u7684libcameraservice\u3002\u524d\u8005\u4f5c\u4e3aCamearService\u7684\u7a0b\u5e8f\u5165\u53e3\uff0c\u800c\u540e\u8005\u5219\u662f\u5177\u4f53\u7684\u903b\u8f91\u4e3b\u4f53\u5b9e\u73b0,\u4e14\u540e\u8005\u662f\u524d\u8005\u7684\u4f9d\u8d56\u9879\u4e4b\u4e00\uff0c\u7531\u524d\u8005\u8d1f\u8d23\u8c03\u7528\u3002\u5176\u670d\u52a1\u6ce8\u518c\u7684\u4e3b\u4f53\u90e8\u5206\uff0c\u4f4d\u4e8ecameraserver\u5185\uff0c\u603b\u4f53\u6d89\u53ca\u7684\u5230\u6e90\u7801\u8def\u5f84\u5982\u4e0b\uff1a<\/p>\n\n\n\n<pre class=\"io-enlighter-pre\"><code class=\"gl\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-highlight=\"\">\/\/cameraserver\nframeworks\/av\/camera\/cameraserver\n\n\/\/libcameraservice\nframeworks\/av\/services\/camera\/libcameraservice<\/code><\/pre>\n\n\n\n<p class=\"has-text-align-justify\">\u6211\u4eec\u627e\u5230cameraserver\u542f\u52a8\u7684\u6e90\u7801\u6587\u4ef6\uff1amain_cameraserver.cpp,\u5176\u6e90\u7801\u5185\u5bb9\u975e\u5e38\u7cbe\u7b80\uff0c\u5177\u4f53\u5185\u5bb9\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n\n\n\n<pre class=\"io-enlighter-pre\"><code class=\"gl\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-highlight=\"\">#define LOG_TAG \"cameraserver\"\n\/\/#define LOG_NDEBUG 0\n\n#include \"CameraService.h\"\n#include &lt;hidl\/HidlTransportSupport.h>\n\nusing namespace android;\n\nint main(int argc __unused, char** argv __unused)\n{\n    signal(SIGPIPE, SIG_IGN);\n\n    \/\/ Set 5 threads for HIDL calls. Now cameraserver will serve HIDL calls in\n    \/\/ addition to consuming them from the Camera HAL as well.\n    hardware::configureRpcThreadpool(5, \/*willjoin*\/ false);\n\n    sp&lt;ProcessState> proc(ProcessState::self());\n    sp&lt;IServiceManager> sm = defaultServiceManager();\n    ALOGI(\"ServiceManager: %p\", sm.get());\n    CameraService::instantiate();\n    ALOGI(\"ServiceManager: %p done instantiate\", sm.get());\n    ProcessState::self()->startThreadPool();\n    IPCThreadState::self()->joinThreadPool();\n}<\/code><\/pre>\n\n\n\n<p>\u5728cameraserver\u7684\u670d\u52a1\u542f\u52a8\u8fc7\u7a0b\u4e2d\uff0c\u5c31\u5305\u542b\u4e86\u670d\u52a1\u6ce8\u518c\u7684\u90e8\u5206\uff0c\u4e3b\u8981\u5305\u542b\u4ee5\u4e0b\u51e0\u4e2a\u6b65\u9aa4\uff1a<\/p>\n\n\n\n<p>1.\u83b7\u53d6ProcessState\u5bf9\u8c61\uff0c\u7528\u4e8e\u8fde\u63a5\u5e95\u5c42binder\u9a71\u52a8;<\/p>\n\n\n\n<p>2.\u83b7\u53d6ServiceManager\u5b9e\u4f8b\uff0c\u901a\u8fc7\u7236\u7c7binstantiate\u63a5\u53e3\u53d1\u5e03\u5230Android\u7cfb\u7edf\u4e2d;<\/p>\n\n\n\n<p>3.\u542f\u52a8binder\u7ebf\u7a0b\u6c60\uff0c\u8be5\u7ebf\u7a0b\u6c60\u7528\u4e8e\u5904\u7406Binder IPC\u901a\u4fe1;<\/p>\n\n\n\n<p>4.\u7ebf\u7a0b\u6267\u884c\uff0c\u6b64\u65f6\u53ef\u4ee5\u5904\u7406Binder IPC\u8bf7\u6c42;<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">\u83b7\u53d6ProcessState\u5bf9\u8c61<\/h4>\n\n\n\n<p>\u8fd9\u4e00\u6b65\u7684\u76ee\u7684\u5176\u5b9e\u662f\u4e3a\u4e86\u901a\u8fc7libbinder\u8fde\u63a5\u5e95\u5c42binder\u9a71\u52a8(\/dev\/binder),\u6d89\u53ca\u5230\u7684\u6e90\u7801\u6587\u4ef6\u5982\u4e0b\uff1a<\/p>\n\n\n\n<pre class=\"io-enlighter-pre\"><code class=\"gl\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-highlight=\"\">frameworks\/native\/libs\/binder\/include\/binder\/ProcessState.h \/\/\u5934\u6587\u4ef6\nframeworks\/native\/libs\/binder\/ProcessState.cpp \/\/\u6e90\u7801\u6587\u4ef6<\/code><\/pre>\n\n\n\n<p>ProcessState\u7c7b\u5728\u6e90\u7801\u4e2d\u662f\u4e00\u4e2a\u5355\u4f8b\u5b9e\u73b0\uff0c\u90e8\u5206\u5173\u952e\u6e90\u7801\u5982\u4e0b\uff1a<\/p>\n\n\n\n<pre class=\"io-enlighter-pre\"><code class=\"gl\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-highlight=\"\">\/\/frameworks\/native\/libs\/binder\/ProcessState.cpp\n\n#ifdef __ANDROID_VNDK__\nconst char* kDefaultDriver = \"\/dev\/vndbinder\";\n#else\nconst char* kDefaultDriver = \"\/dev\/binder\";\n#endif\n\n\/\/native \u670d\u52a1\u4f7f\u7528\u8be5\u63a5\u53e3\u8fdb\u884cbinder\u521d\u59cb\u5316,\u5982\nsp&lt;ProcessState> ProcessState::self()\n{\n    return init(kDefaultDriver, false \/*requireDefault*\/);\n}\n\/\/init \u51fd\u6570\u4f5c\u4e3a\u4e3b\u5165\u53e3\nsp&lt;ProcessState> ProcessState::init(const char *driver, bool requireDefault)\n{\n    [[clang::no_destroy]] static sp&lt;ProcessState> gProcess;\n    [[clang::no_destroy]] static std::mutex gProcessMutex;\n    if (driver == nullptr) {\n        std::lock_guard&lt;std::mutex> l(gProcessMutex);\n        return gProcess;\n    }\n    [[clang::no_destroy]] static std::once_flag gProcessOnce;\n    std::call_once(gProcessOnce, [&amp;](){\n        if (access(driver, R_OK) == -1) {\n            ALOGE(\"Binder driver %s is unavailable. Using \/dev\/binder instead.\", driver);\n            driver = \"\/dev\/binder\";\n        }\n        std::lock_guard&lt;std::mutex> l(gProcessMutex);\n        \/\/use sp&lt;CLASS>::make to construct an sp object\n        gProcess = sp&lt;ProcessState>::make(driver);\n    });\n    if (requireDefault) {\n        \/\/ Detect if we are trying to initialize with a different driver, and\n        \/\/ consider that an error. ProcessState will only be initialized once above.\n        LOG_ALWAYS_FATAL_IF(gProcess->getDriverName() != driver,\n                            \"ProcessState was already initialized with %s,\"\n                            \" can't initialize with %s.\",\n                            gProcess->getDriverName().c_str(), driver);\n    }\n    return gProcess;\n}\n\n\/\/ProcessState constructor\nProcessState::ProcessState(const char *driver)\n    : mDriverName(String8(driver))\n    , mDriverFD(open_driver(driver))\n    , mVMStart(MAP_FAILED)\n    , mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)\n    , mThreadCountDecrement(PTHREAD_COND_INITIALIZER)\n    , mExecutingThreadsCount(0)\n    , mWaitingForThreads(0)\n    , mMaxThreads(DEFAULT_MAX_BINDER_THREADS)\n    , mStarvationStartTimeMs(0)\n    , mThreadPoolStarted(false)\n    , mThreadPoolSeq(1)\n    , mCallRestriction(CallRestriction::NONE)\n{\n    if (mDriverFD >= 0) {\n        \/\/ mmap the binder, providing a chunk of virtual address space to receive transactions.\n        mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);\n        if (mVMStart == MAP_FAILED) {\n            \/\/ *sigh*\n            ALOGE(\"Using %s failed: unable to mmap transaction memory.\\n\", mDriverName.c_str());\n            close(mDriverFD);\n            mDriverFD = -1;\n            mDriverName.clear();\n        }\n    }\n#ifdef __ANDROID__\n    LOG_ALWAYS_FATAL_IF(mDriverFD &lt; 0, \"Binder driver '%s' could not be opened.  Terminating.\", driver);\n#endif\n}\n\/\/\u6784\u9020\u51fd\u6570\u5217\u8868\u521d\u59cb\u5316\u4e2d\u901a\u8fc7open_driver\u8fde\u63a5\u9a71\u52a8\nstatic int open_driver(const char *driver)\n{\n    int fd = open(driver, O_RDWR | O_CLOEXEC);\n    if (fd >= 0) {\n        int vers = 0;\n        status_t result = ioctl(fd, BINDER_VERSION, &amp;vers);\n        if (result == -1) {\n            ALOGE(\"Binder ioctl to obtain version failed: %s\", strerror(errno));\n            close(fd);\n            fd = -1;\n        }\n        if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {\n          ALOGE(\"Binder driver protocol(%d) does not match user space protocol(%d)! ioctl() return value: %d\",\n                vers, BINDER_CURRENT_PROTOCOL_VERSION, result);\n            close(fd);\n            fd = -1;\n        }\n        size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;\n        result = ioctl(fd, BINDER_SET_MAX_THREADS, &amp;maxThreads);\n        if (result == -1) {\n            ALOGE(\"Binder ioctl to set max threads failed: %s\", strerror(errno));\n        }\n        uint32_t enable = DEFAULT_ENABLE_ONEWAY_SPAM_DETECTION;\n        result = ioctl(fd, BINDER_ENABLE_ONEWAY_SPAM_DETECTION, &amp;enable);\n        if (result == -1) {\n            ALOGD(\"Binder ioctl to enable oneway spam detection failed: %s\", strerror(errno));\n        }\n    } else {\n        ALOGW(\"Opening '%s' failed: %s\\n\", driver, strerror(errno));\n    }\n    return fd;\n}<\/code><\/pre>\n\n\n\n<p class=\"has-text-align-justify\">\u5728Native Service\u542f\u52a8\u65f6\uff0c\u6211\u4eec\u901a\u8fc7ProcessState::self()\u83b7\u53d6\u5230\u5355\u4f8b\u5bf9\u8c61\uff0c\u518d\u901a\u8fc7 ProcessState \u7684\u62f7\u8d1d\u6784\u9020\u51fd\u6570\u8d4b\u503c\u7ed9proc\uff0c\u4ece\u800c\u5f97\u5230\u540d\u4e3aproc\u6307\u5411 ProcessState \u7684\u667a\u80fd\u6307\u9488\u3002<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"> \u83b7\u53d6ServiceManager\u5b9e\u4f8b <\/h4>\n\n\n\n<p>\u83b7\u53d6IServiceManager\u5b9e\u4f8b\u662f\u4e3a\u4e86\u540e\u7eed\u5c06\u670d\u52a1\u6dfb\u52a0\u5230ServiceManager\u4e2d\uff0c\u8fd9\u4e00\u6b65\u6d89\u53ca\u5230\u7684\u6e90\u7801\u6587\u4ef6\u5982\u4e0b\uff1a<\/p>\n\n\n\n<pre class=\"io-enlighter-pre\"><code class=\"gl\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-highlight=\"\">\/frameworks\/native\/libs\/binder\/include\/binder\/IServiceManager.h  \/\/\u5934\u6587\u4ef6\n\/frameworks\/native\/libs\/binder\/IServiceManager.cpp  \/\/\u6e90\u6587\u4ef6<\/code><\/pre>\n\n\n\n<p>IServiceManager\u81ea\u8eab\u4e5f\u662f\u5355\u4f8b\u5b9e\u73b0\uff0c\u5176\u5b9e\u73b0\u90e8\u5206\u7684\u4ee3\u7801\u5982\u4e0b\uff1a<\/p>\n\n\n\n<pre class=\"io-enlighter-pre\"><code class=\"gl\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-highlight=\"\">using AidlServiceManager = android::os::IServiceManager;\nsp&lt;IServiceManager> defaultServiceManager()\n{\n\/\/\u901a\u8fc7std::call_once\u786e\u4fdd\u5728\u591a\u7ebf\u7a0b\u5e76\u884c\u8c03\u7528\u60c5\u51b5\u4e0b\u4e5f\u53ea\u4f1a\u6267\u884c\u4e00\u6b21\n    std::call_once(gSmOnce, []() {\n        sp&lt;AidlServiceManager> sm = nullptr;\n        while (sm == nullptr) {\n            sm = interface_cast&lt;AidlServiceManager>(ProcessState::self()->getContextObject(nullptr));\n            if (sm == nullptr) {\n                ALOGE(\"Waiting 1s on context object on %s.\", ProcessState::self()->getDriverName().c_str());\n                sleep(1);\n            }\n        }\n        gDefaultServiceManager = sp&lt;ServiceManagerShim>::make(sm);\n    });\n\n    return gDefaultServiceManager;\n}<\/code><\/pre>\n\n\n\n<p class=\"has-text-align-justify\">\u5728defaultServiceManager\u51fd\u6570\u5b9e\u73b0\u4e2d\uff0c\u9996\u5148\u901a\u8fc7std::call_once\u6765\u786e\u4fdd\u591a\u7ebf\u7a0b\u5e76\u53d1\u8c03\u7528\u7684\u60c5\u51b5\u4e0b\u51fd\u6570\u4e3b\u4f53\u53ea\u4f1a\u8c03\u7528\u4e00\u6b21\uff0c\u5e76\u4e14\u901a\u8fc7while\u5faa\u73af\u6765\u786e\u4fdd\u5728serviceManager\u8fdb\u7a0b\u542f\u52a8\u6162\u65f6\u901a\u8fc7sleep\u4f11\u7720\u6765\u6682\u7f13\u83b7\u53d6\u7684\u8fc7\u7a0b\uff0c\u5176\u4e2d\u6700\u4e3a\u5173\u952e\u7684\u63a5\u53e3\u5728\u4e8egetContextObject\u51fd\u6570\uff0c\u5176\u5177\u4f53\u5185\u5bb9\u5982\u4e0b\uff1a<\/p>\n\n\n\n<pre class=\"io-enlighter-pre\"><code class=\"gl\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-highlight=\"\">\nsp&lt;IBinder> ProcessState::getContextObject(const sp&lt;IBinder>&amp; \/*caller*\/)\n{\n    sp&lt;IBinder> context = getStrongProxyForHandle(0);\n\n    if (context) {\n        \/\/ The root object is special since we get it directly from the driver, it is never\n        \/\/ written by Parcell::writeStrongBinder.\n        internal::Stability::markCompilationUnit(context.get());\n    } else {\n        ALOGW(\"Not able to get context object on %s.\", mDriverName.c_str());\n    }\n    return context;\n}\n\nsp&lt;IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)\n{\n    sp&lt;IBinder> result;\n\n    AutoMutex _l(mLock);\n\n    handle_entry* e = lookupHandleLocked(handle);\n  \n    if (e != nullptr) {\n        IBinder* b = e->binder;\n        if (b == nullptr || !e->refs->attemptIncWeak(this)) {\n            if (handle == 0) {\n                .....\n                IPCThreadState* ipc = IPCThreadState::self();\n                CallRestriction originalCallRestriction = ipc->getCallRestriction();\n                ipc->setCallRestriction(CallRestriction::NONE);\n                Parcel data;\n                status_t status = ipc->transact(\n                        0, IBinder::PING_TRANSACTION, data, nullptr, 0);\n                ipc->setCallRestriction(originalCallRestriction);\n                if (status == DEAD_OBJECT)\n                   return nullptr;\n            }\n            sp&lt;BpBinder> b = BpBinder::create(handle);\n            e->binder = b.get();\n            if (b) e->refs = b->getWeakRefs();\n            result = b;\n        } else {\n            result.force_set(b);\n            e->refs->decWeak(this);\n        }\n    }\n    return result;\n}<\/code><\/pre>\n\n\n\n<p class=\"has-text-align-justify\">getContextObject\u4e2d\u901a\u8fc7getStrongProxyForHandle\u51fd\u6570\u6765\u83b7\u53d6IBinder\u7684sp\u5bf9\u8c61\uff0c\u800c\u5176\u5b9e\u9645\u4e0a\u662f\u901a\u8fc7BpBinder::create(handle)\u6765\u8fdb\u884c\u83b7\u53d6\u7684\uff0c\u4e0d\u8fc7\u5728\u8fd9\u4e00\u6b65\u5f97\u5230\u7684\u5bf9\u8c61\u662f\u6307\u5411BpBinder\u7684sp\uff0c\u6211\u4eec\u5c06\u4e0a\u9762\u7684\u4ee3\u7801\u8fdb\u884c\u7b80\u5316\uff0c\u53ef\u4ee5\u5f97\u5230\u5982\u4e0b\u7684\u5185\u5bb9\uff1a<\/p>\n\n\n\n<pre class=\"io-enlighter-pre\"><code class=\"gl\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-highlight=\"\">\/\/\u539f\u59cb\u5185\u5bb9\nsp&lt;AidlServiceManager> sm = interface_cast&lt;AidlServiceManager>(ProcessState::self()->getContextObject(nullptr))\n\n\/\/\u7b80\u5316\u5185\u5bb9\nsp&lt;android::os::IServiceManager> sm = interface_cast&lt;android::os::IServiceManager>(BpBinder::create(0))<\/code><\/pre>\n\n\n\n<p class=\"has-text-align-justify\">\u8fd9\u91cc\u901a\u8fc7interface_cast\u5c06BpBinder::create(0)\u8f6c\u6362\u4e3a\u4e86android::os::IServiceManager\uff0c\u5177\u4f53\u7684\u8f6c\u6362\u8fc7\u7a0b\u53ef\u4ee5\u770b\u770binterface_cast\u7684\u6e90\u7801\u5b9e\u73b0\uff0c\u5176\u672c\u8eab\u5c5e\u4e8e\u7c7b\u6a21\u677f\u51fd\u6570\uff0c\u6e90\u7801\u4f4d\u4e8eframeworks\/native\/libs\/binder\/include\/binder\/IInterface.h\u5185\uff0c\u5177\u4f53\u5185\u5bb9\u5982\u4e0b\uff1a<\/p>\n\n\n\n<pre class=\"io-enlighter-pre\"><code class=\"gl\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-highlight=\"\">\/**\n * If this is a local object and the descriptor matches, this will return the\n * actual local object which is implementing the interface. Otherwise, this will\n * return a proxy to the interface without checking the interface descriptor.\n * This means that subsequent calls may fail with BAD_TYPE.\n *\/\ntemplate&lt;typename INTERFACE>\ninline sp&lt;INTERFACE> interface_cast(const sp&lt;IBinder>&amp; obj)\n{\n    return INTERFACE::asInterface(obj);\n}<\/code><\/pre>\n\n\n\n<p>\u6211\u4eec\u5c06\u5176\u4e2d\u7684INTERFACE\u6a21\u677f\u505a\u76f8\u5e94\u66ff\u6362\u540e\uff0c\u53ef\u4ee5\u5f97\u5230\u5982\u4e0b\u5185\u5bb9\uff1a<\/p>\n\n\n\n<pre class=\"io-enlighter-pre\"><code class=\"gl\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-highlight=\"\">sp&lt;android::os::IServiceManager> sm = android::os::IServiceManager::asInterface(BpBinder::create(0))<\/code><\/pre>\n\n\n\n<p>\u6211\u4eec\u518d\u5173\u6ce8\u4e00\u4e0bIServiceManager\u7c7b\u7684\u5185\u90e8\uff0c\u53ef\u4ee5\u770b\u5230\u5982\u4e0b\u65b9\u6cd5\uff1a<\/p>\n\n\n\n<pre class=\"io-enlighter-pre\"><code class=\"gl\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-highlight=\"\">\/**\n* Retrieve an existing service, blocking for a few seconds\n* if it doesn't yet exist.\n*\/\nvirtual sp&lt;IBinder>         getService( const String16&amp; name) const = 0;\n\n\/**\n* Retrieve an existing service, non-blocking.\n*\/\nvirtual sp&lt;IBinder>         checkService( const String16&amp; name) const = 0;\n\n\/**\n* Register a service.\n*\/\n\/\/ NOLINTNEXTLINE(google-default-arguments)\nvirtual status_t addService(const String16&amp; name, const sp&lt;IBinder>&amp; service,\n                                bool allowIsolated = false,\n                                int dumpsysFlags = DUMP_FLAG_PRIORITY_DEFAULT) = 0;\n\n\/**\n* Return list of all existing services.\n*\/\n\/\/ NOLINTNEXTLINE(google-default-arguments)\nvirtual Vector&lt;String16> listServices(int dumpsysFlags = DUMP_FLAG_PRIORITY_ALL) = 0;\n\n\/**\n* Efficiently wait for a service.\n*\n* Returns nullptr only for permission problem or fatal error.\n*\/\nvirtual sp&lt;IBinder> waitForService(const String16&amp; name) = 0;\n<\/code><\/pre>\n\n\n\n<p class=\"has-text-align-justify\">\u8fd9\u90e8\u5206\u65b9\u6cd5\u90fd\u662f\u4e0e\u670d\u52a1\u83b7\u53d6\u3001\u4f7f\u7528\u76f8\u5173\u7684\uff0c\u4e14\u90fd\u5c5e\u4e8e\u7eaf\u865a\u51fd\u6570\uff0c\u5177\u4f53\u7684\u5b9e\u73b0\u903b\u8f91\u5728\u5176\u5b50\u7c7bServiceManagerShim\u5185\u3002<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">\u670d\u52a1\u6ce8\u518c<\/h4>\n\n\n\n<p class=\"has-text-align-justify\">\u63a5\u4e0b\u6765\u5c31\u662f\u670d\u52a1\u6ce8\u518c\u4e86\uff0c\u8fd9\u90e8\u5206\u53ef\u80fd\u4e5f\u662f\u6700\u8ba9\u4eba\u56f0\u60d1\u7684\u3002\u4ece\u6574\u4e2a\u4ee3\u7801\u8c03\u7528\u4e0a\u6765\u770b\uff0c\u670d\u52a1\u6ce8\u518c\u6700\u76f4\u89c2\u7684\u8c03\u7528\u5e94\u8be5\u5c5e\u4e8eCameraService::instantiate();\u8fd9\u4e00\u53e5\uff0c\u4f46\u5373\u4f7f\u6211\u4eec\u7ffb\u904dCameraService\u7684\u6e90\u7801\u4e5f\u662f\u65e0\u6cd5\u627e\u5230 instantiate\u7684\u5177\u4f53\u5b9e\u73b0\u7684\uff0c\u539f\u56e0\u5c31\u5728\u4e8e instantiate \u65b9\u6cd5\u5176\u5b9e\u662f\u6765\u81ea\u4e8e\u7236\u7c7b\u7684public\u65b9\u6cd5\uff0c\u6211\u4eec\u5728\u76f8\u5e94\u7684\u5934\u6587\u4ef6\u5185\u53ef\u4ee5\u770b\u5230\uff1a<\/p>\n\n\n\n<pre class=\"io-enlighter-pre\"><code class=\"gl\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-highlight=\"\">\/\/\nclass CameraService :\n    public BinderService&lt;CameraService>,\n    public virtual ::android::hardware::BnCameraService,\n    public virtual IBinder::DeathRecipient,\n    public virtual CameraProviderManager::StatusListener\n{\n  ......\n}<\/code><\/pre>\n\n\n\n<p>\u4e8b\u5b9e\u4e0ainstantiate\u65b9\u6cd5\u5c31\u662f\u7c7b\u6a21\u677fBinderService\u7684\u516c\u6709\u65b9\u6cd5 \uff0c\u5176\u4ee3\u7801\u5b9e\u73b0\u5982\u4e0b\uff1a<\/p>\n\n\n\n<pre class=\"io-enlighter-pre\"><code class=\"gl\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-highlight=\"\">\/\/frameworks\/native\/libs\/binder\/include\/binder\/BinderService.h\ntemplate&lt;typename SERVICE>\nclass BinderService\n{\npublic:\n    static status_t publish(bool allowIsolated = false,\n                            int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT) {\n        sp&lt;IServiceManager> sm(defaultServiceManager());\n        return sm->addService(String16(SERVICE::getServiceName()), new SERVICE(), allowIsolated,dumpFlags);\n    }\n\n    static void publishAndJoinThreadPool(\n            bool allowIsolated = false,\n            int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT) {\n        publish(allowIsolated, dumpFlags);\n        joinThreadPool();\n    }\n\n    static void instantiate() { publish(); }\n\n    static status_t shutdown() { return NO_ERROR; }\n\nprivate:\n    static void joinThreadPool() {\n        sp&lt;ProcessState> ps(ProcessState::self());\n        ps->startThreadPool();\n        ps->giveThreadPoolName();\n        IPCThreadState::self()->joinThreadPool();\n    }\n};\n<\/code><\/pre>\n\n\n\n<p class=\"has-text-align-justify\">\u8fd9\u91cc instantiate\u65b9\u6cd5\u5176\u5b9e\u5c31\u662f\u5c01\u88c5\u4e86\u4e00\u4e0bpublish\u65b9\u6cd5\uff0c\u5728publish\u65b9\u6cd5\u5185\uff0c\u901a\u8fc7IServiceManager\u7684addService\u65b9\u6cd5\u6dfb\u52a0\u670d\u52a1\uff0c\u8fd9\u91cc\u6709\u4e24\u4e2a\u5c0f\u7ec6\u8282\u662f\u9700\u8981\u6ce8\u610f\u7684\uff1a<strong>\u4e00\u4e2a\u662f\u6211\u4eec\u5b9e\u73b0\u7684\u670d\u52a1\u5fc5\u987b\u8981\u5b9e\u73b0getServiceName\u65b9\u6cd5\uff0c\u4e14\u8be5\u65b9\u6cd5\u5fc5\u987b\u4e3apublic static\u65b9\u6cd5\uff1b\u53e6\u4e00\u4e2a\u662f\u6211\u4eec\u5b9e\u73b0\u7684\u670d\u52a1\u5fc5\u987b\u8981\u663e\u5f0f\u5b9e\u73b0\u9ed8\u8ba4\u6784\u9020\u51fd\u6570<\/strong>\uff0c<strong>\u4e14\u5fc5\u987b\u4e3apublic\u53ef\u89c1<\/strong>\uff0c\u65b0\u624b\u4e00\u822c\u4f1a\u5728\u8fd9\u4e24\u4e2a\u5730\u65b9\u8e29\u5751\u3002\u5177\u4f53\u539f\u56e0\uff0c\u6211\u4eec\u53ea\u9700\u8981\u770b<strong>sm->addService(String16(SERVICE::getServiceName()), new SERVICE(), allowIsolated,dumpFlags);<\/strong>\u8fd9\u4e00\u884c\u5c31\u77e5\u9053\u4e3a\u4ec0\u4e48\u4e86\u3002<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">\u542f\u52a8\u4e0e\u52a0\u5165Binder\u7ebf\u7a0b\u6c60<\/h4>\n\n\n\n<p class=\"has-text-align-justify\">\u5728\u5c06\u670d\u52a1\u6ce8\u518c\u5230ServiceManager\u4e4b\u540e\uff0c\u6211\u4eec\u9700\u8981\u542f\u52a8\u670d\u52a1\u4fa7\u7684binder\u7ebf\u7a0b\u6c60\uff0c\u7531\u4e8e\u662f\u591aClient\u5e76\u53d1\u8bf7\u6c42\u7684\uff0c\u7ebf\u7a0b\u6c60\u662f\u5fc5\u987b\u7684\uff0c<\/p>\n\n\n\n<pre class=\"io-enlighter-pre\"><code class=\"gl\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-highlight=\"\">\/\/ \u542f\u52a8binder\u7ebf\u7a0b\u6c60\nProcessState::self()->startThreadPool();\n\/\/ \u52a0\u5165\u5230Binder\u7ebf\u7a0b\u6c60\nIPCThreadState::self()->joinThreadPool();<\/code><\/pre>\n\n\n\n<p>\u9996\u5148\u770b\u770bstartThreadPool\u5185\u7684\u5b9e\u73b0\uff1a<\/p>\n\n\n\n<pre class=\"io-enlighter-pre\"><code class=\"gl\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-highlight=\"\">\/\/frameworks\/native\/libs\/binder\/ProcessState.cpp\nvoid ProcessState::startThreadPool()\n{\n    AutoMutex _l(mLock);\n    if (!mThreadPoolStarted) {\n        mThreadPoolStarted = true;\n        \/\/\u901a\u8fc7true\u8bbe\u5b9a\u4e3a\u4e3b\u7ebf\u7a0b\n        spawnPooledThread(true);\n    }\n}\n\nvoid ProcessState::spawnPooledThread(bool isMain)\n{\n    if (mThreadPoolStarted) {\n        String8 name = makeBinderThreadName();\n        ALOGV(\"Spawning new pooled thread, name=%s\\n\", name.string());\n        sp&lt;Thread> t = sp&lt;PoolThread>::make(isMain);\n        t->run(name.string());\n    }\n}\n\n\/\/PoolThread\u4e3a\u5185\u90e8\u7c7b\uff0c\u7ee7\u627f\u81eaAndroid\u81ea\u884c\u5c01\u88c5\u7684Thread\u7c7b\nclass PoolThread : public Thread\n{\npublic:\n    explicit PoolThread(bool isMain)\n        : mIsMain(isMain)\n    {\n    }\n\nprotected:\n    virtual bool threadLoop()\n    {\n        IPCThreadState::self()->joinThreadPool(mIsMain);\n        return false;\n    }\n\n    const bool mIsMain;\n};\n<\/code><\/pre>\n\n\n\n<p class=\"has-text-align-justify\">\u4ece\u5176\u5b9e\u73b0\u6211\u4eec\u53ef\u4ee5\u770b\u5230\uff0cstartThreadPool\u8c03\u7528\u5c31\u4f1a\u5f00\u542f\u7ebf\u7a0b\u6267\u884c\uff0c\u5e76\u4e14\u662f\u4f5c\u4e3a\u4e3b\u7ebf\u7a0b\u6267\u884c\uff1b\u63a5\u7740\u6211\u4eec\u770b\u770bjoinThreadPool\u7684\u5b9e\u73b0\uff1a<\/p>\n\n\n\n<pre class=\"io-enlighter-pre\"><code class=\"gl\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-highlight=\"\">\/\/frameworks\/native\/libs\/binder\/IPCThreadState.cpp\nvoid IPCThreadState::joinThreadPool(bool isMain)\n{\n    LOG_THREADPOOL(\"**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\\n\", (void*)pthread_self(), getpid());\n    \/\/\u8bbe\u5b9a\u7ebf\u7a0b\u72b6\u6001,mOut\u5c5e\u4e8eParcel\u5bf9\u8c61\n    mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);\n\n    mIsLooper = true;\n    status_t result;\n    do {\n        processPendingDerefs();\n        \/\/ now get the next command to be processed, waiting if necessary\n        result = getAndExecuteCommand();\n\n        if (result &lt; NO_ERROR &amp;&amp; result != TIMED_OUT &amp;&amp; result != -ECONNREFUSED &amp;&amp; result != -EBADF) {\n            LOG_ALWAYS_FATAL(\"getAndExecuteCommand(fd=%d) returned unexpected error %d, aborting\",\n                  mProcess->mDriverFD, result);\n        }\n\n        \/\/ Let this thread exit the thread pool if it is no longer\n        \/\/ needed and it is not the main process thread.\n        if(result == TIMED_OUT &amp;&amp; !isMain) {\n            break;\n        }\n    } while (result != -ECONNREFUSED &amp;&amp; result != -EBADF);\n\n    LOG_THREADPOOL(\"**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%d\\n\",\n        (void*)pthread_self(), getpid(), result);\n\n    mOut.writeInt32(BC_EXIT_LOOPER);\n    mIsLooper = false;\n    talkWithDriver(false);\n}\n\n\/\/\u4ecebinder\u4e2d\u83b7\u53d6command\u5e76\u6267\u884c\nstatus_t IPCThreadState::getAndExecuteCommand()\n{\n    status_t result;\n    int32_t cmd;\n\n    result = talkWithDriver();\n    if (result >= NO_ERROR) {\n        \/\/mIn\u5c5e\u4e8eParcel\u5bf9\u8c61\uff0c\u4ece\u4e2d\u83b7\u53d6binder driver\u4f20\u9012\u7684command\n        size_t IN = mIn.dataAvail();\n        if (IN &lt; sizeof(int32_t)) return result;\n        cmd = mIn.readInt32();\n        IF_LOG_COMMANDS() {\n            alog &lt;&lt; \"Processing top-level Command: \"\n                 &lt;&lt; getReturnString(cmd) &lt;&lt; endl;\n        }\n\n        pthread_mutex_lock(&amp;mProcess->mThreadCountLock);\n        mProcess->mExecutingThreadsCount++;\n        if (mProcess->mExecutingThreadsCount >= mProcess->mMaxThreads &amp;&amp;\n                mProcess->mStarvationStartTimeMs == 0) {\n            mProcess->mStarvationStartTimeMs = uptimeMillis();\n        }\n        pthread_mutex_unlock(&amp;mProcess->mThreadCountLock);\n\n        result = executeCommand(cmd);\n\n        pthread_mutex_lock(&amp;mProcess->mThreadCountLock);\n        mProcess->mExecutingThreadsCount--;\n        if (mProcess->mExecutingThreadsCount &lt; mProcess->mMaxThreads &amp;&amp;\n                mProcess->mStarvationStartTimeMs != 0) {\n            int64_t starvationTimeMs = uptimeMillis() - mProcess->mStarvationStartTimeMs;\n            if (starvationTimeMs > 100) {\n                ALOGE(\"binder thread pool (%zu threads) starved for %\" PRId64 \" ms\",\n                      mProcess->mMaxThreads, starvationTimeMs);\n            }\n            mProcess->mStarvationStartTimeMs = 0;\n        }\n\n        \/\/ Cond broadcast can be expensive, so don't send it every time a binder\n        \/\/ call is processed. b\/168806193\n        if (mProcess->mWaitingForThreads > 0) {\n            pthread_cond_broadcast(&amp;mProcess->mThreadCountDecrement);\n        }\n        pthread_mutex_unlock(&amp;mProcess->mThreadCountLock);\n    }\n    return result;\n}\n<\/code><\/pre>\n\n\n\n<p class=\"has-text-align-justify\">\u6574\u4e2a\u5b9e\u73b0\u8fc7\u7a0b\u4e2d\u6700\u4e3a\u5173\u952e\u7684\u51fd\u6570\u4e3aexecuteCommand\uff0c\u5176\u5185\u90e8\u5b9e\u73b0\u7b80\u5355\u6765\u8bb2\u5c31\u662f\u6839\u636emIn\u4f20\u9012\u7684binder\u9a71\u52a8\u6307\u4ee4\uff08BR_XXX\uff09\u6267\u884c\u76f8\u5173\u52a8\u4f5c\uff0c\u518d\u5c06\u6267\u884c\u7ed3\u679c\uff08BC_XXX\uff09\u901a\u8fc7mOut\u8fd4\u56de\u81f3binder\u9a71\u52a8\u3002<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">\u603b\u7ed3<\/h4>\n\n\n\n<p class=\"has-text-align-justify\">\u8fd9\u91cc\u6211\u4eec\u518d\u7b80\u5355\u603b\u7ed3\u4e00\u4e0b\u5982\u4f55\u5b9e\u73b0\u4e00\u4e2a\u5b89\u5353Native Service\uff1a<\/p>\n\n\n\n<p>1.\u5728\u5934\u6587\u4ef6\u4e2dinclude\u4ee5\u4e0b\u5934\u6587\u4ef6\uff1a<\/p>\n\n\n\n<pre class=\"io-enlighter-pre\"><code class=\"gl\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-highlight=\"\">#include &lt;binder\/IPCThreadState.h>\n#include &lt;binder\/IServiceManager.h>\n#include &lt;binder\/BinderService.h><\/code><\/pre>\n\n\n\n<p class=\"has-text-align-justify\">\u58f0\u660eMyOwnService\u7c7b,\u5e76\u4f7f\u5176\u7ee7\u627fpublic BinderService&lt;MyOwnService>\uff0c\u5e76\u5c06BinderService&lt;MyOwnService>\u8bbe\u7f6e\u4e3a\u53cb\u5143\u7c7b\uff1b\u5e76\u5b9e\u73b0\u5982\u4e0b\u51fd\u6570\uff1a<\/p>\n\n\n\n<pre class=\"io-enlighter-pre\"><code class=\"gl\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-highlight=\"\">MyOwnService();\/\/\u9ed8\u8ba4\u6784\u9020\u51fd\u6570\nstatic char const* getServiceName();  \/\/\u7528\u4e8e\u5b9a\u4e49service\u540d\u79f0<\/code><\/pre>\n\n\n\n<p class=\"has-text-align-justify\">\u5982\u679c\u6211\u4eec\u9700\u8981\u5728MyOwnService\u4e2d\u5b9e\u73b0AIDL\u63a5\u53e3\uff0c\u5982IMyOwnService.aidl\uff0c\u9700\u8981\u7ee7\u627fBnMyOwnService\u7c7b\uff0c\u5e76\u8986\u5199 IMyOwnService.aidl\u4e2d\u6240\u5b9a\u4e49\u7684\u63a5\u53e3\u3002<\/p>\n\n\n\n<p class=\"has-text-align-justify\">\u5982\u679c\u9700\u8981\u5904\u7406BinderDied\u8fd9\u79cd\u5f02\u5e38\u60c5\u51b5\uff0c\u5219\u9700\u8981\u7ee7\u627fIBinder::DeathRecipient\uff0c\u8986\u5199binderDied\u63a5\u53e3\u3002\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n\n\n\n<pre class=\"io-enlighter-pre\"><code class=\"gl\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-highlight=\"\">\/\/for service register\n#include &lt;binder\/IPCThreadState.h>\n#include &lt;binder\/IServiceManager.h>\n#include &lt;binder\/BinderService.h>\n\nclass MyOwnService \uff1apublic BinderService&lt;MyOwnService>,public BnMyOwnService,public \n                     virtual IBinder::DeathRecipient{\npublic:\n      MyOwnService();\n      static char const* getServiceName();\n      .....\nprivate:\n      void binderDied(const wp&lt;IBinder>&amp; who) override;\n      .....\n}<\/code><\/pre>\n\n\n\n<p>2.Main\u51fd\u6570\u7f16\u5199\uff1a<\/p>\n\n\n\n<pre class=\"io-enlighter-pre\"><code class=\"gl\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-highlight=\"\">void main\uff08int argc,char * argv[]\uff09\n{\n   configureRpcThreadpool(3, false \/* callerWillJoin *\/);\n   sp&lt;ProcessState> proc(ProcessState::self());\n   sp&lt;IServiceManager> sm = defaultServiceManager();\n   myOwnService::instantiate();\n\n\/\/\u8fd9\u4e24\u53e5\u4e00\u5b9a\u653e\u5728\u670d\u52a1\u51c6\u5907\u5de5\u4f5c\u5b8c\u6210\u4e4b\u540e\uff0c\u5426\u5219\u540e\u7eed\u7684\u8bed\u53e5\u4e0d\u4f1a\u6267\u884c\n   ProcessState::self()->startThreadPool();\n   IPCThreadState::self()->joinThreadPool();\n}<\/code><\/pre>\n\n\n\n<p>\u81f3\u6b64\u670d\u52a1\u5c31\u80fd\u6ce8\u518c\u5230ServiceManager\u4e2d\uff0c\u5e76\u53ef\u8fdb\u884cbinder\u901a\u4fe1\u4e86\u3002<\/p>\n\n\n\n<p>\u5982\u679c\u6211\u4eec\u8981\u5728\u8fdc\u7aef\u83b7\u53d6service\uff0c\u5728Java\u5c42\u53ef\u4ee5\u91c7\u7528\u5982\u4e0b\u65b9\u5f0f\uff1a<\/p>\n\n\n\n<pre class=\"io-enlighter-pre\"><code class=\"gl\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-highlight=\"\">import android.os.IBinder;\nimport android.os.ServiceManager;\n\n\/\/\u5b57\u7b26\u4e32\u5fc5\u987b\u4e0eservice\u5b9e\u73b0\u7684getServiceName\u63a5\u53e3\u6240\u8fd4\u56de\u7684\u4e00\u81f4\nIBinder binder = ServiceManager.getService(\"myOwnService\");\n\/\/\u5c06IBinder\u901a\u8fc7asInterface\u8f6c\u6362\u4e3a\u5177\u4f53\u7684service\u8fdc\u7aef\u5bf9\u8c61\nmMyOwnService = IMyOwnService.Stub.asInterface(binder);<\/code><\/pre>\n\n\n\n<p>\u5173\u4e8eIMyOwnService.Stub.asInterface\u63a5\u53e3\u7684\u7531\u6765\uff0c\u53ef\u4ee5\u53c2\u8003\u6211\u7684\u53e6\u4e00\u7bc7<a href=\"https:\/\/blog.coderfan.org\/en\/android-aidl-usage.html\/\" class=\"ek-link\">\u6587\u7ae0<\/a>\u3002<\/p>\n        <div class=\"booster-block booster-reactions-block\">\n            <div class=\"twp-reactions-icons\">\n                \n                <div class=\"twp-reacts-wrap\">\n                    <a react-data=\"be-react-1\" post-id=\"4898\" class=\"be-face-icons un-reacted\" href=\"javascript:void(0)\">\n                        <img decoding=\"async\" src=\"https:\/\/blog.coderfan.org\/wp-content\/plugins\/booster-extension\/\/assets\/icon\/happy.svg\" alt=\"Happy\">\n                    <\/a>\n                    <div class=\"twp-reaction-title\">\n                        Happy                    <\/div>\n                    <div class=\"twp-count-percent\">\n                                                    <span style=\"display: none;\" class=\"twp-react-count\">1<\/span>\n                        \n                                                <span class=\"twp-react-percent\"><span>100<\/span> %<\/span>\n                                            <\/div>\n                <\/div>\n\n                <div class=\"twp-reacts-wrap\">\n                    <a react-data=\"be-react-2\" post-id=\"4898\" class=\"be-face-icons un-reacted\" href=\"javascript:void(0)\">\n                        <img decoding=\"async\" src=\"https:\/\/blog.coderfan.org\/wp-content\/plugins\/booster-extension\/\/assets\/icon\/sad.svg\" alt=\"Sad\">\n                    <\/a>\n                    <div class=\"twp-reaction-title\">\n                        Sad                    <\/div>\n                    <div class=\"twp-count-percent\">\n                                                    <span style=\"display: none;\" class=\"twp-react-count\">0<\/span>\n                                                                        <span class=\"twp-react-percent\"><span>0<\/span> %<\/span>\n                                            <\/div>\n                <\/div>\n\n                <div class=\"twp-reacts-wrap\">\n                    <a react-data=\"be-react-3\" post-id=\"4898\" class=\"be-face-icons un-reacted\" href=\"javascript:void(0)\">\n                        <img decoding=\"async\" src=\"https:\/\/blog.coderfan.org\/wp-content\/plugins\/booster-extension\/\/assets\/icon\/excited.svg\" alt=\"Excited\">\n                    <\/a>\n                    <div class=\"twp-reaction-title\">\n                        Excited                    <\/div>\n                    <div class=\"twp-count-percent\">\n                                                    <span style=\"display: none;\" class=\"twp-react-count\">0<\/span>\n                                                                        <span class=\"twp-react-percent\"><span>0<\/span> %<\/span>\n                                            <\/div>\n                <\/div>\n\n                <div class=\"twp-reacts-wrap\">\n                    <a react-data=\"be-react-6\" post-id=\"4898\" class=\"be-face-icons un-reacted\" href=\"javascript:void(0)\">\n                        <img decoding=\"async\" src=\"https:\/\/blog.coderfan.org\/wp-content\/plugins\/booster-extension\/\/assets\/icon\/sleepy.svg\" alt=\"Sleepy\">\n                    <\/a>\n                    <div class=\"twp-reaction-title\">\n                        Sleepy                    <\/div>\n                    <div class=\"twp-count-percent\">\n                                                    <span style=\"display: none;\" class=\"twp-react-count\">0<\/span>\n                        \n                                                <span class=\"twp-react-percent\"><span>0<\/span> %<\/span>\n                                            <\/div>\n                <\/div>\n\n                <div class=\"twp-reacts-wrap\">\n                    <a react-data=\"be-react-4\" post-id=\"4898\" class=\"be-face-icons un-reacted\" href=\"javascript:void(0)\">\n                        <img decoding=\"async\" src=\"https:\/\/blog.coderfan.org\/wp-content\/plugins\/booster-extension\/\/assets\/icon\/angry.svg\" alt=\"Angry\">\n                    <\/a>\n                    <div class=\"twp-reaction-title\">Angry<\/div>\n                    <div class=\"twp-count-percent\">\n                                                    <span style=\"display: none;\" class=\"twp-react-count\">0<\/span>\n                                                                        <span class=\"twp-react-percent\"><span>0<\/span> %<\/span>\n                        \n                    <\/div>\n                <\/div>\n\n                <div class=\"twp-reacts-wrap\">\n                    <a react-data=\"be-react-5\" post-id=\"4898\" class=\"be-face-icons un-reacted\" href=\"javascript:void(0)\">\n                        <img decoding=\"async\" src=\"https:\/\/blog.coderfan.org\/wp-content\/plugins\/booster-extension\/\/assets\/icon\/surprise.svg\" alt=\"Surprise\">\n                    <\/a>\n                    <div class=\"twp-reaction-title\">Surprise<\/div>\n                    <div class=\"twp-count-percent\">\n                                                    <span style=\"display: none;\" class=\"twp-react-count\">0<\/span>\n                                                                        <span class=\"twp-react-percent\"><span>0<\/span> %<\/span>\n                                            <\/div>\n                <\/div>\n\n            <\/div>\n        <\/div>","protected":false},"excerpt":{"rendered":"<p>\u5173\u4e8eAndroid Native\u670d\u52a1\u7684\u5b9e\u73b0<\/p>","protected":false},"author":1,"featured_media":4483,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_editorskit_title_hidden":false,"_editorskit_reading_time":4,"_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[18],"tags":[71,46],"class_list":["post-4898","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-operating-system","tag-aidl","tag-android"],"blocksy_meta":[],"aioseo_notices":[],"featured_image_urls":{"full":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android.jpg",512,269,false],"thumbnail":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android-150x150.jpg",150,150,true],"medium":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android-300x158.jpg",300,158,true],"medium_large":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android.jpg",512,269,false],"large":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android.jpg",512,269,false],"1536x1536":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android.jpg",512,269,false],"2048x2048":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android.jpg",512,269,false],"trp-custom-language-flag":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android-18x9.jpg",18,9,true]},"post_excerpt_stackable":"<p>\u5173\u4e8eAndroid Native\u670d\u52a1\u7684\u5b9e\u73b0<\/p>\n","category_list":"<a href=\"https:\/\/blog.coderfan.org\/en\/category\/operating-system\" rel=\"category tag\">\u64cd\u4f5c\u7cfb\u7edf<\/a>","author_info":{"name":"FranzKafka95","url":"https:\/\/blog.coderfan.org\/en\/author\/yushenglonely95"},"comments_num":"0 comments","featured_image_urls_v2":{"full":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android.jpg",512,269,false],"thumbnail":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android-150x150.jpg",150,150,true],"medium":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android-300x158.jpg",300,158,true],"medium_large":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android.jpg",512,269,false],"large":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android.jpg",512,269,false],"1536x1536":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android.jpg",512,269,false],"2048x2048":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android.jpg",512,269,false],"trp-custom-language-flag":["https:\/\/blog.coderfan.org\/wp-content\/uploads\/2023\/04\/android-18x9.jpg",18,9,true]},"post_excerpt_stackable_v2":"<p>\u5173\u4e8eAndroid Native\u670d\u52a1\u7684\u5b9e\u73b0<\/p>\n","category_list_v2":"<a href=\"https:\/\/blog.coderfan.org\/en\/category\/operating-system\" rel=\"category tag\">\u64cd\u4f5c\u7cfb\u7edf<\/a>","author_info_v2":{"name":"FranzKafka95","url":"https:\/\/blog.coderfan.org\/en\/author\/yushenglonely95"},"comments_num_v2":"0 comments","_links":{"self":[{"href":"https:\/\/blog.coderfan.org\/en\/wp-json\/wp\/v2\/posts\/4898","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.coderfan.org\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.coderfan.org\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.coderfan.org\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.coderfan.org\/en\/wp-json\/wp\/v2\/comments?post=4898"}],"version-history":[{"count":23,"href":"https:\/\/blog.coderfan.org\/en\/wp-json\/wp\/v2\/posts\/4898\/revisions"}],"predecessor-version":[{"id":4938,"href":"https:\/\/blog.coderfan.org\/en\/wp-json\/wp\/v2\/posts\/4898\/revisions\/4938"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.coderfan.org\/en\/wp-json\/wp\/v2\/media\/4483"}],"wp:attachment":[{"href":"https:\/\/blog.coderfan.org\/en\/wp-json\/wp\/v2\/media?parent=4898"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.coderfan.org\/en\/wp-json\/wp\/v2\/categories?post=4898"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.coderfan.org\/en\/wp-json\/wp\/v2\/tags?post=4898"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}