之前几篇博客我们介绍了传统Binder的使用方法,包括c层和java层,这篇博客我们主要介绍下ProcessState和IPCThreadState类的相关方法。

一、正常demon binder调用流程

在一个传统的demon中,如果我们要使用Binder通信,代码大致如下:

[cpp] view plaincopy
  1. int main(int argc, char** argv)
  2. {
  3. sp<ProcessState> proc(ProcessState::self());
  4. MediaPlayerService::instantiate();
  5. ProcessState::self()->startThreadPool();
  6. IPCThreadState::self()->joinThreadPool();
  7. }

1.1 ProcessState::self函数

上面先调用了ProcessState的self方法,

[cpp] view plaincopy
  1. sp<ProcessState> ProcessState::self()
  2. {
  3. Mutex::Autolock _l(gProcessMutex);
  4. if (gProcess != NULL) {
  5. return gProcess;
  6. }
  7. gProcess = new ProcessState;
  8. return gProcess;
  9. }

典型的单例模式,我们先来看看ProcessState的构造函数

[cpp] view plaincopy
  1. ProcessState::ProcessState()
  2. : mDriverFD(open_driver())
  3. , mVMStart(MAP_FAILED)
  4. , mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
  5. , mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
  6. , mExecutingThreadsCount(0)
  7. , mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
  8. , mManagesContexts(false)
  9. , mBinderContextCheckFunc(NULL)
  10. , mBinderContextUserData(NULL)
  11. , mThreadPoolStarted(false)
  12. , mThreadPoolSeq(1)
  13. {
  14. if (mDriverFD >= 0) {
  15. // XXX Ideally, there should be a specific define for whether we
  16. // have mmap (or whether we could possibly have the kernel module
  17. // availabla).
  18. #if !defined(HAVE_WIN32_IPC)
  19. // mmap the binder, providing a chunk of virtual address space to receive transactions.
  20. mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
  21. if (mVMStart == MAP_FAILED) {
  22. // *sigh*
  23. ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
  24. close(mDriverFD);
  25. mDriverFD = -1;
  26. }
  27. #else
  28. mDriverFD = -1;
  29. #endif
  30. }
  31. LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened.  Terminating.");
  32. }

我们在赋值的时候有如下函数 mDriverFD(open_driver())

[cpp] view plaincopy
  1. static int open_driver()
  2. {
  3. int fd = open("/dev/binder", O_RDWR);//打开binder驱动
  4. if (fd >= 0) {
  5. fcntl(fd, F_SETFD, FD_CLOEXEC);
  6. int vers = 0;
  7. status_t result = ioctl(fd, BINDER_VERSION, &vers);
  8. if (result == -1) {
  9. ALOGE("Binder ioctl to obtain version failed: %s", strerror(errno));
  10. close(fd);
  11. fd = -1;
  12. }
  13. if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
  14. ALOGE("Binder driver protocol does not match user space protocol!");
  15. close(fd);
  16. fd = -1;
  17. }
  18. size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;//设置binder线程数,默认15
  19. result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
  20. if (result == -1) {
  21. ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
  22. }
  23. } else {
  24. ALOGW("Opening '/dev/binder' failed: %s\n", strerror(errno));
  25. }
  26. return fd;
  27. }

上面这个函数打开了binder驱动节点,然后设置了binder线程数量。binder驱动打开的fd保存在mDriverFD 中。
具体关闭Binder中线程的问题,可以参考Binder通信过程中的用户空间线程池的管理 这篇博文。

上面函数中还通过mmap来把设备文件/dev/binder映射到内存中。

1.2 instantiate函数

而instantiate函数一般是将service注册到serviceManager中去。

[cpp] view plaincopy
  1. void MediaPlayerService::instantiate() {
  2. defaultServiceManager()->addService(
  3. String16("media.player"), new MediaPlayerService());
  4. }

1.3 ProcessState::startThreadPool函数

我们再来看startThreadPool函数

[cpp] view plaincopy
  1. void ProcessState::startThreadPool()
  2. {
  3. AutoMutex _l(mLock);
  4. if (!mThreadPoolStarted) {
  5. mThreadPoolStarted = true;
  6. spawnPooledThread(true);
  7. }
  8. }

而在spawnPooledThread函数中,先建了一个线程PoolThread

