1. 程式人生 > >Android Native層Binder.transact()函式呼叫 Binder.onTransact() 函式失敗分析

Android Native層Binder.transact()函式呼叫 Binder.onTransact() 函式失敗分析

Q:Android Native層Binder.transact()函式呼叫 Binder.onTransact() 函式失敗?

在Android Native層呼叫Camera.h中的api實現一個截圖功能的應用時,發現通過gCamera->setListener(new ScreenCaptureListener())設定到Camera的mListener的用於接收Camera預覽資料的回撥函式沒有被呼叫,導致截圖失敗?

注:
Camera類檔案彙總:
libcamera_client.so
Camera
ICamera
ICameraClient
ICameraService
CameraBase
CameraHardwareInterface

libcameraservice.so
CameraService
CameraClient
Camera2Client

A: 原因分析

梳理Camera預覽的整個正確流程應該如下:

//TODO:Camera從上往下設定呼叫層次圖1.
Native demo -> Camera -> CameraService -> CameraClient -> CameraHardwareInterface -> CameraHal_Module -> XCDipHardware_einstein -> PipManager
//TODO:Camera從下往上回調呼叫層次圖2.
SN -> XCDipHardware_einstein -> CameraClient -> ICameraClient -> Camera -> SCREENSHOT_MAIN

其LOG如下:

01-01 23:13:40.855 D/XCDipHardware_einstein( 1154): call processLoop.
01-01 23:13:40.865 D/XCDipHardware_einstein( 1154): [XCDipHardware] handlePreviewData call datacb.
01-01 23:13:40.865 V/CameraClient( 1154): __data_cb
01-01 23:13:40.865 D/CameraClient( 1154): dataCallback(16, 0x10 )
01-01 23:13:40.865 D/CameraClient( 1154): CameraClient::handlepreviewData.
01-01 23:13:40.865 V/ICameraClient( 1154): dataCallback
01-01 23:13:40.865 D/ICameraClient( 1154): Bp tid: 4090111104, pid: 1154.
01-01 23:13:40.865 V/ICameraClient( 2879): DATA_CALLBACK
01-01 23:13:40.865 D/ICameraClient( 2879): Bn tid: 2915554048, pid: 2879.
01-01 23:13:40.865 D/Camera ( 2879): Camera::dataCallback
01-01 23:13:40.865 D/Camera ( 2879): Callback tid: 2915554048, pid: 2879.
01-01 23:13:40.865 I/SCREENSHOT_MAIN( 2879): ScreenCaptureListener::postData. offset = 0, size = 1228800,

但是在回傳採集到的影象資料的過程中,ICameraClient中的BpCameraClient呼叫完remote()->transact(DATA_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY)函式後,並沒有接著呼叫到BnCameraClient::onTransact的DATA_CALLBACK,而是直接BpCameraClient dataCallback call transact finished.
從這兒來看,好像是Android的Binder調用出現了問題!

關於IBinder及其transact函式,在網上找到如下說明:

IBinder
android.os.IBinder
Class Overview
Base interface for a remotable object, the core part of a lightweight remote procedure call mechanism designed for high performance when performing in-process and cross-process calls. This interface describes the abstract protocol for interacting with a remotable object. Do not implement this interface directly, instead extend from
Binder
.
The key IBinder API is
transact()
matched by
Binder.onTransact()
. These methods allow you to send a call to an IBinder object and receive a call coming in to a Binder object, respectively. This transaction API is synchronous, such that a call to
transact()
does not return until the target has returned from
Binder.onTransact()
. this is the expected behavior when calling an object that exists in the local process, and the underlying inter-process communication (IPC) mechanism ensures that these same semantics apply when going across processes.

The system maintains a pool of transaction threads in each process that it runs in. These threads are used to dispatch all IPCs coming in from other processes. For example, when an IPC is made from process A to process B, the calling thread in A blocks in transact() as it sends the transaction to process B. The next available pool thread in B receives the incoming transaction, calls Binder.onTransact() on the target object, and replies with the result Parcel. Upon receiving its result, the thread in process A returns to allow its execution to continue. In effect, other processes appear to use as additional threads that you did not create executing in your own process.

