从零开始怎么写android native service?

Posted Jarry_le

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从零开始怎么写android native service?相关的知识,希望对你有一定的参考价值。

从零开始怎么写android native service

        Android service对于从事android开发的人都不是一个陌生的东西,很多人可能会觉得服务很简单。服务是简单,因为复杂的别人做了,所以才会觉得简单。我们先梳理一下服务的分类,首先有本地服务跟系统服务的区分,而在APP里写的服务大多就成为Java服务或者应用服务。

/****************************************************************************************************/
声明:本博内容均由http://blog.csdn.net/edsam49原创,转载请注明出处,谢谢!
/*****************************************************************************************************/

      做APP的人写个应用服务相对来说是最简单的,因为extends了一个service后几个简单的接口就可以跑起来了,写完这种服务可能也只是对服务一知半解,因为值钱的service类Google的人已经帮你写好了,这是你的福气为你带来了便利,当然也可能会麻痹你:),但是做APP的人会有能解决问题是首要任务了,有时间还是对它了解更清楚点比较好,在此不再讨论这个。

做设备做系统的人,经常可能会去写系统服务,也就是framework下面的服务,systemserver里面注册的服务,写这种服务一般来说比较少,只有做设备系统的才会这样干,才有机会有完成的系统代码,可以在里面自由遨游,笔者三年前写过一个,可以看看【自己动手从零开始写一个完整的android Servicehttp://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),代码如下:

[java] view plain copy
  1. class IdemoNativeService : public IInterface  
  2.   
  3. public:  
  4.     enum   
  5.         CONNECT = IBinder::FIRST_CALL_TRANSACTION,  
  6.         PRINTSTRING_CMD,  
  7.     ;  
  8.   
  9. public:  
  10.     DECLARE_META_INTERFACE(demoNativeService);  
  11.     virtual status_t connect(int pid,int previewhw,int intf,int fmt,int chan) = 0;  
  12.   virtual  status_t  printString(const char *str) = 0;  
  13.   
  14. ;  

      当然定义好了IdemoNativeService的头文件,就需要去实操了,先来搞定BpdemoNativeService,它的父类是BpInterface<IdemoNativeService>,这里面主要是涉及数据的跨进程用到的parcel,读啊,写啊,按套路来,也不难,也有AIDL工具可以使用,帮你转出来,再稍微修改一下就可以了,里面有一个很重要的remote,这个和remote就是幕后功臣啊,它保存了服务实例的对象啊,它是来之BpRefBase的一个成员,生成服务的时候,会得到赋值,定义完了以后,很重要的一个程序就是要IMPLEMENT_META_INTERFACE(demoNativeService,"android.hardware.IdemoNativeService");这个宏是非常重要的,跟前面那个DECLARE是对应的,前面声明,后面实现,当然我们带的参数跟的名字是必须一致的,这样才能正常沟通嘛!

[java] view plain copy
  1. class BpdemoNativeService: public BpInterface<IdemoNativeService>  
  2.   
  3. public:  
  4.     BpdemoNativeService(const sp<IBinder>& impl)  
  5.     : BpInterface<IdemoNativeService>(impl)  
  6.       
  7.       
  8.   
  9.     virtual status_t connect(int pid,int previewhw,int intf,int fmt,int chan)  
  10.       
  11.         Parcel data, reply;  
  12.         data.writeInterfaceToken(IdemoNativeService::getInterfaceDescriptor());  
  13.         data.writeInt32(pid);  
  14.         data.writeInt32(previewhw);  
  15.         data.writeInt32(intf);  
  16.         data.writeInt32(fmt);  
  17.         data.writeInt32(chan);  
  18.         remote()->transact(IdemoNativeService::CONNECT, data, &reply);  
  19.         return reply.readInt32();  
  20.       
  21.   
  22.   virtual  status_t  printString(const char *str)  
  23.     
  24.     Parcel data, reply;  
  25.     data.writeInterfaceToken(IdemoNativeService::getInterfaceDescriptor());  
  26.     data.writeCString(str);  
  27.     remote()->transact(IdemoNativeService::PRINTSTRING_CMD, data, &reply);  
  28.         return reply.readInt32();  
  29.     
  30. ;  
  31.   
  32. IMPLEMENT_META_INTERFACE(demoNativeService, "android.hardware.IdemoNativeService");//android.hardware.IdemoNativeService ds.demonativeservice  

       接着需要写服务stub了,BndemoNativeService的父类是BnInterface<IdemoNativeService>,有没有发现BndemoNativeService跟BpdemoNativeService,都会基于接口类IdemoNativeService,这样沟通起来的接口就唯一了,就具备了对话的可能;

