《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 = 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,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端代码:

   
from binder import Service,Binder,Parcelclass 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 0DESCRIPTOR='sos_center'import binderprint binder.listServices()import sysprint sys.argvif 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 blocke.printStackTrace();} catch (NoSuchMethodException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalArgumentException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (InvocationTargetException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (RemoteException e) {// TODO Auto-generated catch blocke.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!



更多相关文章

  1. 没有一行代码,「2020 新冠肺炎记忆」这个项目却登上了 GitHub 中
  2. android - ui 研究,QQ登陆篇
  3. Android(安卓)中不同项目共用通用库Module方法
  4. Android源代码加入SDK,在程序中查看android源代码
  5. Android弹幕实现:基于B站弹幕开源系统(1)
  6. Android(安卓)Binder IPC分析
  7. Qt for Android之2048实现
  8. 推荐几个可提高开发效率的Android(安卓)studio插件
  9. Android操作JNI函数以及复杂对象传递

随机推荐

  1. Win10系统下MySQL8.0.16 压缩版下载与安
  2. MYSQL定时清除备份数据的具体操作
  3. Win10下免安装版MySQL8.0.16的安装和配置
  4. 解决当MySQL数据库遇到Syn Flooding问题
  5. MySQL数据库主从复制延时超长的解决方法
  6. 解决Node.js mysql客户端不支持认证协议
  7. mysql大批量插入数据的4种方法示例
  8. MySQL死锁套路之唯一索引下批量插入顺序
  9. MySQL找出未提交事务信息的方法分享
  10. MySQL中查询某一天, 某一月, 某一年的数