Binder通訊過程中,transact()和Binder.onTransact()在不同的兩個程序中被呼叫,從我錄出來的log中發現,transact()函式跑在系統的mediaserver程序中,而Binder.onTransact()應該是跑在我的demo程序中的,這些API原本都應該是同步的,當mediaserver程序的呼叫執行緒把transaction傳送給demo程序之後,自身就應該阻塞在transact()中,demo程序中的空閒執行緒然後接收過來的transaction,並對目標物件呼叫Binder.onTransact(),並用結果Parcel回覆,mediaserver程序中的執行緒收到後,繼續從阻塞的地方開始執行。

但是現在發現,我的這個demo程式中出現兩個問題:
1. Transact()函式被呼叫後直接返回了,並沒有阻塞住?
2. Transact()函式呼叫後,我的demo程序並沒有去執行Binder.ontransact()函式,說明要麼是我的demo程序或者其相關執行緒些時不存在或者是阻塞住了?

為什麼transact()函式呼叫後並沒有阻塞住?

看ICameraClient.cpp檔中的BpCameraClient:: dataCallback()函式
這裡寫圖片描述
圖3 BpCameraClient:: dataCallback()函式

檢測返回值err,log並沒有列印,說明返回值正確;檢測Binder.transact()的引數IBinder::FLAG_ONEWAY,官網解釋:
這裡寫圖片描述
圖4 Binder引數FLAG_ONEWAY

在Binder.transact()中加多這個引數,這個函式就是一個非同步呼叫,會立刻返回,我做了一個測試,將這個引數去掉,再次執行時發現圖3最後一行log確實沒有輸出來,BpBinder呼叫執行緒阻塞住,但是demo程式依然沒有影象輸出來!說明Binder失敗的原因不在這兒!

Demo程序中相關執行緒阻塞或者不存在?

在demo程式執行卡住時,我在系統中使用

[email protected]:/data/capture # debuggerd64 ps | grep screenshot | busybox awk '{print $2}'
Sending request to dump task 2994.
Tombstone written to: /data/tombstones/tombstone_00

打印出當前demo程序狀態資訊,發現demo程序只有一個主執行緒

pid: 2994, tid: 2994, name: screenshot >>> /system/bin/screenshot <<<

這個執行緒堆疊如下:

backtrace:
#00 pc 0000000000019a5c /system/lib64/libc.so (syscall+28)
#01 pc 00000000000202b0 /system/lib64/libc.so (pthread_mutex_lock+252)
#02 pc 000000000001efb4 /system/lib64/libc.so (__pthread_cond_timedwait_relative(pthread_cond_t*, pthread_mutex_t*, timespec const*)+116)
#03 pc 000000000001f028 /system/lib64/libc.so (__pthread_cond_timedwait(pthread_cond_t*, pthread_mutex_t*, timespec const*, int)+68)
#04 pc 00000000000031b4 /system/bin/screenshot

可以看出,當前它正阻塞在gAvailableCV.waitRelative(gAvailableLock, 1000*1000000)函式中, 於是我用pthread_create函式另起了一個執行緒去執行Camera::connect()函式,依然是這樣的。
說明Bn端的Binder訊息並不是在主執行緒或者我們自己使用pthread_create建立的執行緒中處理的,也就是說我們的程序中並沒有處理Binder Bp端和Bn端訊息的執行緒,那麼該如何建立這兩個執行緒呢?

在Binder通訊機制中,一個服務如果要使用Binder,就必須做兩件事:
1. 開啟binder裝置;
2. 建立一個looper迴圈,然後等待請求。

在ICameraClient類及其派生類Camera和我們呼叫Camera介面的demo中,都沒有看到做這兩件事的程式碼,
看看Camera與ICameraClient有關係的程式碼:

class Camera :  public CameraBase<Camera>, public BnCameraClient{}
class BnCameraClient : public BnInterface<ICameraClient>{}
class ICameraClient : public IInterface{}

看起來,BnInterface似乎是開啟Binder裝置的

template<typename INTERFACE>
class BnInterface : public INTERFACE, public BBinder
{
public:
    virtual sp<IInterface>      queryLocalInterface(const String16& _descriptor);
    virtual const String16&     getInterfaceDescriptor() const;
protected:
    virtual IBinder*            onAsBinder();
};

