首页 > 代码库 > Kivy A to Z -- 如何从Python创建一个基于Binder的Service及如何从Java访问Python创建的Service

Kivy A to Z -- 如何从Python创建一个基于Binder的Service及如何从Java访问Python创建的Service

      《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 = http://www.mamicode.com/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 = http://www.mamicode.com/Parcel.obtain();>
解释下:

因为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