精华内容
参与话题
问答
  • 混合编程

    2014-01-07 21:07:58
    在嵌入式系统开发中,目前使用的主要编程语言是C和汇编,C++已经有相应的编译器,但是现在使用还是比较少的。在稍大规模的嵌入式软件中,例如含有OS,大部分的代码都是用C编写的,主要是因为C语言的结构比较好,便于...

        在嵌入式系统开发中,目前使用的主要编程语言是C和汇编,C++已经有相应的编译器,但是现在使用还是比较少的。在稍大规模的嵌入式软件中,例如含有OS,大部分的代码都是用C编写的,主要是因为C语言的结构比较好,便于人的理解,而且有大量的支持库。尽管如此,很多地方还是要用到汇编语言,例如开机时硬件系统的初始化,包括CPU状态的设定,中断的使能,主频的设定,以及RAM的控制参数及初始化,一些中断处理方面也可能涉及汇编。另外一个使用汇编的地方就是一些对性能非常敏感的代码块,这是不能依靠C编译器的生成代码,而要手工编写汇编,达到优化的目的。而且,汇编语言是和CPU的指令集紧密相连的,作为涉及底层的嵌入式系统开发,熟练对应汇编语言的使用也是必须的。
        单纯的C或者汇编编程请参考相关的书籍或者手册,这里主要讨论C和汇编的混合编程,包括相互之间的函数调用。下面分四种情况来进行讨论,暂不涉及C++。
    1. 在C语言中内嵌汇编
    在C中内嵌的汇编指令包含大部分的ARM和Thumb指令,不过其使用与汇编文件中的指令有些不同,存在一些限制,主要有下面几个方面:
    a.不能直接向PC寄存器赋值,程序跳转要使用B或者BL指令
    b.在使用物理寄存器时,不要使用过于复杂的C表达式,避免物理寄存器冲突
    c.R12和R13可能被编译器用来存放中间编译结果,计算表达式值时可能将R0到R3、R12及R14用于子程序调用,因此要避免直接使用这些物理寄存器
    d.一般不要直接指定物理寄存器,而让编译器进行分配
    内嵌汇编使用的标记是 __asm或者asm关键字,用法如下:
    __asm
    {
           instruction [; instruction]
           …
           [instruction]
    }
    asm(“instruction [; instruction]”);
    下面通过一个例子来说明如何在C中内嵌汇编语言,
    #include <stdio.h>
    void my_strcpy(const char *src, char *dest)
    {
           char ch;
           __asm
           {
                  loop:
                  ldrb       ch, [src], #1
                  strb       ch, [dest], #1
                  cmp        ch, #0
                  bne         loop
           }
    }
    int main()
    {
           char *a = "forget it and move on!";
           char b[64];
           my_strcpy(a, b);
           printf("original: %s", a);
           printf("copyed:   %s", b);  
           return 0;
    }
    在这里C和汇编之间的值传递是用C的指针来实现的,因为指针对应的是地址,所以汇编中也可以访问。
    2. 在汇编中使用C定义的全局变量
    内嵌汇编不用单独编辑汇编语言文件,比较简洁,但是有诸多限制,当汇编的代码较多时一般放在单独的汇编文件中。这时就需要在汇编和C之间进行一些数据的传递,最简便的办法就是使用全局变量。
    /*    cfile.c
    *    定义全局变量,并作为主调程序
    */
    #include <stdio.h>
    int gVar_1 = 12;
    extern        asmDouble(void);
    int main()
    {
           printf("original value of gVar_1 is: %d", gVar_1);
           asmDouble();
           printf("       modified value of gVar_1 is: %d", gVar_1);
           return 0;
    }
           对应的汇编语言文件
    ;called by main(in C),to double an integer, a global var defined in C
    is used.
           AREA asmfile, CODE, READONLY
           EXPORT       asmDouble
           IMPORT   gVar_1
    asmDouble
           ldr r0, =gVar_1
           ldr          r1, [r0]
           mov        r2, #2      
           mul         r3, r1, r2
           str          r3, [r0]
           mov        pc, lr
           END
    3. 在C中调用汇编的函数
    在C中调用汇编文件中的函数,要做的主要工作有两个,一是在C中声明函数原型,并加extern关键字;二是在汇编中用EXPORT导出函数名,并用该函数名作为汇编代码段的标识,最后用mov pc, lr返回。然后,就可以在C中使用该函数了。从C的角度,并不知道该函数的实现是用C还是汇编。更深的原因是因为C的函数名起到表明函数代码起始地址的左右,这个和汇编的label是一致的。
    /* cfile.c
    * in C,call an asm function, asm_strcpy
    *       Sep 9, 2004
    */
    #include <stdio.h>
    extern void asm_strcpy(const char *src, char *dest);
    int main()
    {
           const        char *s = "seasons in the sun";
           char        d[32];
           asm_strcpy(s, d);
           printf("source: %s", s);
           printf("       destination: %s",d);
           return 0;
    }
    ;asm function implementation
           AREA asmfile, CODE, READONLY
           EXPORT asm_strcpy
    asm_strcpy
    loop
           ldrb          r4, [r0], #1       ;address increment after read
           cmp         r4, #0
           beq           over
           strb          r4, [r1], #1
           b               loop
    over
           mov           pc, lr
           END
           在这里,C和汇编之间的参数传递是通过ATPCS(ARM Thumb Procedure Call Standard)的规定来进行的。简单的说就是如果函数有不多于四个参数,对应的用R0-R3来进行传递,多于4个时借助栈,函数的返回值通过R0来返回。
    4. 在汇编中调用C的函数
    在汇编中调用C的函数,需要在汇编中IMPORT 对应的C函数名,然后将C的代码放在一个独立的C文件中进行编译,剩下的工作由连接器来处理。
    ;the details of parameters transfer comes from ATPCS
    ;if there are more than 4 args, stack will be used
           EXPORT asmfile
           AREA asmfile, CODE, READONLY
           IMPORT   cFun
           ENTRY
           mov        r0, #11
           mov        r1, #22
           mov        r2, #33
           BL       cFun
           END
    /*C file, called by asmfile */
    int       cFun(int a, int b, int c)
    {
           return a + b + c;
    }
           在汇编中调用C的函数,参数的传递也是通过ATPCS来实现的。需要指出的是当函数的参数个数大于4时,要借助stack,具体见ATPCS规范 


    展开全文
  • 精通matlab7.0混合编程.

    2015-06-30 09:21:20
    3.3 几种常见的混合编程方法简介 3.3.1 使用matlab自带的matlab compiler 3.3.2 利用matlab引擎 3.3.3 利用activex控件 3.3.4 利用mat文件 3.3.5 c-mex 3.3.6 利用mideva/matcom 3.3.7 利用matrix[lib]实现混合编程 ...
  • MATLAB混合编程与工程应用.pdf

    热门讨论 2010-05-19 10:32:48
    Matlab混合编程的原因 1. 可以实现对已有代码的重用; 2. 合理使用开发组资源,进行高效开发; 3. 提供多种发布形式,方便发布; 4. 利用其它编程语言,提供程序的执行效率; MATLAB 混合编程与工程应用 目 录 第1 ...
  • 1、混合编程的基本方式C 语言与汇编语言混合编程通常有两种基本方法:在C 语言中嵌入汇编程序和在C 语言中调用汇编程序。1.1 在C51 中嵌入汇编程序在C51 中嵌入汇编程序主要用于实现延时或中断处理,以便生成精练的...
  • 精通MATLAB70混合编程-matlab7x.rar 系统地介绍MATLAB 7.0的混合编程方法和技巧。全书共分为13章。第1章和第2章介绍MATLAB的基础知识,第3章简要介绍MATLAB混合编程,第4章至第9章分别介绍几种典型的混合编程方法...
  • MATLAB/C++混合编程编程入门。希望对需要的朋友有帮助。最近在进行c++与matlab的混合编程,急切需要进行学习。希望有相关需求的朋友和我进行技术交流。我的邮箱:12506133056@qq.com。
  • 介绍了VB与MATLAB混合编程的方法,二者结合可以充分利用VB的方便快捷和MATLAB软件工具箱的强大功能。 关键词: VB VC++ MATLAB DLL 编译 MATLAB Add-in ...
  • 我想利用Matlab和C混合编程,然后目前是照着知乎上一个回答的方法来做的,链接如下: ... 但是我按照他的步骤来做以后还是有问题。我的IDE是vs2015和matlab2016a ...现在的问题是,我每次打开VS在调试里面点了附加...
  • MATLAB和C#混合编程

    2018-04-02 15:17:29
    C#和MatLab的混合编程,充分利用了winform的直观显示和matlab的强大计算能力。本文主要对项目中遇到的一些问题进行总结。希望达到的目的是使一个Matlab与C#混合编程的小白能够通过本文的步骤,Step by Step,顺利将...
  • 全书共分11章,第1和第2章概述了 MATLAB混合编程的发展状况及基本方法,第3至10章分别介绍了几种典型的混合编程方法,包括使用MEX、MATLAB Engine、MATLAB ACTIVEX、Mideva、MATLAB Builder for .NET、MATLAB Web ...
  • 摘要:Matlab具有很强的数值计算和分析等能力,而C/C++是目前最为流行的高级程序设计语言,两者互补结合的混合编程在科学研究和工程实践中具有非常重要的意义。从Matlab调用C/C++代码及C/C++调用m文件两方面,深入地...
  • vc与MATLAB混合编程研究-VC与Matlab混合编程的研究与实现.pdf vc与MATLAB混合编程研究,可以看看,不错
  • MATLAB与VB混合编程方式教程-MATLAB与VB混合编程方式简易教程.doc MATLAB与VB混合编程方式简易教程
  • GPU与MATLAB混合编程 PDF

    2019-02-27 11:36:44
    GPU与MATLAB混合编程 PDF 书籍 本书介绍CPU和MATLAB的联合编程方法,包括首先介绍了不使用GPU实现MATLAB加速的方法;然后介绍了MATLAB和计算统一设备架(CUDA)配置通过分析进行zuiyou规划,以及利用c-mex进行CUDA...
  • Qt与Matlab混合编程

    2018-01-06 22:12:14
    本文主要讲述以Qt做主语言,MATLAB做数学计算,并将计算的结果生成相应的库文件或者直接生成一个可执行文件给Qt调用,以此来实现Qt与MATLAB混合编程,资源包含详细的解释和代码以供参考。
  • 搭建java和matlab混合编程的环境要求,因为网上博客写的比较杂,而且win10上和win7、win8上环境搭建有些不同,故记录操作步骤。对于Java和matlab混合编程可见http://blog.csdn.net/ipad_li/article/details/44516089...
  • Labview与Matlab混合编程

    2011-01-13 11:50:53
    结合实例详细介绍labview通过ActiveX自动化技术与Matlab进行混合编程,达到了利 用Matlab优化算法库的目的。 将Labview与Matlab有机结合,是一条开发智能虚拟仪器的有效途径。
  • C#与Matlab2014a混合编程

    2015-07-09 16:09:27
    为了实现C#与Matlab混合编程,费了好大劲才实现生成的DLL文件能够正常调用,而不出现调用组件报错“初始值设定项引发异常”的问题。网上有很多资源可供参考,可惜参差不齐,特把解决过程记录,方便后面想学习的朋友...
  • matlab混合编程向导

    千次阅读 2012-04-09 14:40:12
    matlab混合编程向导(vc,vb,.net...)   一.matlab与vc混编 1.通过mcc将matlab的m文件转化为cpp,c文件或dll供vc调用:  这方面的实现推荐精华区Zosco和ljw总结的方法(x-6-1-4-3-1和2)  vc的设置请参看...
    
    

     

    一.matlab与vc混编
    1.通过mcc将matlab的m文件转化为cpp,c文件或dll供vc调用:
       这方面的实现推荐精华区Zosco和ljw总结的方法(x-6-1-4-3-1和2)
       vc的设置请参看精华区x-6-1-4-3-5,通过这种设置可以调用c math library和c++
       math library,C++ mathlib的pdf在精华区x-6-1-10里
    2.通过matlab的combuilder将m文件做成com组件,供vc调用
       参看精华区x-6-1-5
    3.通过matcom:
       这个mathworks公司已经不在出新版本了,而且他的功能可能用第1和第2种方法代替
       推荐使用前两种方式,matcom的使用精华区有一部分,从x-6-1-4下找

    二.matlab与vb混编
    1.mathworks公司专为vb开发了一个com组件:matrixvb,里面有大量的数学函数.
       这个组件需要另装,请在安装盘上或网上下载,注册码参看精华区x-6-1-3-8
       装好这个组件后,他自带了两个pdf文件,用法及其能用的函数全在上面,推荐阅读
       这两个pdf文件,精华区的位置是x-6-1-4-6
    2.通过matlab的combuilder将m文件做成com组件,供vb调用
       参看精华区x-6-1-5

    三.matlab与.net混编
       在6.1版本以前一直没有这方面的讨论,用mcc编成dll或cpp,c文件应该可以,由于
       .net调用com组件很方便,推荐使用通过matlab的combuilder将m文件做成com组件,供
       .net调用,参看精华区x-6-1-5

    四.试试通过matlab的combuilder做成com组件的方式实现混编,这也是mathworks公司
    推荐的方法,他不受语言和编译器的局限,相信高版本的matlab将在这方面有更大的改进
    这方面的介绍参看精华区x-6-1-5

    五.其他的语言或编译器与matlab混编
        精华区x-6-1-4里有一些相关的介绍,不全,还请用过的大侠总结这方面的内容。
    如果此编译器能调用com组件,那么也能通过matlab的combuilder将m文件做成com组件,
    供他们调用的方法

    六.matlab调用外部dll(动态链接库等)
    参看精华区x-6-1-4-9

    七.混编的一些常见问题FAQ
    1.mcc编译m文件出现这样的错误提示(例):
    Error: File "psf3" is a script M-file and cannot be compiled with the
    current Compiler.
    matlab complier不能编译script M-file,请将script M-file转成function m-file
    最简单的一个转法是:如果你的m文件叫a.m,在开头加上一句: function a
    推荐精华区x-6-1-4-12文章
    2.mxArray,mwArray转double或其他的类型
    参看精华区x-6-1-4-3-7和8
    3.在vc下调用toolbox中的有些函数会出现问题
    参看精华区x-6-1-4-3-9,这种问题还没有很好的解决方法
    4.lib /def:libmmfile.def /out:libmmfile.lib /machine:ix86是什么意思?
    这是调用VC的编译器lib.exe,从def文件导出lib文件,以实现对dll的调用
    这是dos下的命令行。matlab的高版本已经不需要这么用了,它的lib文件全在
    <matlabroot>/extern/lib/win32里,根据各个编译器的不同,此文件夹里还有分类的
    文件夹。
    5.我在Vc中选择新建一个matlab工程,可是最后一步确定的时候总是弹出一个窗口说 
    MSDEV.EXE应用程序错误."0x528aa3003指令” 
    引用的“0x52880000"内存.该内存不能为”written“?
    这种情况是matlab的addin不能用,没有太好的解决方法,所以只能改用mcc
    来代替addin了
    6.matlab下生成的exe文件,怎么拿到没有matlab的机器上运行?

    展开全文
  • 混合编程接口规范

    2017-10-14 14:55:28
    操作系统中C和汇编的混合编程接口规范

    一般地,在Linux或者其他操作系统中,一个模块都有一个总入口和多个具体的接口集,例如驱动、中间件等。上层应用调用总入口,并传递具体接口对应的命令号cmd和具体的接口参数,总入口负责分发调用具体的接口来实现。那么,总入口会有以下声明形式:

    Drv_entry(uint8_t cmd, void*,void*,void*);

    Drv_entry(void*, void*,void*, uint8_tcmd);

    两者的不同,就在于cmd的位置不同,那么两者哪种声明比较好呢?如果你能立刻做出正确的选择,那么你的嵌入式软件功底是蛮不错的。如果不能,那说明你还需要好好努力。

    这个问题跟ABI和编译优化有关,可以先思考,答案放文章最后。

    一、ABI

    API是应用编程接口,是高级语言接口。而ABI是应用二进制编程接口,在C和汇编语言混合编程中必须要非常熟练,尤其在操作系统的定制和优化里面常常需要注意和运用。纯C编程虽然可以不用在意ABI,但不熟悉会造成程序性能下降。

    ABI要解决的问题包括:1.参数如何传递和返回,不同的参数类型,一个或者多个传参 2. 参数传递过程中对栈、寄存器的利用。

    ABI是一种编译约定,并不是CPU对软件的强制规定。有一个寄存器例外,就是PC寄存器。编译器必须要使用CPU指定的寄存器作为PC寄存器。

    二、ATPCS

    ARM体系的ABI。

    2.1寄存器约定:

    1. 子程序通过寄存器R0~R3来传递参数. 这时寄存器可以记作: A1~A4 , 被调用的子程序在返回前无需恢复寄存器R0~R3的内容.

    2. 在子程序中,使用R4~R11来保存局部变量.这时寄存器R4~R11可以记作:V1~V8 .如果在子程序中使用到V1~V8的某些寄存器,子程序进入时必须保存这些寄存器的值,在返回前必须恢复这些寄存器的值,对于子程序中没有用到的寄存器则不必执行这些操作.在THUMB程序中,通常只能使用寄存器R4~R7来保存局部变量.

      3.寄存器R12用作子程序间scratch寄存器,记作ip; 在子程序的连接代码段中经常会有这种使用规则.

      4.寄存器R13用作数据栈指针,记做SP,在子程序中寄存器R13不能用做其他用途. 寄存器SP在进入子程序时的值和退出子程序时的值必须相等.

    5.寄存器R14用作连接寄存器,记作lr ; 它用于保存子程序的返回地址,如果在子程序中保存了返回地址,则R14可用作其它的用途.

      6.寄存器R15是程序计数器,记作PC ; 它不能用作其他用途.

    2.2参数的传递规则.

    1.参数个数可变的子程序参数传递规则

      对于参数个数可变的子程序,当参数不超过4个时,可以使用寄存器R0~R3来进行参数传递,当参数超过4个时,还可以使用数据栈来传递参数. 在参数传递时,将所有参数看做是存放在连续的内存单元中的字数据。然后,依次将各名字数据传送到寄存器R0,R1,R2,R3; 如果参数多于4个,将剩余的字数据传送到数据栈中,入栈的顺序与参数顺序相反,即最后一个字数据先入栈. 按照上面的规则,一个浮点数参数可以通过寄存器传递,也可以通过数据栈传递,也可能一半通过寄存器传递,另一半通过数据栈传递.

     2.参数个数固定的子程序参数传递规则

      对于参数个数固定的子程序,参数传递与参数个数可变的子程序参数传递规则不同,如果系统包含浮点运算的硬件部件,浮点参数将按照下面的规则传递: 各个浮点参数按顺序处理;为每个浮点参数分配FP寄存器;分配的方法是,满足该浮点参数需要的且编号最小的一组连续的FP寄存器.第一个整数参数通过寄存器R0~R3来传递,其他参数通过数据栈传递.

    2.3参数返回规则

      1.结果为一个32位的整数时,可以通过寄存器R0返回.

      2.结果为一个64位整数时,可以通过R0和R1返回,依此类推.

      3.结果为一个浮点数时,可以通过浮点运算部件的寄存器f0,d0或者s0来返回. 4.结果为一个复合的浮点数时,可以通过寄存器f0-fN或者d0~dN来返回. 5.对于位数更多的结果,需要通过调用内存来传递.

    三、MIPS ABI

           MIPS体系ABI

    3.1寄存器约定

     

    3.2传参约定

    1. 参数从左到右存放在a0,a1,a2,a3寄存器中,返回值存放在v0和v1寄存器中。

    2. 每个参数不论是字还是字节都用一个寄存器来传,自动进行0扩展或者有符号扩展。如果是数据结构传递需要模拟内存的存放,而不是一个字节也用一个寄存器来放。传递数据结构需要进行汇编的调试以保证正确。GL5110不允许传递数据结构,应该传递数据结构的指针。GL5110不允许使用浮点数。

    3. 当参数个数超过4个时,用栈空间进行传递。事实上,a0,a1,a2,a3表示的参数在栈中也有对应的16字节空间。(是不是有点太浪费栈了...)

    4. 返回32位数在v0,64位时为v0和v1。

    四、51体系ABI

           有兴趣可以自己编程并切换到汇编模式看看。实践得真知。

    五、开篇的答案

           有了上面的提示,应该大致知道原因了,第二种声明才是合理的。

           既然是总入口,那其会根据CMD来进行分发,大致是这样:

           Drv_entry(void*A,void*B,void*C, uint8_t cmd)

    {

           Switch(cmd):

                  Case CMD1: cmd1(A, B, C);break;

                  Case CMD2: cmd2(A, B, C);break;

                  …

    }

           可以看出,总入口和调用具体接口的A、B、C参数顺序是一样的,因此其在寄存器上的传递安排也是一样的。如果使用编译优化,那上层应用调用总入口之后,A, B, C已经在对应的寄存器上,分发调用具体接口时不需要进行改变。如果将cmd作为第一个参数,那分发调用时需要调整A、B、C所在的寄存器。至于A、B、C在哪个寄存器,不同体系的ABI有不同的约定,看看上面就清楚了。

    关注微信公众号:嵌入式企鹅圈,获得上百篇物联网原创技术分享!


     

    展开全文
  • Android与Python混合编程

    万次阅读 多人点赞 2019-09-01 00:33:48
    早在2017年的时候,出于业余兴趣,我就开始研究关于Python移植到Android上的实现方案,我一直希望能实现Android与Python的混合编程,并为此写了一系列博客,我希望借助JNI技术,实现Java与Python的交互。或许是出于...

    前言

    早在2017年的时候,出于业余兴趣,我就开始研究关于Python移植到Android上的实现方案,我一直希望能实现Android与Python的混合编程,并为此写了一系列博客,我希望借助JNI技术,实现Java与Python的交互。或许是出于上班忙,时间少,精力有限,人的惰性等等原因,一直没有实现一套框架,降低Android与Python混编的难度,做到尽可能封装C语言代码,让使用者无需掌握NDK开发,C语言编程等。原理是早已走通了,剩下的就是苦力活,写C代码,写JNI代码,对接口一一封装。

    现在终于不用遗憾了,因为已经有人做了我一直想做的事,而且是以我想要的思路。我一直关注着Android与Python混合编程的信息,当我看到Chaquopy框架时,真的难掩的开心,比我自己实现的还要开心!

    如果有人想探寻Android与Python的混编的原理与实现,那我之前的写的博客还能派上一点用场

    Android 平台的Python——基础篇(一)

    Android 平台的Python——基础篇(一)

    Android 平台的Python——JNI方案(二)

    Android 平台的Python——JNI方案(二)

    Android 平台的Python——CLE方案实现(三)

    Android 平台的Python——CLE方案实现(三)

    Android 平台的Python——第三方库移植

    Android 平台的Python——第三方库移植

    Android 平台的Python——编译Python解释器

    Android 平台的Python——编译Python解释器

    Chaquopy是什么?

    简单的直观的解释,它是在Android Studio中基于Gradle的构建系统实现的一个插件。它可以帮助我们用最简便的方式实现Android技术与Python混合编程。甚至对于Python的忠实拥趸来说,可以完全使用Python语言开发一个apk,基本不用写Java代码。

    实际上Chaquopy并不仅仅是一个插件那么简单,它是一套框架。gradle插件这部分只是用来打包apk的而已

    基础用法-快速入门

    首先使用Android studio创建一个hello工程,快速编写代码感受一下

    请先确保你当前电脑上的Python环境可用,Chaquopy是根据当前电脑上的Python版本来选择集成对应的版本解释器到apk中的。如你的电脑上有多个Python版本,可通过配置明确指定对应的版本

    defaultConfig {
        python {
            buildPython "C:/Python36/python.exe"
        }
    }
    

    配置依赖

    工程根目录下的 build.gradle

    buildscript {
        repositories {
            google()
            jcenter()
            // 设置仓库
            maven { url "https://chaquo.com/maven" }
        }
        dependencies {
            classpath 'com.android.tools.build:gradle:3.3.1'
            // 导入Chaquopy框架的包
            classpath "com.chaquo.python:gradle:6.3.0"
        }
    }
    

    app模块下的 build.gradle

    apply plugin: 'com.android.application'
    // 应用插件
    apply plugin: 'com.chaquo.python'
    
    android {
        compileSdkVersion 28
        defaultConfig {
            applicationId "org.hello"
            minSdkVersion 16
            targetSdkVersion 28
            versionCode 1
            versionName "1.0"
    
            // 指定abi,如需在模拟器调试,增加"x86",否则指定"armeabi-v7a"即可
            ndk {
                abiFilters "armeabi-v7a", "x86"
            }
        }
        buildTypes {}
    }
    
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar'])
        implementation 'com.android.support:appcompat-v7:28.0.0'
        implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    }
    

    配置完成后,同步一下gradle,网络状况不良可能会失败,多同步几次,亲测无需代理,同步成功后,所需的依赖就准备好了

    编写代码

    同步成功后,在工程中的main目录下会生成python文件夹,如未生成,手动生成一个即可,该目录即用来存放我们自己编写的python代码

    Python代码

    python文件夹中创建hello.py

    from java import jclass
    
    def greet(name):
        print("--- hello,%s ---" % name)
    
    def add(a,b):
        return a + b
    
    def sub(count,a=0,b=0,c=0):
        return count - a - b -c
    
    def get_list(a,b,c,d):
        return [a,b,c,d]
    
    def print_list(data):
        print(type(data))
        # 遍历Java的ArrayList对象
        for i in range(data.size()):
            print(data.get(i))
    
    # python调用Java类 
    def get_java_bean():
        JavaBean = jclass("org.hello.JavaBean")
        jb = JavaBean("python")
        jb.setData("json")
        jb.setData("xml")
        jb.setData("xhtml")
        return jb
    

    Java代码

    MainActivity.java

    package org.hello;
    
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    import com.chaquo.python.Kwarg;
    import com.chaquo.python.PyObject;
    import com.chaquo.python.android.AndroidPlatform;
    import com.chaquo.python.Python;
    import java.util.ArrayList;
    import java.util.List;
    
    public class MainActivity extends AppCompatActivity {
        static final String TAG = "PythonOnAndroid";
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initPython();
            callPythonCode();
        }
        // 初始化Python环境
        void initPython(){
            if (! Python.isStarted()) {
                Python.start(new AndroidPlatform(this));
            }
        }
        // 调用python代码
        void callPythonCode(){
            Python py = Python.getInstance();
            // 调用hello.py模块中的greet函数,并传一个参数
            // 等价用法:py.getModule("hello").get("greet").call("Android");
            py.getModule("hello").callAttr("greet", "Android");
            
            // 调用python内建函数help(),输出了帮助信息
            py.getBuiltins().get("help").call();
    
            PyObject obj1 = py.getModule("hello").callAttr("add", 2,3);
            // 将Python返回值换为Java中的Integer类型
            Integer sum = obj1.toJava(Integer.class);
            Log.d(TAG,"add = "+sum.toString());
            
            // 调用python函数,命名式传参,等同 sub(10,b=1,c=3)
            PyObject obj2 = py.getModule("hello").callAttr("sub", 10,new Kwarg("b", 1), new Kwarg("c", 3));
            Integer result = obj2.toJava(Integer.class);
            Log.d(TAG,"sub = "+result.toString());
            
            // 调用Python函数,将返回的Python中的list转为Java的list
            PyObject obj3 = py.getModule("hello").callAttr("get_list", 10,"xx",5.6,'c');
            List<PyObject> pyList = obj3.asList();
            Log.d(TAG,"get_list = "+pyList.toString());
            
            // 将Java的ArrayList对象传入Python中使用
            List<PyObject> params = new ArrayList<PyObject>();
            params.add(PyObject.fromJava("alex"));
            params.add(PyObject.fromJava("bruce"));
            py.getModule("hello").callAttr("print_list", params);
      
            // Python中调用Java类
            PyObject obj4 = py.getModule("hello").callAttr("get_java_bean");
            JavaBean data = obj4.toJava(JavaBean.class);
            data.print();
        }
    }
    
    

    准备一个类,让Python返调Java类

    package org.hello;
    
    import android.util.Log;
    import java.util.ArrayList;
    import java.util.List;
    
    public class JavaBean {
        private String name;
        private List<String> data;
    
        public JavaBean(String n){
            this.name = n;
            data = new ArrayList<String>();
        }
    
        public void setData(String el){
            this.data.add(el);
        }
    
        public void print(){
            for (String it: data) {
                Log.d("Java Bean - "+this.name,it);
            }
        }
    }
    

    小结:

    1. Python没有方法重载,通常一个函数会声明很多参数,注意使用Kwarg类进行命名式传参
    2. 注意对象转换,PyObject类是桥梁,fromJava函数将一个Java对象转换为相应的Python对象,toJava函数正好相反,将Python中的对象转换成Java中的对象
    3. 以上未演示map用法,实际上与List类似,对应Python中的字典对象,PyObject提供了asMap方法

    进阶用法

    生成静态代理

    我们可以使用Python类来扩展Java,实质上就是编写Python类后,使用工具自动生成对应的Java类

    在gradle中进行配置python模块

    defaultConfig {
            python {
                staticProxy "test_class"
            }
    }
    

    在Python目录中创建test_class.py

    from android.os import Bundle
    from android.support.v7.app import AppCompatActivity
    from com.chaquo.python.hello import R
    from java import jvoid, Override, static_proxy,jint,method
    
    
    class MainActivityEx(static_proxy(AppCompatActivity)):
    
        @Override(jvoid, [Bundle])
        def onCreate(self, state):
            AppCompatActivity.onCreate(self, state)
            self.setContentView(R.layout.activity_main)
    
        
    
    	'''
    	    要想Java类生成对应方法,必须使用该装饰器,指定返回值和参数类型
    	'''
        @method(jint, [jint])
        def func(self,num):
            return 1 + num
    

    Make工程之后会生成对应的Java代码。注意,生成的代码并不在src下,在方法中引用一下MainActivityEx,并自动导包后,可点进去查看生成的源码

    // Generated at 2019-08-31T12:29:18Z with the command line:
    // --path D:\workspace\flutter_space\flutter_web\hello\app\build\generated\python\sources\debug;D:\workspace\flutter_space\flutter_web\hello\app\build\generated\python\requirements\debug/common --java D:\workspace\flutter_space\flutter_web\hello\app\build\generated\python\proxies\debug test_class
    
    package test_class;
    
    import com.chaquo.python.*;
    import java.lang.reflect.*;
    import static com.chaquo.python.PyObject._chaquopyCall;
    
    @SuppressWarnings("deprecation")
    public class MainActivityEx extends android.support.v7.app.AppCompatActivity implements StaticProxy {
        static {
            Python.getInstance().getModule("test_class").get("MainActivityEx");
        }
        
        public MainActivityEx() {
            PyObject result;
            result = _chaquopyCall(this, "__init__");
            if (result != null) result.toJava(void.class);
        }
        
        @Override public void onCreate(android.os.Bundle arg0) {
            PyObject result;
            result = _chaquopyCall(this, "onCreate", arg0);
            if (result != null) result.toJava(void.class);
        }
        
        public int func(int arg0) {
            PyObject result;
            result = _chaquopyCall(this, "func", arg0);
            return result.toJava(int.class);
        }
    
        // 省略......
    }
    
    

    注意,要使用静态代理生成器,Python中的类必须使用static_proxy方法进行包装,如需生成方法,还需要使用相关的Python装饰器,详细用法见Static proxy文档

    静态代理可同时配置多个

    defaultConfig {
            python {
               staticProxy(
               "chaquopy.test.static_proxy.basic",
                "chaquopy.test.static_proxy.header",
               "chaquopy.test.static_proxy.method"
               )
            }
    }
    

    第三方库引入

    Chaquopy支持90%的纯Python源码的第三方库,如BeautifulSoup等,当然,Python很多知名库都是C/C++语言写的,使用Python包装一层而已,例如numpypillowscikit-learn等等,像这样的二进制包,Chaquopy框架也支持一部分,这就相当难得了,实际上,Python移植到安卓平台,最难搞的就是第三方库的移植。想查看Chaquopy支持哪些包含二进制包的Python库,请点击Chaquopy pypi

    增加gradle配置

    defaultConfig {
        python {
            // ......
            pip {
                install "Beautifulsoup4"
                install "requests"
                install "numpy"
            }
        }
    }
    

    hello.py中增加代码

    from bs4 import BeautifulSoup
    import requests
    import numpy as np
    
    # ...省略...
    
    # 爬取网页并解析
    def get_http():
        r = requests.get("https://www.baidu.com/")
        r.encoding ='utf-8'
        bsObj = BeautifulSoup(r.text,"html.parser")
        for node in bsObj.findAll("a"):
            print("---**--- ", node.text)
            
    # 使用numpy
    def print_numpy():
        y = np.zeros((5,), dtype = np.int)
        print(y)
    

    MainActivity.java增加调用代码

        void callPythonCode(){
            // ......省略
            py.getModule("hello").callAttr("get_http");
            py.getModule("hello").callAttr("print_numpy");
        }
    

    使用了网络,还需增加网络权限

    <uses-permission android:name="android.permission.INTERNET"/>
    

    完全使用Python开发

    前面说过了,Chaquopy框架可以完全使用Python语言编写apk,并且开发者还提供了一个 模板工程

    整个工程的main目录下只有一个Python目录,没有java目录,这实际上就是我们之前说的静态代理,并不是没有Java代码,只是根据Python代码自动生成对应的Java代码

    from android.os import Bundle
    from android.support.v7.app import AppCompatActivity
    from com.chaquo.python.hello import R
    from java import jvoid, Override, static_proxy
    
    
    class MainActivity(static_proxy(AppCompatActivity)):
    
        @Override(jvoid, [Bundle])
        def onCreate(self, state):
            AppCompatActivity.onCreate(self, state)
            self.setContentView(R.layout.activity_main)
    

    原理解析

    Chaquopy框架并未开源,因此只能通过反编译apk来探究其实现原理

    查看AndroidPlatform.class源码,有如下方法

      private void loadNativeLibs() {
        System.loadLibrary("crystax");
        System.loadLibrary("crypto_chaquopy");
        System.loadLibrary("ssl_chaquopy");
        System.loadLibrary("sqlite3");
        System.loadLibrary("python" + Common.PYTHON_SUFFIX);
        System.loadLibrary("chaquopy_java");
      }
    

    当我看到crystax.so的加载代码时,立刻明白了其实现原理,它使用的是crystax版本的ndk工具链,继续查看反编译的资源结构验证猜想
    在这里插入图片描述
    由其资源结构,基本可知其实现方案,几乎与我之前研究并写的一些博客吻合,该框架的实现方式,基本与我的想法不谋而合,也是我推崇的实现方案。

    简单说就是以android的JNI技术为桥梁,JNI技术解决了Java与C/C++混合编程的问题,而Python官方解释器则是纯C语言实现的,名为CPython解释器,在Android上,Python解释器就是一个so动态库。JNI接口使得C语言能反射Java的类与方法,而Python运行在C语言之上,那么Python也就具备了调用Java的能力。整个过程就是Java调用C语言代码,C再调用CPython解释器从而执行Python代码;Python调用CPython解释器,CPython调用C语言代码,C语言代码再反射Java代码,完成一次反调。这之间,粘合Java与CPython解释器的一段C语言代码,也就是Chaquopy框架干的事,不出所料它应该就是libchaquopy_java.so
    在这里插入图片描述

    还有一点值得说说,看过Python解释器源码的应该知道,PyObject是CPyhton解释器中一切对象的超类,当然,在C语言中它是一个结构体,CPython 提供的C语言API,基本上也就是将C语言结构体转换为PyObject实现与Python代码的交互,Python调用C也一样,而Chaquopy框架在处理Java与Python交互时,很巧妙的使用Java实现一个PyObject类,我的理解,它实际上就是将CPython解释器中的PyObject映射到了一个Java类,通过操作这个类实现交互,很有一点前端里所谓虚拟DOM的意思。

    更多深入的具体的细节,请直接查看上面给出的我之前写的博客。

    文档

    这篇文章仅作为一篇开胃菜,更多详细的具体的用法,还是需要查看Chaquopy的文档的,查看文档也是程序员的基本素养了

    如果想学习调用Python解释器,这里还有编译好的各个平台版本的Python解释器

    缺陷

    多线程
    Chaquopy是线程安全的。但是,因为它基于CPython(Python参考实现),所以它受到CPython的全局解释器锁(GIL)的限制。这意味着尽管Python代码可以在任意数量的线程上运行,但在任何给定时刻只会执行其中一个线程。

    内存管理
    如果Python对象引用直接或间接引用原始Python对象的Java对象,则可以创建跨语言引用循环。任何一种语言的垃圾收集器都无法检测到这样的循环。避免内存泄漏。要么在循环中的某处使用弱引用,要么在不再需要时手动中断循环。

    欢迎关注我的公众号:编程之路从0到1
    编程之路从0到1

    Google开发专家带你学 AI:入门到实战(Keras/Tensorflow)(附源码)

    展开全文
  • Java_MATLAB混合编程

    2015-02-05 16:29:00
    Java与MATLAB混合编程,里面有两个例子,分别是: Java将数据传给MATLAB,要MATLAB画图; Java将数据传给MATLAB,MATLAB处理后再Java数据返还给Java。
  • ADSP-TS101S的软件设计可以采用汇编语言、高级语言(C/C )或高级语言与汇编语言混合编程。完全采用汇编编程,执行效率高,但对于复杂算法编写难度大,开发周期长,可读性和可移植性差;而完全采用C编程虽然可以弥补...
  • 我想要vs与MATLAB混合编程实现图像处理的可视化界面,调用的shiyan1的程序后为什么不会显示图像呢?我不知道到底是哪里出问题了,运行程序不显示哪里有错误就是不会显示图像,望大家帮帮我,谢谢了。下面是我的程序...
  • C#Matlab混合编程必备MWArray组件

    热门讨论 2012-06-11 13:54:32
    用于C#与Matlab混合编程数据转换的组件MWArray。之前在网上找了很久,坑人的太多了,不是下不下来就是不能用。所以亲自分别在在windows7和Xp两种系统下安装了Matlab2011B,将从中拷贝出的64位MWArray.dll和32位的...
  • Qt Quick 之 QML 与 C++ 混合编程详解

    万次阅读 多人点赞 2014-07-04 12:22:34
    但它不是万能的,也有很多局限性,原来 Qt 的一些技术,比如低阶的网络编程如 QTcpSocket ,多线程,又如 XML 文档处理类库 QXmlStreamReader / QXmlStreamWriter 等等,在 QML 中要么不可用,要么用起来不方便,...
  • labview和MATLAB混合编程

    2019-02-27 11:10:09
    matlab和labview混合编程。 结合实例详细介绍labview与Matlab进行混合编程,达到了利用Matlab优化算法库的目的。

空空如也

1 2 3 4 5 ... 20
收藏数 10,633
精华内容 4,253
关键字:

混合编程