《Kivy A to Z -- 如何从python代码中直接访问Android的Service》 一文中讲到了如何从python访问java的service,这一篇再来讲下如何创建一个基于Binder的Python Service以及如何从Java代码中访问这个Python创建的Service。
先来看代码,再作下解释:
接《Kivy A to Z -- 如何从python代码中直接访问Android的Service》一文,我们在相关的文件中增加代码:
binder_wrap.cpp
using namespace android; class PythonBBinder : public BBinder { public: static status_t instantiate(const char *name,const char *descriptor,fnOnTransact onTrans,void *data); PythonBBinder(const char *name,const char *descriptor,fnOnTransact onTrans,void *data); virtual ~PythonBBinder(); virtual status_t onTransact(uint32_t code, const android::Parcel &data, android::Parcel *reply, uint32_t flags); private: android::String16 name; android::String16 descriptor; fnOnTransact mOnTransact; void *mData; }; status_t PythonBBinder::instantiate(const char *name,const char *descriptor,fnOnTransact onTrans,void *data) { if(name == NULL || descriptor == NULL) { return -1; } ProcessState::self()->startThreadPool(); return android::defaultServiceManager()->addService(String16(name),new PythonBBinder(name,descriptor,onTrans,data)); } PythonBBinder::PythonBBinder(const char *name,const char *descriptor,fnOnTransact onTrans,void *data) { LOGE("PythonBBinder created"); this->name = String16(name); this->descriptor = String16(descriptor); this->mOnTransact = onTrans; this->mData = data; } PythonBBinder::~PythonBBinder() { LOGE("PythonBBinder destroyed"); } android::status_t PythonBBinder::onTransact(uint32_t code, const android::Parcel &data, android::Parcel *reply, uint32_t flags) { LOGE("OnTransact(%u,%u)", code, flags); if (this->mOnTransact) { if (!data.enforceInterface(this->descriptor)) { return android::PERMISSION_DENIED; } return this->mOnTransact(code,reinterpret_cast<const void*>(&data),reinterpret_cast<void *>(reply),flags,this->mData); } else { return BBinder::onTransact(code, data, reply, flags); } return android::NO_ERROR; } int server_create(const char *name,const char *descriptor,fnOnTransact onTrans,void *data) { return PythonBBinder::instantiate(name,descriptor,onTrans,data); }
这里是对BBinder作了封装,fnOnTransact是一个回调函数类型,声明如下:
typedef int (*fnOnTransact)(uint32_t code,const void *data,void *reply,uint32_t flags,void *userData);
这个回调函数的作用是用于在服务端的onTransact被调用时,将消息的处理转到Python代码中去。
这里一定要注意了,千万不要忘记调用下面的代码:
android::ProcessState::self()->startThreadPool();
这个函数会创建一个用于接收客户端请求的线程,少调用了这行代码,客户的代码将会因得不到服务端的回应而不会返回。
接下来看在如何在Python代码中对server_create函数进行封装,这里依旧使用cython来封装C++代码:
binder.pyx
cdef extern from "binder_wrap.h": ctypedef int (*fnOnTransact)(uint32_t code,const void *data,void *reply,uint32_t flags,void *userData) int server_create(const char *name,const char *descriptor,fnOnTransact onTrans,void *data) ... cdef int OnTransact(uint32_t code,const void *data,void *reply,uint32_t flags,void *userData) with gil: d = Parcel(<unsigned int>data) r = Parcel(<unsigned int>reply) service = <object>userData return service.OnTransact(code,d,r,flags) class Service(object): def __init__(self,const char *name,const char *descriptor): Py_INCREF(self) server_create(name,descriptor,OnTransact,<void *>self) def OnTransact(self,code,data,reply,flags): return 0
这里,cython的易用性可以说是体现的淋漓尽至了,在Service类中,我们将Service对象作为server_create的data参数,该参数最终传给server_create函数创建的C++的PythonBBinder对象。
与此同时,我们定义了一个叫OnTransact的C函数,这个函数将会在PythonBBinder的onTransact被调用时被调用,而在这个函数里直接调用了Service的OnTransact函数,
Python代码里通过继承Service对象,并重新实现OnTransact函数,就可以达到处理从Python代码中处理onTransact的目的。
接下来看下python 的server端代码:
</pre><pre>
from binder import Service,Binder,Parcel class MyService(Service): RESCUE_SIGNAL=2 def __init__(self,name,descriptor): super(MyService,self).__init__(name,descriptor) def OnTransact(self,code,data,reply,flags): print '+++++++++++++++',code,data,reply,flags if code == MyService.RESCUE_SIGNAL: print data.readString16() reply.writeInt32(0) reply.writeString16(u'roger that!report your position.'); return 0 DESCRIPTOR='sos_center' import binder print binder.listServices() import sys print sys.argv if len(sys.argv) == 1: s = MyService('sos','sos_center') print 'sos service start' while True: import time time.sleep(1.0)
这里创建了一个叫sos的service,代码简单明了,不多说。
最后来看下Java端访问这个sos的代码:
try { Class<?> ServiceManager; ServiceManager = Class.forName("android.os.ServiceManager"); Method getService = ServiceManager.getMethod("getService", String.class); IBinder b = (IBinder)getService.invoke(ServiceManager, "sos"); Parcel data = Parcel.obtain(); data.writeInterfaceToken("sos_center"); Parcel reply = Parcel.obtain(); data.writeString("mayday!mayday!"); b.transact(2, data, reply, 0); reply.readException(); String r = reply.readString(); Log.i("Test",r); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); }
解释下:
因为ServiceManager是隐藏的类,不能直接访问,所以该示例用reflect的方法来访问ServiceManager,通过ServiceManager.getService来获取到“sos”的IBinder接口,再通过IBinder的transact函数来与Python创建的service进行通信。
运行python和java代码,在python服务端将收到如下的内容:
mayday!mayday!
在java客户端将收到如下的输出:
roger that!report your position.
最后,还是那两句话:
enjoy it!
have fun!
Kivy A to Z -- 如何从Python创建一个基于Binder的Service及如何从Java访问Python创建的Service