[java] view plain copy
  1. class BndemoNativeService: public BnInterface<IdemoNativeService>  
  2.   
  3. public:  
  4.     virtual status_t onTransact( uint32_t code,const Parcel& data,Parcel* reply,uint32_t flags = 0);  
  5. ;  
  6. status_t BndemoNativeService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)  
  7.   
  8.     switch(code)  
  9.       
  10.         /*case CONNECT:  
  11.             CHECK_INTERFACE(IdemoNativeService, data, reply); 
  12.             int pid         = data.readInt32(); 
  13.             int previewhw   = data.readInt32(); 
  14.             int intf        = data.readInt32(); 
  15.             int fmt         = data.readInt32(); 
  16.             int chan        = data.readInt32(); 
  17.             reply->writeInt32(connect(pid,previewhw,intf,fmt,chan)); 
  18.             return NO_ERROR; 
  19.             break; 
  20.         case PRINTSTRING_CMD:  
  21.             CHECK_INTERFACE(IdemoNativeService, data, reply); 
  22.             const char *str; 
  23.       str = data.readCString(); 
  24.             reply->writeInt32(printString(str)); 
  25.             return NO_ERROR; 
  26.             break;*/  
  27.   
  28.         default:  
  29.             return BBinder::onTransact(code, data, reply, flags);  
  30.       
  31.   

      到这就轮到了大块头service实体demoNativeService了,demoNativeService是基于BndemoNativeService,在demoNativeService里面定义了一个instantiate()接口用于添加service到servicemanager里面去,注意demoNativeService()跟析构函数~demoNativeService()需要写成private的,免得别人可以new出对象来。在里面重写了onTransact,一旦BpdemoNativeService有风吹草动,就会联动到BndemoNativeService,因为服务实体重写了onTransact,所以实际就会先执行到demoNativeService::onTransact这里来,在这里面处理不了,可以再转给BpdemoNativeService的onTransact或者直接到BBinder的onTransact;

