從零開始怎麼寫androidnativeservice?
從零開始怎麼寫android native service
Android service對於從事android開發的人都不是一個陌生的東西,很多人可能會覺得服務很簡單。服務是簡單,因為複雜的別人做了,所以才會覺得簡單。我們先梳理一下服務的分類,首先有本地服務跟系統服務的區分,而在APP裡寫的服務大多就成為Java服務或者應用服務。
/****************************************************************************************************/
宣告:本博內容均由http://blog.csdn.net/edsam49原創,轉載請註明出處,謝謝!
/*****************************************************************************************************/
做APP的人寫個應用服務相對來說是最簡單的,因為extends了一個service後幾個簡單的介面就可以跑起來了,寫完這種服務可能也只是對服務一知半解,因為值錢的service類Google的人已經幫你寫好了,這是你的福氣為你帶來了便利,當然也可能會麻痺你:),但是做APP的人會有能解決問題是首要任務了,有時間還是對它瞭解更清楚點比較好,在此不再討論這個。
做裝置做系統的人,經常可能會去寫系統服務,也就是framework下面的服務,systemserver裡面註冊的服務,寫這種服務一般來說比較少,只有做裝置系統的才會這樣幹,才有機會有完成的系統程式碼,可以在裡面自由遨遊,筆者三年前寫過一個,可以看看【自己動手從零開始寫一個完整的android Service】http://blog.csdn.net/edsam49/article/details/8163639
那剩下的一個是本地服務,也就是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<IdemoNativeService>,這裡面主要是涉及資料的跨程式用到的parcel,讀啊,寫啊,按套路來,也不難,也有AIDL工具可以使用,幫你轉出來,再稍微修改一下就可以了,裡面有一個很重要的remote,這個和remote就是幕後功臣啊,它儲存了服務例項的物件啊,它是來之BpRefBase的一個成員,生成服務的時候,會得到賦值,定義完了以後,很重要的一個程式就是要IMPLEMENT_META_INTERFACE(demoNativeService,”android.hardware.IdemoNativeService”);這個巨集是非常重要的,跟前面那個DECLARE是對應的,前面宣告,後面實現,當然我們帶的引數跟的名字是必須一致的,這樣才能正常溝通嘛!
class BpdemoNativeService: public BpInterface<IdemoNativeService> { public: BpdemoNativeService(const sp<IBinder>& impl) : BpInterface<IdemoNativeService>(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<IdemoNativeService>,有沒有發現BndemoNativeService跟BpdemoNativeService,都會基於介面類IdemoNativeService,這樣溝通起來的介面就唯一了,就具備了對話的可能;
class BndemoNativeService: public BnInterface<IdemoNativeService> { 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 ", 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 ", str); ALOGD("printString: %s ", str); reply->writeInt32(printString(str)); return NO_ERROR; } break; default: return BndemoNativeService::onTransact(code, data, reply, flags); } return NO_ERROR; } #endif
寫完了服務,那我們就再寫一個可執行檔案來生成一下,裡面startThreadPool生成執行緒池,然後再呼叫joinThreadPool來監聽變化;
sp<ProcessState> proc(ProcessState::self()); sp<IServiceManager> 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<android::IServiceManager> sm = android::defaultServiceManager(); android::sp<android::IBinder> binder; android::sp<IdemoNativeService> 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<IdemoNativeService>(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);
下面就是筆者測試的列印,如下:
# dem demoNativeServiceclient demoNativeServiceserver # demoNativeServiceserver & [2] 2332 # --------- beginning of /dev/log/main 02-19 17:10:57.890 E/HelloWorldService( 2332): demoNativeService created # # dem demoNativeServiceclient demoNativeServiceserver # demoNativeServiceclient 02-19 17:11:02.520 I/demoNativeService/Service( 2334): demoNativeService client is now starting, pid=2334 02-19 17:11:02.520 I/demoNativeService/Service( 2334): IdemoNativeService client is now trying 02-19 17:11:02.520 D/HelloWorldService( 2332): OnTransact(2,16) 02-19 17:11:02.520 D/HelloWorldService( 2332): PrintString: Good man desheng 02-19 17:11:02.520 D/HelloWorldService( 2332): printString: Good man desheng 02-19 17:11:02.520 D/HelloWorldService( 2332): demoNativeService printString:Good man desheng 02-19 17:11:02.520 I/demoNativeService/Service( 2334): demoNativeService client printString, ret=66 02-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, 4 02-19 17:11:02.520 D/HelloWorldService( 2332): demoNativeService connect:2334, 1, 2, 3, 4 02-19 17:11:02.520 I/demoNativeService/Service( 2334): demoNativeService client connect, ret=88 02-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 list Found 78 services: 0 ds.demonativeservice: [android.hardware.IdemoNativeService] 1 phone: [com.android.internal.telephony.ITelephony] 2 iphonesubinfo: [com.android.internal.telephony.IPhoneSubInfo] 3 simphonebook: [com.android.internal.telephony.IIccPhoneBook] 4 isms: [com.android.internal.telephony.ISms] 5 jeavoxmiddleware: [android.jeavox.IMiddleWareService]
寫到這,如果要給應用呼叫的話,還需要寫Client,JNI,JNI及以上在此不再討論了,我們就簡易來看看client怎麼處理吧,其實有點類似上面那個可執行檔案的寫法,這裡可能就是有一個物件的概念,可以保持,大概如下:
demoNativeServiceClient::demoNativeServiceClient() { sp<IServiceManager> sm = defaultServiceManager(); sp<IBinder> binder = sm->getService(String16("ds.demonativeservice")); mdemoNativeService = interface_cast<IdemoNativeService>(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); }
羅哩羅嗦寫了這麼多,請大家拍磚,輕拍一下:)
相關文章
- 從零開始寫一個ExporterExport
- 從零開始寫JavaScript框架(二)JavaScript框架
- 從零開始寫JavaScript框架(一)JavaScript框架
- 從零開始仿寫一個抖音App——開始APP
- 從零開始編寫指令碼引擎指令碼
- 從零開始寫一個網頁網頁
- 從零開始手寫Koa2框架框架
- 如何從零開始寫一個網站網站
- Python要怎麼開始(零)Python
- 【React技術棧】從零開始手寫reduxReactRedux
- 從零開始寫一個node爬蟲(一)爬蟲
- 從零開始編寫一個babel外掛Babel
- 從零開始寫一個Javascript解析器JavaScript
- 【Java EE】從零開始寫專案【總結】Java
- 從零開始寫Java Web框架——maven 外掛JavaWeb框架Maven
- 從零開始編寫自己的JavaScript框架(一)JavaScript框架
- 從零開始編寫自己的JavaScript框架(二)JavaScript框架
- 從零開始機器學習機器學習
- 從零開始 OpenCVOpenCV
- 從零開始,我是怎麼做出銷售合同管理系統?
- 從零開始:用REACT寫一個格鬥遊戲(一)React遊戲
- 從零開始:用REACT寫一個格鬥遊戲(二)React遊戲
- 從零開始寫一個微前端框架-沙箱篇前端框架
- 從零開始機器學習--05機器學習
- 從零開始機器學習-03機器學習
- 從零開始機器學習--4機器學習
- flutter 從零開始-1Flutter
- 從零開始手寫一個微前端框架-渲染篇前端框架
- java 從零開始手寫 RPC (04) -序列化JavaRPC
- 從零開始寫 Docker(十二)---實現 mydocker stop 停止容器Docker
- 從零開始寫 Docker(五)---基於 overlayfs 實現寫操作隔離Docker
- 從零開始認識 SparkSpark
- 從零開始的 Flutter 動畫Flutter動畫
- 從零開始--webpack 4 配置Web
- 從零開始學習KafkaKafka
- 從零開始學習機器學習機器學習
- 【ROS】從零開始學ROSROS
- 從零開始學習laravelLaravel