《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(&data),reinterpret_cast(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(data)
r = Parcel(reply)
service =
与此同时,我们定义了一个叫OnTransact的C函数,这个函数将会在PythonBBinder的onTransact被调用时被调用,而在这个函数里直接调用了Service的OnTransact函数,
Python代码里通过继承Service对象,并重新实现OnTransact函数,就可以达到处理从Python代码中处理onTransact的目的。
接下来看下python 的server端代码:
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)
最后来看下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!