[cpp] view plaincopy
  1. void ProcessState::spawnPooledThread(bool isMain)
  2. {
  3. if (mThreadPoolStarted) {
  4. String8 name = makeBinderThreadName();
  5. ALOGV("Spawning new pooled thread, name=%s\n", name.string());
  6. sp<Thread> t = new PoolThread(isMain);
  7. t->run(name.string());
  8. }
  9. }

我们看下这个PoolThread线程,最后还是调用了IPCThreadState::self()->joinThreadPool(mIsMain);

[cpp] view plaincopy
  1. class PoolThread : public Thread
  2. {
  3. public:
  4. PoolThread(bool isMain)
  5. : mIsMain(isMain)
  6. {
  7. }
  8. protected:
  9. virtual bool threadLoop()
  10. {
  11. IPCThreadState::self()->joinThreadPool(mIsMain);
  12. return false;
  13. }
  14. const bool mIsMain;
  15. };

我们再来看看IPCThreadState的joinThreadPool函数,先看看其定义,参数默认是true。

[cpp] view plaincopy
  1. void                joinThreadPool(bool isMain = true);

也就是在main函数中
     ProcessState::self()->startThreadPool();  
     IPCThreadState::self()->joinThreadPool();

这两个函数都是调用了joinThreadPool函数且参数都是true,只是上面的函数新建了一个thread。

1.4 IPCThreadState::joinThreadPool函数

我们再来看看这个函数 joinThreadPool:

[cpp] view plaincopy
  1. void IPCThreadState::joinThreadPool(bool isMain)
  2. {
  3. LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid());
  4. mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
  5. // This thread may have been spawned by a thread that was in the background
  6. // scheduling group, so first we will make sure it is in the foreground
  7. // one to avoid performing an initial transaction in the background.
  8. set_sched_policy(mMyThreadId, SP_FOREGROUND);
  9. status_t result;
  10. do {
  11. processPendingDerefs();
  12. // now get the next command to be processed, waiting if necessary
  13. result = getAndExecuteCommand();
  14. if (result < NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) {
  15. ALOGE("getAndExecuteCommand(fd=%d) returned unexpected error %d, aborting",
  16. mProcess->mDriverFD, result);
  17. abort();
  18. }
  19. // Let this thread exit the thread pool if it is no longer
  20. // needed and it is not the main process thread.
  21. if(result == TIMED_OUT && !isMain) {
  22. break;
  23. }
  24. } while (result != -ECONNREFUSED && result != -EBADF);
  25. LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%p\n",
  26. (void*)pthread_self(), getpid(), (void*)result);
  27. mOut.writeInt32(BC_EXIT_LOOPER);
  28. talkWithDriver(false);
  29. }

这个函数就是一个死循环,不断从驱动获取数据,我们来看getAndExecuteCommand函数:

[cpp] view plaincopy
  1. status_t IPCThreadState::getAndExecuteCommand()
  2. {
  3. status_t result;
  4. int32_t cmd;
  5. result = talkWithDriver();//获取binder驱动数据
  6. if (result >= NO_ERROR) {
  7. size_t IN = mIn.dataAvail();
  8. if (IN < sizeof(int32_t)) return result;
  9. cmd = mIn.readInt32();
  10. IF_LOG_COMMANDS() {
  11. alog << "Processing top-level Command: "
  12. << getReturnString(cmd) << endl;
  13. }
  14. pthread_mutex_lock(&mProcess->mThreadCountLock);
  15. mProcess->mExecutingThreadsCount++;
  16. pthread_mutex_unlock(&mProcess->mThreadCountLock);
  17. result = executeCommand(cmd);
  18. pthread_mutex_lock(&mProcess->mThreadCountLock);
  19. mProcess->mExecutingThreadsCount--;
  20. pthread_cond_broadcast(&mProcess->mThreadCountDecrement);
  21. pthread_mutex_unlock(&mProcess->mThreadCountLock);
  22. // After executing the command, ensure that the thread is returned to the
  23. // foreground cgroup before rejoining the pool.  The driver takes care of
  24. // restoring the priority, but doesn't do anything with cgroups so we
  25. // need to take care of that here in userspace.  Note that we do make
  26. // sure to go in the foreground after executing a transaction, but
  27. // there are other callbacks into user code that could have changed
  28. // our group so we want to make absolutely sure it is put back.
  29. set_sched_policy(mMyThreadId, SP_FOREGROUND);
  30. }
  31. return result;
  32. }

getAndExecuteCommand函数中先调用talkWithDriver就是从binder驱动获取数据,然后调用executeCommand执行命令