[cpp] view plain copy
  1. void demoNativeService::instantiate()   
  2.     android::defaultServiceManager()->addService(  
  3.                 IdemoNativeService::descriptor, new demoNativeService());  
  4.   
  5.   
  6. demoNativeService::demoNativeService()  
  7.   
  8.     ALOGE("demoNativeService created");  
  9.     mOpened = 1;  
  10.   
  11.   
  12. demoNativeService::~demoNativeService()  
  13.   
  14.     ALOGE("demoNativeService destroyed");  
  15.   
  16.   
  17.  status_t  demoNativeService::connect(int pid,int previewhw,int intf,int fmt,int chan)  
  18.       
  19.     ALOGD("demoNativeService connect:%d, %d, %d, %d, %d", pid, previewhw, intf, fmt, chan);  
  20.     return 88;  
  21.       
  22.   
  23.   
  24.  status_t  demoNativeService::printString(const char *str)  
  25.       ALOGD("demoNativeService printString:%s", str);  
  26.     return 66;   
  27.   
  28.        
  29. #if 1         
  30.  status_t demoNativeService::onTransact(uint32_t code,  
  31.                                                 const android::Parcel &data,  
  32.                                                 android::Parcel *reply,  
  33.                                                 uint32_t flags)  
  34.   
  35.         ALOGD("OnTransact(%u,%u)", code, flags);  
  36.           
  37.         switch(code)   
  38.         case CONNECT:   
  39.             CHECK_INTERFACE(IdemoNativeService, data, reply);  
  40.             int pid         = data.readInt32();  
  41.             int previewhw   = data.readInt32();  
  42.             int intf        = data.readInt32();  
  43.             int fmt         = data.readInt32();  
  44.             int chan        = data.readInt32();  
  45.               
  46.       ALOGD("CONNECT: %d, %d, %d, %d, %d\\n", pid,previewhw,intf,fmt,chan);  
  47.             reply->writeInt32(connect(pid,previewhw,intf,fmt,chan));  
  48.             return NO_ERROR;  
  49.             break;  
  50.                                   
  51.         case PRINTSTRING_CMD:   
  52.                 CHECK_INTERFACE(IdemoNativeService, data, reply);  
  53.                 const char *str;  
  54.                 str = data.readCString();  
  55.                 ALOGD("PrintString: %s\\n", str);  
  56.                 ALOGD("printString: %s\\n", str);  
  57.                                 reply->writeInt32(printString(str));  
  58.                 return NO_ERROR;  
  59.          break;  
  60.         default:  
  61.                 return BndemoNativeService::onTransact(code, data, reply, flags);  
  62.           
  63.   
  64.         return NO_ERROR;  
  65.   
  66. #endif  

      写完了服务,那我们就再写一个可执行文件来生成一下,里面startThreadPool生成线程池,然后再调用joinThreadPool来监听变化;

[cpp] view plain copy
  1.     sp<ProcessState> proc(ProcessState::self());  
  2.     sp<IServiceManager> sm = defaultServiceManager();  
  3. //    ALOGI("ServiceManager: %p", sm.get());  
  4.     demoNativeService::instantiate();  
  5.     ProcessState::self()->startThreadPool();  
  6.     IPCThreadState::self()->joinThreadPool();  

      写到这,可以说服务已经可以跑起来了,那我们怎么验证呢,最快的办法还是写一个可执行文件去测一下它的接口,看通没通就知道了;

[cpp] view plain copy
  1.     int ret= -1;  
  2.     int pid = IPCThreadState::self()->getCallingPid();  
  3.   
  4.  ALOGI("demoNativeService client is now starting, pid=%d", pid);  
  5.   
  6.   android::sp<android::IServiceManager> sm = android::defaultServiceManager();  
  7.   android::sp<android::IBinder> binder;  
  8.   android::sp<IdemoNativeService> shw;  
  9.   
  10.   do   
  11.           binder = sm->getService(android::String16("ds.demonativeservice"));  
  12.           if (binder != 0)  
  13.                   break;  
  14.           ALOGW("IdemoNativeService not published, waiting...");  
  15.           usleep(500000);  
  16.    while(true);   
  17.   
  18. ALOGI("IdemoNativeService client is now trying");  
  19.   
  20.  shw = android::interface_cast<IdemoNativeService>(binder);  
  21.  ret = shw->printString("Good man desheng");  
  22. ALOGI("demoNativeService client printString, ret=%d", ret);  
  23.    
  24.  ret = shw->connect(pid,1, 2, 3, 4);  
  25. ALOGI("demoNativeService client connect, ret=%d", ret);  

    下面就是笔者测试的打印,如下:

[plain] view plain copy
  1. # dem  
  2. demoNativeServiceclient   demoNativeServiceserver     
  3. # demoNativeServiceserver &                                                      
  4. [2] 2332  
  5. # --------- beginning of /dev/log/main  
  6. 02-19 17:10:57.890 E/HelloWorldService( 2332): demoNativeService created  
  7.   
  8. #   
  9. # dem  
  10. demoNativeServiceclient   demoNativeServiceserver     
  11. # demoNativeServiceclient                                                        
  12. 02-19 17:11:02.520 I/demoNativeService/Service( 2334): demoNativeService client is now starting, pid=2334  
  13. 02-19 17:11:02.520 I/demoNativeService/Service( 2334): IdemoNativeService client is now trying  
  14. 02-19 17:11:02.520 D/HelloWorldService( 2332): OnTransact(2,16)  
  15. 02-19 17:11:02.520 D/HelloWorldService( 2332): PrintString: Good man desheng  
  16. 02-19 17:11:02.520 D/HelloWorldService( 2332): printString: Good man desheng  
  17. 02-19 17:11:02.520 D/HelloWorldService( 2332): demoNativeService printString:Good man desheng  
  18. 02-19 17:11:02.520 I/demoNativeService/Service( 2334): demoNativeService client printString, ret=66  
  19. 02-19 17:11:02.520 D/HelloWorldService( 2332): OnTransact(1,16)  
  20. 02-19 17:11:02.520 D/HelloWorldService( 2332): CONNECT: 2334, 1, 2, 3, 4  
  21. 02-19 17:11:02.520 D/HelloWorldService( 2332): demoNativeService connect:2334, 1, 2, 3, 4  
  22. 02-19 17:11:02.520 I/demoNativeService/Service( 2334): demoNativeService client connect, ret=88  
  23. 02-19 17:11:02.520 I/demoNativeService/Service( 2334): Hello client is now exiting  
  24. #   
  25. # 02-19 17:11:07.540 D/InitAlarmsService( 2259): Clearing and rescheduling alarms.  
  26.   
  27. # service list  
  28. Found 78 services:  
  29. 0   ds.demonativeservice: [android.hardware.IdemoNativeService]  
  30. 1   phone: [com.android.internal.telephony.ITelephony]  
  31. 2   iphonesubinfo: [com.android.internal.telephony.IPhoneSubInfo]  
  32. 3   simphonebook: [com.android.internal.telephony.IIccPhoneBook]  
  33. 4   isms: [com.android.internal.telephony.ISms]  
  34. 5   jeavoxmiddleware: [android.jeavox.IMiddleWareService]  

    写到这,如果要给应用调用的话,还需要写Client,JNI,JNI及以上在此不再讨论了,我们就简易来看看client怎么处理吧,其实有点类似上面那个可执行文件的写法,这里可能就是有一个对象的概念,可以保持,大概如下:

[cpp] view plain copy
  1. demoNativeServiceClient::demoNativeServiceClient()  
  2.   
  3.     sp<IServiceManager> sm = defaultServiceManager();  
  4.     sp<IBinder> binder = sm->getService(String16("ds.demonativeservice"));  
  5.     mdemoNativeService = interface_cast<IdemoNativeService>(binder);  
  6.   
  7.   
  8. demoNativeServiceClient::~demoNativeServiceClient()  
  9.   
  10.     mdemoNativeService = NULL;  
  11.   
  12.   
  13. int32_t demoNativeServiceClient::connect(int previewhw,int intf,int fmt,int chan)  
  14.   
  15.     return mdemoNativeService->connect(getCallingPid(),previewhw,intf,fmt,chan);  
  16.   
  17.   
  18. int32_t demoNativeServiceClient::printString(const char *str)  
  19.   
  20.    return mdemoNativeService->printString(str);  
  21.   

     罗哩罗嗦写了这么多,请大家拍砖,轻拍一下:)

以上是关于从零开始怎么写android native service?的主要内容,如果未能解决你的问题,请参考以下文章

深入浅出React Native 3: 从零开始写一个Hello World

原生项目如何从零开始集成 React Native

从零开始学习React Native开发

从零开始学习React Native开发

30天React Native从零到IOS/Android双平台发布总结

React native 从零开始