從零開始怎麼寫android native service?

來源:互聯網
上載者:User

從零開始怎麼寫android native service?

Android service對於從事android開發的人都不是一個陌生的東西,很多人可能會覺得服務很簡單。服務是簡單,因為複雜的別人做了,所以才會覺得簡單。我們先梳理一下服務的分類,首先有本地服務跟系統服務的區分,而在APP裡寫的服務大多就成為Java服務或者應用服務。

做APP的人寫個應用服務相對來說是最簡單的,因為extends了一個service後幾個簡單的介面就可以跑起來了,寫完這種服務可能也只是對服務一知半解,因為值錢的service類Google的人已經幫你寫好了,這是你的福氣為你帶來了便利,當然也可能會麻痹你:),但是做APP的人會有能解決問題是首要任務了,有時間還是對它瞭解更清楚點比較好,在此不再討論這個。

做裝置做系統的人,經常可能會去寫系統服務,也就是framework下面的服務,systemserver裡面註冊的服務,寫這種服務一般來說比較少,只有做裝置系統的才會這樣幹,才有機會有完成的系統代碼,可以在裡面自由遨遊,筆者三年前寫過一個,可以看看【自己動手從零開始寫一個完整的android Service

那剩下的一個是本地服務,也就是native service,這種服務我們瞭解的系統裡面多媒體、audio system都是寫成了本地服務,這樣寫的好處就是啟動並執行效率更高一點,因為C/C++先天性就比JAVA的運行效率要高一點。筆者就是由於長期主要從事的都是底層開發的,我們有時有這麼一種需求,又要運行效率高,又要好移植,主要是考慮推廣寫東西給廣大客戶,那麼我就寫一個本地服務,這樣是最獨立的了,效率也最高了,那一個本地服務到底怎麼寫呢?大多數的人寫過的服務以java服務居多,真正寫本地服務的不多,本地服務相對來說又是更複雜一點的。因此決定從零開始自己動手寫一個本地service,下面就大概描述一下過程。

本地服務有四大塊,服務介面(IService),服務代理(也就是BpService),服務stub(也就是BnService),服務實體(Service);下面筆者的執行個體就以demoNativeService來開啟,力求簡單,裡面就寫了兩個介面;

首先定義好服務介面IdemoNativeService,IdemoNativeService服務介面的父類是IInterface,在裡面主要是要聲明一下介面,在DECLARE_META_INTERFACE(demoNativeService),代碼如下:

 

class IdemoNativeService : public IInterface{public:enum {CONNECT = IBinder::FIRST_CALL_TRANSACTION,PRINTSTRING_CMD,};public:DECLARE_META_INTERFACE(demoNativeService);virtual status_t connect(int pid,int previewhw,int intf,int fmt,int chan) = 0;  virtual  status_t  printString(const char *str) = 0;};

 

當然定義好了IdemoNativeService的標頭檔,就需要去實操了,先來搞定BpdemoNativeService,它的父類是BpInterface,這裡面主要是涉及資料的跨進程用到的parcel,讀啊,寫啊,按套路來,也不難,也有AIDL工具可以使用,幫你轉出來,再稍微修改一下就可以了,裡面有一個很重要的remote,這個和remote就是幕後功臣啊,它儲存了服務執行個體的對象啊,它是來之BpRefBase的一個成員,產生服務的時候,會得到賦值,定義完了以後,很重要的一個程式就是要IMPLEMENT_META_INTERFACE(demoNativeService,"android.hardware.IdemoNativeService");這個宏是非常重要的,跟前面那個DECLARE是對應的,前面聲明,後面實現,當然我們帶的參數跟的名字是必須一致的,這樣才能正常溝通嘛!

 

class BpdemoNativeService: public BpInterface{public:BpdemoNativeService(const sp& impl): BpInterface(impl){}virtual status_t connect(int pid,int previewhw,int intf,int fmt,int chan){Parcel data, reply;data.writeInterfaceToken(IdemoNativeService::getInterfaceDescriptor());data.writeInt32(pid);data.writeInt32(previewhw);data.writeInt32(intf);data.writeInt32(fmt);data.writeInt32(chan);remote()->transact(IdemoNativeService::CONNECT, data, &reply);return reply.readInt32();}  virtual  status_t  printString(const char *str)  {    Parcel data, reply;    data.writeInterfaceToken(IdemoNativeService::getInterfaceDescriptor());    data.writeCString(str);    remote()->transact(IdemoNativeService::PRINTSTRING_CMD, data, &reply);return reply.readInt32();  }};IMPLEMENT_META_INTERFACE(demoNativeService, "android.hardware.IdemoNativeService");//android.hardware.IdemoNativeService ds.demonativeservice

 