兌現後變成

class BnInterface : public ICameraClient, public BBinder
BBinder, BpBinder, 是不是和BnXXX以及BpXXX對應的呢?找到它定義的地方:
BBinder::BBinder()
    : mExtras(NULL)
{
    //也沒有開啟Binder裝置?
}

說明Binder機制在自身初始化過程中,並沒有主動去開啟Binder裝置!

回到Android工程中,去找main_mediaserver中是如何使用Binder的:
這裡寫圖片描述
圖5 main_systemserver main函式

第一個呼叫的函式是ProcessState::self(),然後賦值給了proc變數,程式執行完,proc會自動delete內部的內容,所以就自動釋放了先前分配的資源。
ProcessState位置在ProcessState位置在frameworks/native/libs/binder/ProcessState.cpp

sp<ProcessState> ProcessState::self()
{
    if (gProcess != NULL) return gProcess; //第一次進來肯定不走這兒
    AutoMutex _l(gProcessMutex);  //鎖保護
    if (gProcess == NULL) gProcess = new ProcessState;   //建立一個ProcessState物件
return gProcess;   //這裡返回的是指標,但是函式返回的是sp<xxx>,所以把sp<xxx>看成是XXX*是可以的
}

再來看看ProcessState建構函式

ProcessState::ProcessState()
    : mDriverFD(open_driver())  //Android很多程式碼都是這麼寫的,稍不留神就沒看見這裡呼叫了一個很重要的函式
    , mVMStart(MAP_FAILED)//對映記憶體的起始地址
    , mManagesContexts(false)
    , mBinderContextCheckFunc(NULL)
    , mBinderContextUserData(NULL)
    , mThreadPoolStarted(false)
    , mThreadPoolSeq(1)
{
    if (mDriverFD >= 0) {
    //BIDNER_VM_SIZE定義為(1*1024*1024) - (4096 *2) 1M-8K
         mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE,
             mDriverFD, 0);//這個需要你自己去man mmap的用法了,不過大概意思就是
        //將fd對映為記憶體,這樣記憶體的memcpy等操作就相當於write/read(fd)了
    }
    ...
}

open_driver,就是開啟/dev/binder這個裝置,這個是android在核心中搞的一個專門用於完成程序間通訊而設定的一個虛擬的裝置, 就是核心的提供的一個機制.

static int open_driver()
{
      int fd = open("/dev/binder", O_RDWR);//開啟/dev/binder
      if (fd >= 0) {
          ....
          size_t maxThreads = 15;
          //通過ioctl方式告訴核心,這個fd支援最大執行緒數是15個。
          result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);    
      }
      return fd;
}

sp proc(ProcessState::self())這兒應該是開啟Binder裝置的操作.
開啟binder裝置的地方是和程序相關的,一個程序開啟一個就可以了。
那麼,在哪裡進行類似的訊息迴圈looper操作呢?

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

這兒應該是開啟Binder裝置的操作, 那麼

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

應該是進行類似訊息迴圈的looper的操作啦!
看看startThreadPool:

void ProcessState::startThreadPool()
{
  ...
    spawnPooledThread(true);
}

void ProcessState::spawnPooledThread(bool isMain)
{
    sp<Thread> t = new PoolThread(isMain);isMain是TRUE
    //建立執行緒池,然後run起來,和java的Thread何其像也。
    t->run(buf);
 }
PoolThread從Thread類中派生,那麼此時會產生一個執行緒嗎?看看PoolThread和Thread的構造
PoolThread::PoolThread(bool isMain)
        : mIsMain(isMain)
{

}

Thread::Thread(bool canCallJava)//canCallJava預設值是true
    :   mCanCallJava(canCallJava),
        mThread(thread_id_t(-1)),
        mLock("Thread::mLock"),
        mStatus(NO_ERROR),
        mExitPending(false), mRunning(false)
{

}

這個時候還沒有建立執行緒, 然後呼叫PoolThread::run,實際呼叫了基類的run:

status_t Thread::run(const char* name, int32_t priority, size_t stack)
{
    bool res;
    if (mCanCallJava) {
        res = createThreadEtc(_threadLoop,//執行緒函式是_threadLoop
                 this, name, priority, stack, &mThread);
    }
}

終於,在run函式中,建立執行緒了。從此主執行緒執行

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

新開的執行緒執行_threadLoop

int Thread::_threadLoop(void* user)
{
    Thread* const self = static_cast<Thread*>(user);
    sp<Thread> strong(self->mHoldSelf);
    wp<Thread> weak(strong);
    self->mHoldSelf.clear();

    do {
        ...
        if (result && !self->mExitPending) {
            result = self->threadLoop();哇塞,呼叫自己的threadLoop
        }
    }

我們是PoolThread物件,所以呼叫PoolThread的threadLoop函式

virtual bool PoolThread ::threadLoop()
{
    //mIsMain為true。
   //而且注意,這是一個新的執行緒,
   //所以必然會建立一個新的IPCThreadState物件(記得執行緒本地儲存嗎?TLS),然後      
    IPCThreadState::self()->joinThreadPool(mIsMain);
    return false;
}

主執行緒和工作執行緒都呼叫了joinThreadPool,看看這個幹嘛了!

void IPCThreadState::joinThreadPool(bool isMain)
{
    mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
    status_t result;
    do {
        int32_t cmd;
        result = talkWithDriver();
        result = executeCommand(cmd);
    } while (result != -ECONNREFUSED && result != -EBADF);

    mOut.writeInt32(BC_EXIT_LOOPER);
    talkWithDriver(false);
}

有loop了,但是好像是有兩個執行緒都執行了這個啊!這裡有兩個訊息迴圈.
下面看看executeCommand

status_t IPCThreadState::executeCommand(int32_t cmd)
{
    BBinder* obj;
    RefBase::weakref_type* refs;
    status_t result = NO_ERROR;
    case BR_TRANSACTION:
    {
        binder_transaction_data tr;
        result = mIn.read(&tr, sizeof(tr));
        //來了一個命令,解析成BR_TRANSACTION,然後讀取後續的資訊
        Parcel reply;
        if (tr.target.ptr) {
            //這裡用的是BBinder。
            sp<BBinder> b((BBinder*)tr.cookie);
            const status_t error = b->transact(tr.code, buffer, &reply, 0);
        }
    }
}

讓我們看看BBinder的transact函式幹嘛了

status_t BBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    //呼叫自己的onTransact函式      
    err = onTransact(code, data, reply, flags);
    return err;
}

BnCameraClient從BBinder派生,所以會呼叫到它的onTransact函式
然後BnCameraClient的onTransact函式收取命令,然後派發到派生類Camera的函式,由它完成實際的工作。

從上面的分析來看,我的demo程式程序中需要呼叫

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

來開啟Binder裝置,還需要呼叫

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

來建立Binder訊息處理執行緒,該執行緒為一個loop迴圈處理執行緒。

在demo程序中新增這三句後,BpCameraClient果然就可以呼叫到BnCameraClient中去了,在串列埠中輸入:
Busybox ps –T命令檢視當前demo程序中的執行緒狀態:

3151 0 0:00 /system/bin/screenshot
3152 0 0:00 {Binder_1} /system/bin/screenshot
3154 0 0:00 {Binder_2} /system/bin/screenshot

發現此時screenshot程序確實有三個程序,其中兩個為Binder執行緒,進一步打印出當前demo程序狀態資訊,發現demo程序中現在有三個執行緒了:

5 pid: 2994, tid: 2994, name: screenshot >>> /system/bin/screenshot <<<
687 pid: 2994, tid: 2995, name: Binder_1 >>> /system/bin/screenshot <<<
2019 pid: 2994, tid: 2997, name: Binder_2 >>> /system/bin/screenshot <<<

Screenshot為demo主執行緒,Binder_1堆疊如下:

backtrace:
#00 pc 000000000006104c /system/lib64/libc.so (nanosleep+4)
#01 pc 0000000000037e00 /system/lib64/libc.so (sleep+40)
#02 pc 0000000000002c64 /system/bin/screenshot
#03 pc 0000000000028af0 /system/lib64/libcamera_client.so (android::Camera::dataCallback(int, android::s
#04 pc 000000000002e8dc /system/lib64/libcamera_client.so (android::BnCameraClient::onTransact(unsigned
#05 pc 0000000000021bac /system/lib64/libbinder.so (android::BBinder::transact(unsigned int, android::Pa
#06 pc 000000000002a04c /system/lib64/libbinder.so (android::IPCThreadState::executeCommand(int)+876)
#07 pc 000000000002a22c /system/lib64/libbinder.so (android::IPCThreadState::getAndExecuteCommand()+92)
#08 pc 000000000002a2a0 /system/lib64/libbinder.so (android::IPCThreadState::joinThreadPool(bool)+76)
#09 pc 0000000000031bd0 /system/lib64/libbinder.so
#10 pc 00000000000169c0 /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+208)
#11 pc 000000000001620c /system/lib64/libutils.so
#12 pc 000000000001f168 /system/lib64/libc.so (__pthread_start(void*)+52)
#13 pc 000000000001b370 /system/lib64/libc.so (__start_thread+16)

Binder_1是BBinder訊息處理執行緒。
Binder_2堆疊如下:

backtrace:
#00 pc 000000000006173c /system/lib64/libc.so (__ioctl+4)
#01 pc 0000000000088a48 /system/lib64/libc.so (ioctl+100)
#02 pc 00000000000299a4 /system/lib64/libbinder.so (android::IPCThreadState::talkWithDriver(bool)+164)
#03 pc 000000000002a1e8 /system/lib64/libbinder.so (android::IPCThreadState::getAndExecuteCommand()+24
#04 pc 000000000002a2a0 /system/lib64/libbinder.so (android::IPCThreadState::joinThreadPool(bool)+76)
#05 pc 0000000000031bd0 /system/lib64/libbinder.so
#06 pc 00000000000169c0 /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+208)
#07 pc 000000000001620c /system/lib64/libutils.so
#08 pc 000000000001f168 /system/lib64/libc.so (__pthread_start(void*)+52)
#09 pc 000000000001b370 /system/lib64/libc.so (__start_thread+16)

它應該就是BpBinder向Binder裝置傳送訊息的執行緒。

相關推薦

Android NativeBinder.transact()函式呼叫 Binder.onTransact() 函式失敗分析

Q:Android Native層Binder.transact()函式呼叫 Binder.onTransact() 函式失敗? 在Android Native層呼叫Camera.h中的api實現一個截圖功能的應用時,發現通過gCamera->setLi

使用GDB除錯Android Native 程式碼

--------------步驟:0. adb root0. adb shell0. ps | grep browser1. gdbserver :5039 --attach pid2. adb forward tcp:5039 tcp:5039 1. prebuilts/gcc/linux-x86/arm/

[M0]Android NativeLooper詳解

前言 我們知道Java 層的Looper 的訊息佇列在沒有訊息處理的時候,會wait在MessageQueue.next() 函式裡,對於MessageQueue.next() 函式是如何實現的wait,卻是一知半解。而且Android Framework

C#VS中一個函式呼叫另一個函式的程式碼樣例

//主函式 說明:下面的函式是想求許可證的十六位編號,最後一位是許可編號的校驗碼,是以本體碼("JY" + xukbh)為基礎來計算 entities[0].XuKeZhengBianHao = "JY" + xukbh + xukebianhaojiaoyanma(xukbh);  

MFC中非類成員函式呼叫類成員函式方法

1、定義對話方塊類物件全域性變數指標 CDialog *g_pDlg,同時在初始化對話方塊時用this指標初始化此全域性變數。 2、在非類成員函式中可以使用g_pDlg->成員函式名或變數名進行呼叫訪問。   假如有類A,類B兩個類。如果想在B中呼叫A的成員函式,該怎麼辦

Python 函式呼叫&定義函式&函式引數

