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 = 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

上一篇:C++ Primer中文本查询示例Query的实现


下一篇:天天生鲜中关于app的配置问题