接著需要寫服務stub了,BndemoNativeService的父類是BnInterface,有沒有發現BndemoNativeService跟BpdemoNativeService,都會基於介面類IdemoNativeService,這樣溝通起來的介面就唯一了,就具備了對話的可能;

 

class BndemoNativeService: public BnInterface{public:virtual status_t onTransact( uint32_t code,const Parcel& data,Parcel* reply,uint32_t flags = 0);};status_t BndemoNativeService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags){switch(code){/*case CONNECT: {CHECK_INTERFACE(IdemoNativeService, data, reply);int pid         = data.readInt32();int previewhw   = data.readInt32();int intf        = data.readInt32();int fmt         = data.readInt32();int chan        = data.readInt32();reply->writeInt32(connect(pid,previewhw,intf,fmt,chan));return NO_ERROR;}break;case PRINTSTRING_CMD: {CHECK_INTERFACE(IdemoNativeService, data, reply);const char *str;      str = data.readCString();reply->writeInt32(printString(str));return NO_ERROR;}break;*/default:return BBinder::onTransact(code, data, reply, flags);}}

 

到這就輪到了大塊頭service實體demoNativeService了,demoNativeService是基於BndemoNativeService,在demoNativeService裡面定義了一個instantiate()介面用於添加service到servicemanager裡面去,注意demoNativeService()跟解構函式~demoNativeService()需要寫成private的,免得別人可以new出對象來。在裡面重寫了onTransact,一旦BpdemoNativeService有風吹草動,就會聯動到BndemoNativeService,因為服務實體重寫了onTransact,所以實際就會先執行到demoNativeService::onTransact這裡來,在這裡面處理不了,可以再轉給BpdemoNativeService的onTransact或者直接到BBinder的onTransact;

 

void demoNativeService::instantiate() {android::defaultServiceManager()->addService(                IdemoNativeService::descriptor, new demoNativeService());}demoNativeService::demoNativeService(){    ALOGE("demoNativeService created");    mOpened = 1;}demoNativeService::~demoNativeService(){    ALOGE("demoNativeService destroyed");} status_t  demoNativeService::connect(int pid,int previewhw,int intf,int fmt,int chan){     ALOGD("demoNativeService connect:%d, %d, %d, %d, %d", pid, previewhw, intf, fmt, chan);   return 88; } status_t  demoNativeService::printString(const char *str){   ALOGD("demoNativeService printString:%s", str);   return 66; }     #if 1        status_t demoNativeService::onTransact(uint32_t code,                                                const android::Parcel &data,                                                android::Parcel *reply,                                                uint32_t flags){        ALOGD("OnTransact(%u,%u)", code, flags);                switch(code) { case CONNECT: {CHECK_INTERFACE(IdemoNativeService, data, reply);int pid         = data.readInt32();int previewhw   = data.readInt32();int intf        = data.readInt32();int fmt         = data.readInt32();int chan        = data.readInt32();      ALOGD("CONNECT: %d, %d, %d, %d, %d\n", pid,previewhw,intf,fmt,chan);reply->writeInt32(connect(pid,previewhw,intf,fmt,chan));return NO_ERROR;}break;                       case PRINTSTRING_CMD: {                CHECK_INTERFACE(IdemoNativeService, data, reply);                const char *str;                str = data.readCString();                ALOGD("PrintString: %s\n", str);                ALOGD("printString: %s\n", str);reply->writeInt32(printString(str));                return NO_ERROR;        } break;        default:                return BndemoNativeService::onTransact(code, data, reply, flags);        }        return NO_ERROR;}#endif

 

寫完了服務,那我們就再寫一個可執行檔來產生一下,裡面startThreadPool產生線程池,然後再調用joinThreadPool來監聽變化;

 

    sp proc(ProcessState::self());    sp sm = defaultServiceManager();//    ALOGI("ServiceManager: %p", sm.get());    demoNativeService::instantiate();    ProcessState::self()->startThreadPool();    IPCThreadState::self()->joinThreadPool();

 