一.函式呼叫 就拿abs()函式來舉例 用來返回數值的絕對值 >>> abs(-5)5 二.定義函式  我們可以通過def 來自定義函式,格式為 def  函式名(引數): 舉一個定義計算兩個數值相乘的函式:  def product(x

C#建構函式呼叫其他建構函式(轉)

其實就是使用this來實現的。看一下例子就會明白的了。 class Class1 { public Class1() { //Code 1 } public Class1(string s) : this() { //Code 2 } publ

函式呼叫約定與函式名稱修飾規則(一)

    作者:星軌(oRbIt)    E_Mail:[email protected]    轉載請註明原作者,否則請勿轉載       使用C/C++語言開發軟體的程式設計師經常碰到這樣的問題:有時候是程式編譯沒有問題,但是連結的時候總是報告函式不存在(經典的L

函式呼叫約定及函式名修飾規則

函式呼叫約定:是指當一個函式被呼叫時,函式的引數會被傳遞給被呼叫的函式和返回值會被返回給呼叫函式。函式的呼叫約定就是描述引數是怎麼傳遞和由誰平衡堆疊的,當然還有返回值。 幾種型別:__stdcall,__cdecl,__fastcall,__thiscall,__n

不定引數函式呼叫不定引數函式

#include <stdarg.h> #include <stdio.h> int myprintf(const char *fmt, ...) { int ret; va_list ap; va_start(a

函式呼叫過程中函式棧詳解

當程序被載入到記憶體時,會被分成很多段 程式碼段:儲存程式文字,指令指標EIP就是指向程式碼段,可讀可執行不可寫,如果發生寫操作則會提示segmentation fault 資料段:儲存初始化的全域性變數和靜態變數,可讀可寫不可執行 BSS:未初始化的全域性變數

函式呼叫棧的獲取原理分析

上一篇文章《在Linux程式中輸出函式呼叫棧》,講述了在Linux中如何利用backtrace獲取呼叫棧,本篇文章主要介紹一下獲取函式呼叫棧的原理,並給出相應的實現方式。 要了解呼叫棧,首先需要了解函式的呼叫過程,下面用一段程式碼作為例子: #include <

利用函式指標實現父類函式呼叫子類函式

父子類關係 對於繼承關係中的父類和子類,我們可以說子類是父類的一種,子類繼承了父類的屬性和行為。因此,子類可以訪問父類的所有非私有成員。相反,父類一般情況下是不能訪問子類成員的。然而,我們可以通過一些方法間接的實現父類訪問子類,即父類函式訪問子類函式。

C++Static 靜態函式呼叫非靜態函式

test.h class test : { public: test(void); ~test(void); public: //你的其他函式 ... ..

linux下函式呼叫棧Backtraces函式

Backtraces A backtrace is a list of the function calls that are currently active in a thread. The usual way to inspect a backtrace of a

Android系統的Binder機制及其native應用

以下是個人對android中Binder機制的理解,主要是通訊過程的概述,涉及知識不是很深,做到有一個巨集觀的認識,如有錯誤,望指點. Binder是一種程序間通訊機制. 既然是程序間的通訊,那首先就是存在兩個程序. android中的程序間通訊是使用binder機制,那麼

Android native程序間通訊例項-binder篇之——HAL訪問JAVA的服務

有一天在群裡聊天的時候,有人提出一個問題,怎樣才能做到HAL層訪問JAVA層的介面?剛好我不會,所以做了一點研究。   之前的文章末尾部分說過了service call 可以用來除錯系統的binder服務。 傳送門: Android native程序間通訊例項-binder篇之&mda

Android呼叫C++實現共享記憶體(Native

MemoryFile是java層封裝的介面,它實現共享記憶體主要呼叫瞭如下函式: int fd = open("/dev/ashmem",O_RDWR); ioctl(fd, ASHMEM_SET_NAME,name); ioctl(fd,ASHMEM_SET

Android native程序間通訊例項-binder結合共享記憶體

  在android原始碼的驅動目錄下,一般會有共享記憶體的相關實現原始碼,目錄是:kernel\drivers\staging\android\ashmem.c。但是本篇文章不是講解android共享記憶體的功能實現原理,而是講怎麼運用它。   1.    在linux中,不同程序間擁有自己獨

Android Framework 分析---2消息機制Native

jnienv car 下一個 sas tracking zed 高效 方法 java 在Android的消息機制中。不僅提供了供Application 開發使用的java的消息循環。事實上java的機制終於還是靠native來實現的。在native不僅提供一套消息傳