[cpp] view plaincopy
  1. status_t IPCThreadState::executeCommand(int32_t cmd)
  2. {
  3. BBinder* obj;
  4. RefBase::weakref_type* refs;
  5. status_t result = NO_ERROR;
  6. switch ((uint32_t)cmd) {
  7. case BR_ERROR:
  8. result = mIn.readInt32();
  9. break;
  10. case BR_OK:
  11. break;
  12. ......
  13. case BR_TRANSACTION:
  14. {
  15. binder_transaction_data tr;
  16. result = mIn.read(&tr, sizeof(tr));
  17. ALOG_ASSERT(result == NO_ERROR,
  18. "Not enough command data for brTRANSACTION");
  19. if (result != NO_ERROR) break;
  20. Parcel buffer;
  21. buffer.ipcSetDataReference(
  22. reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
  23. tr.data_size,
  24. reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
  25. tr.offsets_size/sizeof(binder_size_t), freeBuffer, this);
  26. const pid_t origPid = mCallingPid;
  27. const uid_t origUid = mCallingUid;
  28. const int32_t origStrictModePolicy = mStrictModePolicy;
  29. const int32_t origTransactionBinderFlags = mLastTransactionBinderFlags;
  30. mCallingPid = tr.sender_pid;
  31. mCallingUid = tr.sender_euid;
  32. mLastTransactionBinderFlags = tr.flags;
  33. int curPrio = getpriority(PRIO_PROCESS, mMyThreadId);
  34. if (gDisableBackgroundScheduling) {
  35. if (curPrio > ANDROID_PRIORITY_NORMAL) {
  36. // We have inherited a reduced priority from the caller, but do not
  37. // want to run in that state in this process.  The driver set our
  38. // priority already (though not our scheduling class), so bounce
  39. // it back to the default before invoking the transaction.
  40. setpriority(PRIO_PROCESS, mMyThreadId, ANDROID_PRIORITY_NORMAL);
  41. }
  42. } else {
  43. if (curPrio >= ANDROID_PRIORITY_BACKGROUND) {
  44. // We want to use the inherited priority from the caller.
  45. // Ensure this thread is in the background scheduling class,
  46. // since the driver won't modify scheduling classes for us.
  47. // The scheduling group is reset to default by the caller
  48. // once this method returns after the transaction is complete.
  49. set_sched_policy(mMyThreadId, SP_BACKGROUND);
  50. }
  51. }
  52. //ALOGI(">>>> TRANSACT from pid %d uid %d\n", mCallingPid, mCallingUid);
  53. Parcel reply;
  54. status_t error;
  55. IF_LOG_TRANSACTIONS() {
  56. TextOutput::Bundle _b(alog);
  57. alog << "BR_TRANSACTION thr " << (void*)pthread_self()
  58. << " / obj " << tr.target.ptr << " / code "
  59. << TypeCode(tr.code) << ": " << indent << buffer
  60. << dedent << endl
  61. << "Data addr = "
  62. << reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer)
  63. << ", offsets addr="
  64. << reinterpret_cast<const size_t*>(tr.data.ptr.offsets) << endl;
  65. }
  66. if (tr.target.ptr) {
  67. sp<BBinder> b((BBinder*)tr.cookie);
  68. error = b->transact(tr.code, buffer, &reply, tr.flags);//这个最终到service的onTransact函数
  69. } else {
  70. error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);
  71. }
  72. //ALOGI("<<<< TRANSACT from pid %d restore pid %d uid %d\n",
  73. //     mCallingPid, origPid, origUid);
  74. if ((tr.flags & TF_ONE_WAY) == 0) {
  75. LOG_ONEWAY("Sending reply to %d!", mCallingPid);
  76. if (error < NO_ERROR) reply.setError(error);
  77. sendReply(reply, 0);
  78. } else {
  79. LOG_ONEWAY("NOT sending reply to %d!", mCallingPid);
  80. }
  81. mCallingPid = origPid;
  82. mCallingUid = origUid;
  83. mStrictModePolicy = origStrictModePolicy;
  84. mLastTransactionBinderFlags = origTransactionBinderFlags;
  85. IF_LOG_TRANSACTIONS() {
  86. TextOutput::Bundle _b(alog);
  87. alog << "BC_REPLY thr " << (void*)pthread_self() << " / obj "
  88. << tr.target.ptr << ": " << indent << reply << dedent << endl;
  89. }
  90. }
  91. break;
  92. ......
  93. }