寫到這,可以說服務已經可以跑起來了,那我們怎麼驗證呢,最快的辦法還是寫一個可執行檔去測一下它的介面,看通沒通就知道了;

 

       int ret= -1;      int pid = IPCThreadState::self()->getCallingPid();      ALOGI("demoNativeService client is now starting, pid=%d", pid);        android::sp sm = android::defaultServiceManager();        android::sp binder;        android::sp shw;        do {                binder = sm->getService(android::String16("ds.demonativeservice"));                if (binder != 0)                        break;                ALOGW("IdemoNativeService not published, waiting...");                usleep(500000);        } while(true);      ALOGI("IdemoNativeService client is now trying");       shw = android::interface_cast(binder);       ret = shw->printString("Good man desheng");     ALOGI("demoNativeService client printString, ret=%d", ret);              ret = shw->connect(pid,1, 2, 3, 4);     ALOGI("demoNativeService client connect, ret=%d", ret);

 

下面就是筆者測試的列印,如下:

 

# demdemoNativeServiceclient   demoNativeServiceserver   # demoNativeServiceserver &                                                    [2] 2332# --------- beginning of /dev/log/main02-19 17:10:57.890 E/HelloWorldService( 2332): demoNativeService created# # demdemoNativeServiceclient   demoNativeServiceserver   # demoNativeServiceclient                                                      02-19 17:11:02.520 I/demoNativeService/Service( 2334): demoNativeService client is now starting, pid=233402-19 17:11:02.520 I/demoNativeService/Service( 2334): IdemoNativeService client is now trying02-19 17:11:02.520 D/HelloWorldService( 2332): OnTransact(2,16)02-19 17:11:02.520 D/HelloWorldService( 2332): PrintString: Good man desheng02-19 17:11:02.520 D/HelloWorldService( 2332): printString: Good man desheng02-19 17:11:02.520 D/HelloWorldService( 2332): demoNativeService printString:Good man desheng02-19 17:11:02.520 I/demoNativeService/Service( 2334): demoNativeService client printString, ret=6602-19 17:11:02.520 D/HelloWorldService( 2332): OnTransact(1,16)02-19 17:11:02.520 D/HelloWorldService( 2332): CONNECT: 2334, 1, 2, 3, 402-19 17:11:02.520 D/HelloWorldService( 2332): demoNativeService connect:2334, 1, 2, 3, 402-19 17:11:02.520 I/demoNativeService/Service( 2334): demoNativeService client connect, ret=8802-19 17:11:02.520 I/demoNativeService/Service( 2334): Hello client is now exiting# # 02-19 17:11:07.540 D/InitAlarmsService( 2259): Clearing and rescheduling alarms.# service listFound 78 services:0ds.demonativeservice: [android.hardware.IdemoNativeService]1phone: [com.android.internal.telephony.ITelephony]2iphonesubinfo: [com.android.internal.telephony.IPhoneSubInfo]3simphonebook: [com.android.internal.telephony.IIccPhoneBook]4isms: [com.android.internal.telephony.ISms]5jeavoxmiddleware: [android.jeavox.IMiddleWareService]

 

寫到這,如果要給應用調用的話,還需要寫Client,JNI,JNI及以上在此不再討論了,我們就簡易來看看client怎麼處理吧,其實有點類似上面那個可執行檔的寫法,這裡可能就是有一個對象的概念,可以保持,大概如下:

 

demoNativeServiceClient::demoNativeServiceClient(){sp sm = defaultServiceManager();sp binder = sm->getService(String16("ds.demonativeservice"));mdemoNativeService = interface_cast(binder);}demoNativeServiceClient::~demoNativeServiceClient(){mdemoNativeService = NULL;}int32_t demoNativeServiceClient::connect(int previewhw,int intf,int fmt,int chan){return mdemoNativeService->connect(getCallingPid(),previewhw,intf,fmt,chan);}int32_t demoNativeServiceClient::printString(const char *str){   return mdemoNativeService->printString(str);}

 

羅哩羅嗦寫了這麼多,請大家拍磚,輕拍一下:)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.