上面就是处理各种命令,最后BR_TRANSACTION命令的时候会调用BBinder的transact,最后调用service中的onTransact函数。

[cpp] view plaincopy
  1. status_t BBinder::transact(
  2. uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
  3. {
  4. data.setDataPosition(0);
  5. status_t err = NO_ERROR;
  6. switch (code) {
  7. case PING_TRANSACTION:
  8. reply->writeInt32(pingBinder());
  9. break;
  10. default:
  11. err = onTransact(code, data, reply, flags);//调用onTransact函数
  12. break;
  13. }
  14. if (reply != NULL) {
  15. reply->setDataPosition(0);
  16. }
  17. return err;
  18. }

二、不使用Binder线程

还记得在healthd中,我们没有使用Binder线程,我们看看代码是怎么写的。

[cpp] view plaincopy
  1. static void binder_event(uint32_t /*epevents*/) {
  2. IPCThreadState::self()->handlePolledCommands();
  3. }
  4. void healthd_mode_android_init(struct healthd_config* /*config*/) {
  5. ProcessState::self()->setThreadPoolMaxThreadCount(0);
  6. IPCThreadState::self()->disableBackgroundScheduling(true);
  7. IPCThreadState::self()->setupPolling(&gBinderFd);
  8. if (gBinderFd >= 0) {
  9. if (healthd_register_event(gBinderFd, binder_event))
  10. KLOG_ERROR(LOG_TAG,
  11. "Register for binder events failed\n");
  12. }
  13. gBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar();
  14. gBatteryPropertiesRegistrar->publish();
  15. }

具体是调用healthd_mode_android_init函数,在这个函数先调用Binder接口,然后将fd 注册到epoll中去,处理函数就是binder_event函数。

2.1 ProcessState::setThreadPoolMaxThreadCount函数

先来看设置Binder最大线程数的函数:

[cpp] view plaincopy
  1. status_t ProcessState::setThreadPoolMaxThreadCount(size_t maxThreads) {
  2. status_t result = NO_ERROR;
  3. if (ioctl(mDriverFD, BINDER_SET_MAX_THREADS, &maxThreads) != -1) {
  4. mMaxThreads = maxThreads;
  5. } else {
  6. result = -errno;
  7. ALOGE("Binder ioctl to set max threads failed: %s", strerror(-result));
  8. }
  9. return result;
  10. }

最后是通过ioctl设置最大线程数。

2.2 IPCThreadState::disableBackgroundScheduling函数

下面我们再来看disableBackgroundScheduling函数,应该是禁止后台线程的意思

[cpp] view plaincopy
  1. void IPCThreadState::disableBackgroundScheduling(bool disable)
  2. {
  3. gDisableBackgroundScheduling = disable;
  4. }

我们再来看看在哪里使用了gDisableBackgroundScheduling 这个变量, 还是在executeCommand函数中处理BR_TRANSACTION命令时有下面这段代码

[cpp] view plaincopy
  1. case BR_TRANSACTION:
  2. {
  3. binder_transaction_data tr;
  4. result = mIn.read(&tr, sizeof(tr));
  5. ALOG_ASSERT(result == NO_ERROR,
  6. "Not enough command data for brTRANSACTION");
  7. if (result != NO_ERROR) break;
  8. Parcel buffer;
  9. buffer.ipcSetDataReference(
  10. reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
  11. tr.data_size,
  12. reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
  13. tr.offsets_size/sizeof(binder_size_t), freeBuffer, this);
  14. const pid_t origPid = mCallingPid;
  15. const uid_t origUid = mCallingUid;
  16. const int32_t origStrictModePolicy = mStrictModePolicy;
  17. const int32_t origTransactionBinderFlags = mLastTransactionBinderFlags;
  18. mCallingPid = tr.sender_pid;
  19. mCallingUid = tr.sender_euid;
  20. mLastTransactionBinderFlags = tr.flags;
  21. int curPrio = getpriority(PRIO_PROCESS, mMyThreadId);
  22. if (gDisableBackgroundScheduling) {
  23. if (curPrio > ANDROID_PRIORITY_NORMAL) {
  24. // We have inherited a reduced priority from the caller, but do not
  25. // want to run in that state in this process.  The driver set our
  26. // priority already (though not our scheduling class), so bounce
  27. // it back to the default before invoking the transaction.
  28. setpriority(PRIO_PROCESS, mMyThreadId, ANDROID_PRIORITY_NORMAL);
  29. }
  30. } else {
  31. if (curPrio >= ANDROID_PRIORITY_BACKGROUND) {
  32. // We want to use the inherited priority from the caller.
  33. // Ensure this thread is in the background scheduling class,
  34. // since the driver won't modify scheduling classes for us.
  35. // The scheduling group is reset to default by the caller
  36. // once this method returns after the transaction is complete.
  37. set_sched_policy(mMyThreadId, SP_BACKGROUND);
  38. }
  39. }

2.3 IPCThreadState::setupPolling

下面我们再来看看setupPolling函数

[cpp] view plaincopy
  1. int IPCThreadState::setupPolling(int* fd)
  2. {
  3. if (mProcess->mDriverFD <= 0) {
  4. return -EBADF;
  5. }
  6. mOut.writeInt32(BC_ENTER_LOOPER);
  7. *fd = mProcess->mDriverFD;
  8. return 0;
  9. }

我们看代码这个函数只是获取Binder驱动的fd

2.4 Binder驱动 有数据

然后我们把fd加入主线程的epoll进行监听,当Binder驱动有数据的时候,就会调用binder_event函数

[cpp] view plaincopy
  1. static void binder_event(uint32_t /*epevents*/) {
  2. IPCThreadState::self()->handlePolledCommands();
  3. }

我们来看下handlePolledCommands函数:

[cpp] view plaincopy
  1. status_t IPCThreadState::handlePolledCommands()
  2. {
  3. status_t result;
  4. do {
  5. result = getAndExecuteCommand();
  6. } while (mIn.dataPosition() < mIn.dataSize());
  7. processPendingDerefs();
  8. flushCommands();
  9. return result;
  10. }

getAndExecuteCommand函数之前我们分析过,这里再来看下:

[cpp] view plaincopy
  1. status_t IPCThreadState::getAndExecuteCommand()
  2. {
  3. status_t result;
  4. int32_t cmd;
  5. result = talkWithDriver();//获取binder驱动数据
  6. if (result >= NO_ERROR) {
  7. size_t IN = mIn.dataAvail();
  8. if (IN < sizeof(int32_t)) return result;
  9. cmd = mIn.readInt32();
  10. IF_LOG_COMMANDS() {
  11. alog << "Processing top-level Command: "
  12. << getReturnString(cmd) << endl;
  13. }
  14. pthread_mutex_lock(&mProcess->mThreadCountLock);
  15. mProcess->mExecutingThreadsCount++;
  16. pthread_mutex_unlock(&mProcess->mThreadCountLock);
  17. result = executeCommand(cmd);//执行命令
  18. pthread_mutex_lock(&mProcess->mThreadCountLock);
  19. mProcess->mExecutingThreadsCount--;
  20. pthread_cond_broadcast(&mProcess->mThreadCountDecrement);
  21. pthread_mutex_unlock(&mProcess->mThreadCountLock);
  22. // After executing the command, ensure that the thread is returned to the
  23. // foreground cgroup before rejoining the pool.  The driver takes care of
  24. // restoring the priority, but doesn't do anything with cgroups so we
  25. // need to take care of that here in userspace.  Note that we do make
  26. // sure to go in the foreground after executing a transaction, but
  27. // there are other callbacks into user code that could have changed
  28. // our group so we want to make absolutely sure it is put back.
  29. set_sched_policy(mMyThreadId, SP_FOREGROUND);
  30. }
  31. return result;
  32. }

先获取binder驱动数据,然后再执行executeCommand函数,和之前一样执行到BR_TRANSACTION命令会调用BBinder的transact,最终执行到service的onTransact函数中。

当然这些数据的处理都是在healthd的主线程中,是epoll在binder驱动有数据的时候执行的。

2.5 处理完数据后 清理工作

我们继续看handlePolledCommands函数

[cpp] view plaincopy
  1. status_t IPCThreadState::handlePolledCommands()
  2. {
  3. status_t result;
  4. do {
  5. result = getAndExecuteCommand();
  6. } while (mIn.dataPosition() < mIn.dataSize());
  7. processPendingDerefs();
  8. flushCommands();
  9. return result;
  10. }

最后做一些清理工作,在flushCommands函数中将和binder驱动的交互关闭。

[cpp] view plaincopy
  1. void IPCThreadState::flushCommands()
  2. {
  3. if (mProcess->mDriverFD <= 0)
  4. return;
  5. talkWithDriver(false);
  6. }

三、总结

这篇博客我们主要讲了使用binder的demon的binder调用流程,以及不使用binder线程的代码调用方法,举例了healthd中的一个例子。



原始地址: http://blog.csdn.net/kc58236582/article/details/51744398

Android Binder ProcessState IPCThreadState相关介绍相关推荐

  1. Android Service服务的相关介绍

    文章目录 Android Service服务的相关介绍 创建方式 启动方式 生命周期 onStartCommand参数及返回值的理解 ServiceConnection 场景说明 前台服务 问答 st ...

  2. Android中的sdk相关介绍

    简介 Sdk(software development kit)是指被软件工程师用于为特定的软件包.框架.硬件平台等建立应用软件的开发工具集,即软件开发工具包.就像前面配置java环境中的jdk类似, ...

  3. Android O 前期预研之二:HIDL相关介绍

    在上一篇博客里,大致介绍了下Android O 中treble计划的一些背景与相关基本架构,这一篇中跟大家一起来探讨下HIDL相关的内容. Android HAL类型  在此之前的ANDROID版本当 ...

  4. Android Pmem相关介绍

    http://fangjian0518.blog.163.com/blog/#m=0 Android Pmem相关介绍 2011-10-18 09:40:26|  分类: Android PMEM | ...

  5. 理解Android Binder机制(3/3):Java层

    本文是Android Binder机制解析的第三篇,也是最后一篇文章.本文会讲解Binder Framework Java部分的逻辑. Binder机制分析的前面两篇文章,请移步这里: 理解Andro ...

  6. Android BINDER详解

    1.   进程间通信的本质(2个进程) 用户空间的进程如果想相互通信, 必须经过内核, 因为不同进程的用户地址空间是独立的, 但是共享同一个内核空间. 内核为了支持进程间通信, 一般会有一个驱动, 以 ...

  7. Android Binder设计与实现 - 设计篇

    本文属于原创作品,转载请注明出处并放于明显位置,原文地址:http://www.cnblogs.com/albert1017/p/3849585.html 前言 在学习Android的Binder机制 ...

  8. Android Binder(也许是最容易理解的)

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  9. 一篇文章了解相见恨晚的 Android Binder 进程间通讯机制

    概述 最近在学习Binder机制,在网上查阅了大量的资料,也看了老罗的Binder系列的博客和Innost的深入理解Binder系列的博客,都是从底层开始讲的,全是C代码,虽然之前学过C和C++,然而 ...

最新文章

  1. Python快速学习02:基本数据类型 序列
  2. 【转载】笛卡尔转极坐标
  3. chgrp 简明笔记
  4. android ViewPager滑动事件讲解
  5. 父子沪c转大牌过户_机动车异地过户(转籍)
  6. 案例分享,从0到1了解一个完整项目
  7. 本人教你五分钟学会五笔(不服找我)
  8. php中对象是引用类型吗?
  9. 华为交换机关闭网口_华为交换机如何关闭网络端口号
  10. 机器学习笔记之概率图模型(五)马尔可夫随机场的结构表示
  11. 物联网毕业设计 车牌识别系统 stm32
  12. 狂神JAVA博客MySQL_狂神说SpringBoot08:整合Druid
  13. Oracle数据库,创建表并给表、字段添加注释
  14. 深信服上网行为管理(AC)、安全网关(SG)学习笔记
  15. 一个人一个微博、一个App一个故事:通过微博草根账号做英语学习App的“爱卡微口语”获晨脉创投天使投资
  16. 支付宝支付之“单笔转账到支付宝账户接口”的调用(生成签名、上传应用公钥、下载SDK、接口调用、报错自动排查、查看错误码)
  17. 四万高手过招,这份阿里全球数学竞赛试题你真的不要看吗...
  18. 4.17 使用阴影/高光命令解决图像曝光不足问题 [原创Ps教程]
  19. pc使用qq for android,腾讯QQ for Pad Android版
  20. 苹果icloud邮箱抓取

热门文章

  1. sharepoint中一些gridview的使用方法
  2. linux中使用ssh或scp时如何跳过RSA key fingerprint输入yes/no
  3. 征值和特征向量的几何意义、计算及其性质
  4. va_list 简介
  5. C/C++常见库函数实现(memcpy、memset、 strcpy)
  6. 我用 PyTorch 复现了 LeNet-5 神经网络(自定义数据集篇)!
  7. 摄像机投射投影模型_综述及详解
  8. 在状态栏中插入类似进度条的可视控件
  9. Linux中su命令详解
  10. C/C++常用宏定义,注意事项,宏中#和##的用法