利用Qt在Android上使用grpc需要*.a的静态库,Windows上编译的lib库以及linux编译出来的.a,经过尝试,均无法链接成功。本文尝试使用NDK来编译Android版本的grpc静态库。

前言

Qt for Android上要使用grpc,需要用到对应Android架构的静态库文件,本文总结记录下在Ubuntu

准备工作

安装git:sudo apt install git

安装cmake:sudo apt install cmake

安装构建工具:sudo apt-get install build-essential autoconf libtool

下载Android NDK:android-ndk-r18b-linux-x86_64.zip

安装grpc:

$ mkdir git & cd git

$ git clone https://github.com/grpc/grpc

$ cd grpc

$ git submodule update --init

编译

环境:Ubuntu18.04

使用Android NDK交叉工具链编译:

$ cd ../ & mkdir grpc-build

$ vim compile.sh 输入以下脚本内容

#!/bin/sh

cd /home/yakong/git/grpc-build

export NDK_ROOT=/home/yakong/Downloads/android-ndk-r16b

cmake /home/yakong/git/grpc/

-DCMAKE_TOOLCHAIN_FILE=${NDK_ROOT}/build/cmake/android.toolchain.cmake

-DANDROID_ABI=armeabi-v7a

-DANDROID_PLATFORM=android-24

-DANDROID_STL=c++_static

-DRUN_HAVE_STD_REGEX=0

-DRUN_HAVE_POSIX_REGEX=0

-DRUN_HAVE_STEADY_CLOCK=0

-DCMAKE_BUILD_TYPE=Release

cmake --build . --target grpc++

$ chmod +x ./compile.sh

$ ./compile.sh

成功构建完成后,在grpc-build生成各*.a静态库文件

Qt for Android使用

环境:Windows10+Qt5.12.3+Android NDK r18b+华为荣耀9手机

新建QtgRPC-Server应用程序,链接好grpc以及第三方的protobuffer等静态库后,构建出现undefined reference to `rand/stderr/strtof`的链接错误,解决请看附录章节。

程序构建打包生成apk后,在Android设备上安装运行直接崩溃退出,提示Protocol Buffer版本不匹配错误,解决请看附录章节。

程序可以在Android真机上运行起来了,但是看日志显示好像?grpc服务没有启动起来,在应用程序输出窗口有以下红色错误(看不懂):

一波刚平,一波又起,真是一波接一波,继续打怪!但是,我直接运行Console服务端(QtgRPC-Server),以及Qt Widget客户端(grpc-client),结果显示可以通信,哈哈,上述红色日志部分不影响,最终运行的结果:

至此,基于Qt C++跨平台特性,Qt for Android调用grpc成功!

附录

1. /bin/sh: 1: go: not found错误

[ 15%] Generating err_data.c

/bin/sh: 1: go: not found

third_party/boringssl/crypto/err/CMakeFiles/err.dir/build.make:83: recipe for target 'third_party/boringssl/crypto/err/err_data.c' failed

make[3]: *** [third_party/boringssl/crypto/err/err_data.c] Error 127

make[3]: *** Deleting file 'third_party/boringssl/crypto/err/err_data.c'

CMakeFiles/Makefile2:3345: recipe for target 'third_party/boringssl/crypto/err/CMakeFiles/err.dir/all' failed

make[2]: *** [third_party/boringssl/crypto/err/CMakeFiles/err.dir/all] Error 2

CMakeFiles/Makefile2:1016: recipe for target 'CMakeFiles/grpc++.dir/rule' failed

make[1]: *** [CMakeFiles/grpc++.dir/rule] Error 2

Makefile:463: recipe for target 'grpc++' failed

make: *** [grpc++] Error 2

原因为缺少go编译环境

解决:sudo apt install golang

2. 查看*.a架构信息

yakong@ubuntu:~/git/grpc-build/third_party/protobuf$ ls

CMakeFiles lib Makefile protobuf.pc

cmake_install.cmake libprotobuf.a protobuf-lite.pc

yakong@ubuntu:~/git/grpc-build/third_party/protobuf$ file libprotobuf.a

libprotobuf.a: current ar archive

yakong@ubuntu:~/git/grpc-build/third_party/protobuf$ lipo -info libprotobuf.a

Command 'lipo' not found, did you mean:

command 'lilo' from deb lilo

Try: sudo apt install

yakong@ubuntu:~/git/grpc-build/third_party/protobuf$ readelf -h libprotobuf.a | grep 'Class|File|Machine'

File: libprotobuf.a(any_lite.cc.o)

Class: ELF32

Machine: ARM

File: libprotobuf.a(arena.cc.o)

Class: ELF32

Machine: ARM

3. undefined reference to `rand/stderr/strtof`

Qt for Android中使用grpc出现链接错误:

该文章指出,在NDK15以后,标准IO设备:stderr 等都不被支持了~

仔细发现我使用的是r16b版本编译grpc,而Qt Creator中却使用的是r18b版本,版本确实不一致,有可能会造成影响。

然而,我将Qt Creator使用的ndk版本换成r16b,一样的错误:

我再次使用r18b版本的ndk在Ubuntu上进行grpc编译,还是出现类似错误:

对照错误出现的位置,修改grpc源码:

1)将使用rand()的位置改为固定的之值,比如1(任意随机的整数值)

2)将使用stderr的地方注释掉,不影响功能

3)将strtof修改为strtod或使用低版本的ndk

之后重新编译,重新在Qt中加载使用,比较幸运,构建成功了~

4. 运行错误:Protocol Buffer版本不匹配

This program requires version 3.9.0 of the Protocol Buffer runtime library, but the installed version is 3.8.0. Please update your library. If you compiled the program yourself, make sure that your headers are from the same version of Protocol Buffers as your link-time library.

分析:我在Ubuntu中使用ndk编译grpc使用的protobuf版本为3.8.0,而我在qt for android中使用的protoc命令产生的*.pb.cc是3.9.0版本生成的。所以,希望*.pb.cc也由3.8.0版本的protobuf编译器来生成。

编译生成protobuf编译器:

$ ./grpc/third_party/protobuf

$./autogen.sh

$./configure

$ make    // 耗时较长

$ [sudo] make install

执行protoc –version时出错:

protoc: error while loading shared libraries: libprotoc.so.19: cannot open shared object file: No such file or directory

该文章指出:protobuf的默认安装路径是/usr/local/lib,而/usr/local/lib 不在Ubuntu体系默认的 LD_LIBRARY_PATH 里,所以就找不到该lib

解决方法:

1.在/etc/ld.so.conf.d目录中创建文件libprotobuf.conf 写入内容:/usr/local/lib

2.输入命令重置内容生效:sudo ldconfig

这时,再运行protoc –version 就可以正常看到版本号了

在grpc目录下执行:

$ make

报错了:zconf.h找不到

解决方法:sudo apt-get install zlib1g-dev

再次make, 编译漫长,耐心等待…

$ sudo make install

接着编译自带cpp例子helloworld:

将生成的helloworld.pb.h/cpp以及helloworld.grpc.pb.h/cpp拷贝在qt工程中使用,而且包含头文件也用同一套的,否则会出现“#error This file was generated by an older version of protoc which is ^”的错误:

这下构建成功后,运行,不会在运行时报protobuf版本不对导致崩溃了~

5. Qt工程pro文件设置

增加:DEFINES += _WIN32_WINNT=0x0600

增加包含路径和链接静态库路径:

INCLUDEPATH += $$PWD/../../include

DEPENDPATH += $$PWD/../../include

unix:!macx: LIBS += -L$$PWD/../../grpc-build-r18b/ -lgrpc++

unix:!macx: PRE_TARGETDEPS += $$PWD/../../grpc-build-r18b/libgrpc++.a

unix:!macx: LIBS += -L$$PWD/../../grpc-build-r18b/ -lgrpc

unix:!macx: PRE_TARGETDEPS += $$PWD/../../grpc-build-r18b/libgrpc.a

unix:!macx: LIBS += -L$$PWD/../../grpc-build-r18b/ -lgpr

unix:!macx: PRE_TARGETDEPS += $$PWD/../../grpc-build-r18b/libgpr.a

unix:!macx: LIBS += -L$$PWD/../../grpc-build-r18b/ -laddress_sorting

unix:!macx: PRE_TARGETDEPS += $$PWD/../../grpc-build-r18b/libaddress_sorting.a

unix:!macx: LIBS += -L$$PWD/../../grpc-build-r18b/third_party/protobuf/ -lprotobuf

unix:!macx: PRE_TARGETDEPS += $$PWD/../../grpc-build-r18b/third_party/protobuf/libprotobuf.a

unix:!macx: LIBS += -L$$PWD/../../grpc-build-r18b/third_party/zlib/ -lz

unix:!macx: PRE_TARGETDEPS += $$PWD/../../grpc-build-r18b/third_party/zlib/libz.a

unix:!macx: LIBS += -L$$PWD/../../grpc-build-r18b/third_party/boringssl/ssl/ -lssl

unix:!macx: PRE_TARGETDEPS += $$PWD/../../grpc-build-r18b/third_party/boringssl/ssl/libssl.a

unix:!macx: LIBS += -L$$PWD/../../grpc-build-r18b/third_party/cares/cares/lib/ -lcares

unix:!macx: PRE_TARGETDEPS += $$PWD/../../grpc-build-r18b/third_party/cares/cares/lib/libcares.a

注意:其中include路径包含grpc的头文件以及protobuf的./src/google中的文件

6. Qt测试工程部分代码

Helloworld.proto

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

syntax = "proto3";

option java_multiple_files =true;

option java_package ="io.grpc.examples.helloworld";

option java_outer_classname ="HelloWorldProto";

option objc_class_prefix ="HLW";

package helloworld;// The greeting service definition.service Greeter {// Sends a greetingrpc SayHello (HelloRequest) returns (HelloReply) {}

}// The request message containing the user's name.message HelloRequest {

string name =1;floatscore =2;

}// The response message containing the greetingsmessage HelloReply {

string message =1;floatscore =2;

}

服务端QtgRPC-Server

main.cpp

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

#include#include#include#include#include#include"helloworld.pb.h"#include"helloworld.grpc.pb.h"usinggrpc::Server;usinggrpc::ServerBuilder;usinggrpc::ServerContext;usinggrpc::Status;usinghelloworld::HelloRequest;usinghelloworld::HelloReply;usinghelloworld::Greeter;// Logic and data behind the server's behavior.classGreeterServiceImpl final :publicGreeter::Service {

Status SayHello(ServerContext* context,constHelloRequest* request,

HelloReply* reply) override {

std::string prefix("Hello ");

reply->set_message(prefix + request->name());

reply->set_score(request->score());returnStatus::OK;

}

};voidRunServer() {

std::cout <

std::string server_address("0.0.0.0:50051");

GreeterServiceImpl service;

ServerBuilder builder;// Listen on the given address without any authentication mechanism.builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());// Register "service" as the instance through which we'll communicate with// clients. In this case it corresponds to an *synchronous* service.builder.RegisterService(&service);// Finally assemble the server.std::unique_ptr server(builder.BuildAndStart());

std::cout <Wait();

}intmain(intargc,char*argv[])

{

std::cout <

}

客户端grpc-client

Widget.h

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

#ifndefWIDGET_H#defineWIDGET_H#include#include#include#include#include#include#include"helloworld.grpc.pb.h"usinggrpc::Channel;usinggrpc::ClientContext;usinggrpc::Status;usinghelloworld::HelloRequest;usinghelloworld::HelloReply;usinghelloworld::Greeter;classGreeterClient {public:

GreeterClient(std::shared_ptr channel)

: stub_(Greeter::NewStub(channel)) {}// Assembles the client's payload, sends it and presents the response back// from the server.std::string SayHello(conststd::string& user) {// Data we are sending to the server.HelloRequest request;

request.set_name(user);// Container for the data we expect from the server.HelloReply reply;// Context for the client. It could be used to convey extra information to// the server and/or tweak certain RPC behaviors.ClientContext context;// The actual RPC.Status status = stub_->SayHello(&context, request, &reply);// Act upon its status.if(status.ok()) {returnreply.message();

}else{

std::cout <

<

}

}floatSayScore(constfloatscore = std::numeric_limits::max()) {// Data we are sending to the server.HelloRequest request;

request.set_score(score);// Container for the data we expect from the server.HelloReply reply;// Context for the client. It could be used to convey extra information to// the server and/or tweak certain RPC behaviors.ClientContext context;// The actual RPC.Status status = stub_->SayHello(&context, request, &reply);// Act upon its status.if(status.ok()) {returnreply.score();

}else{

std::cout <

<

}

}private:

std::unique_ptr<:stub> stub_;

};classQTextBrowser;classWidget :publicQWidget

{

Q_OBJECTpublic:

Widget(QWidget *parent = nullptr);

~Widget();privateslots:voidreq();private:

QTextBrowser *text;

};

#endif // WIDGET_H

Widget.cpp

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

#include"Widget.h"#include#include#include

Widget::Widget(QWidget *parent)

: QWidget(parent)

, text(newQTextBrowser())

{

setWindowTitle(tr("grpc-client"));

resize(320,460);

QVBoxLayout *layout =newQVBoxLayout(this);

QPushButton *button =newQPushButton("req");

connect(button, &QPushButton::clicked,this, &Widget::req);

layout->addWidget(text);

layout->addWidget(button);

setLayout(layout);

}

Widget::~Widget()

{

}voidWidget::req()

{// Instantiate the client. It requires a channel, out of which the actual RPCs// are created. This channel models a connection to an endpoint (in this case,// localhost at port 50051). We indicate that the channel isn't authenticated// (use of InsecureChannelCredentials()).GreeterClient greeter(grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials()));

std::string user("world");

std::string reply = greeter.SayHello(user);floatreplyf= greeter.SayScore();

std::cout <

std::cout <

text->append(QString("Greeter received: %1").arg(reply.c_str()));

text->append(QString("Greeter received: %1").arg(replyf));

}

}

strtof linux内核,Qt for Android使用grpc探索相关推荐

  1. Android研究-linux内核启动到android系统

    很多人阅读代码,总喜欢从头开始,这样觉得很安全,有依靠,无论如何总是能知道"头",有头就能找到任何需要的部分. Android生在linux内核基础上,linux内核启动的最后一步 ...

  2. 修改linux内核启动动画,Android 开机界面及Linux内核启动界面的修改(tiny6410)

    第一开机界面替换,即内核bootloader启动小企鹅界面更换 1.首先找一个自定义120*120的png图像,这里名称linux.png,但是后面的ppm名称必须是图中所示 在linux下执行下面代 ...

  3. 【嵌入式Linux驱动开发】二十一、Linux内核自带的KEY驱动探索

      君子应知进退方,时机不到且隐藏.   妆未梳成未见客,势弱稍时敛锋芒.   腹隐良谋待机至,东山再起斗志昂.   遥想曹刘煮酒事,高明刘备扮愚郎. 文章目录 一. Linux 内核自带 KEY 驱 ...

  4. QT接收Linux内核,QT界面程序经过网路与普通的linux应用程序进行数据传送的情况...

    有时候会遇到QT界面程序经过网路与普通的linux应用程序进行数据传送的情况:(UDP协议,非TCP协议) 个人感觉比管道.共享内存.信号量.消息队列好用 Qt udp_client 1.我们新建Qt ...

  5. Linux 内核监控在 Android 攻防中的应用

    在日常分析外部软件时,遇到的反调试/反注入防护已经越来越多,之前使用的基于 frida 的轻量级沙盒已经无法满足这类攻防水位的需要,因此需要有一种更加深入且通用的方式来对 APP 进行全面的监测和绕过 ...

  6. linux文件系统启动流程,linux 内核启动过程以及挂载android 根文件系统的过程

    转载 作者:汕头大学-黄珠唐 时间:2009 年10 月29 日 主要介绍linux 内核启动过程以及挂载android 根文件系统的过程,以及介绍android 源代码中文件系统部分的浅析. 主要源 ...

  7. Android是基于Linux的开源操作系统也是Linux内核

    Android是基于Linux的开源操作系统也是Linux内核 亿仁网 发布时间: 18-08-2023:46山西亿仁电子商务官方帐号 Android是基于Linux的开源操作系统,主要用于嵌入式设备 ...

  8. 使用Android模拟器调试linux内核

    使用Android模拟器调试linux内核 为什么需要调试linux内核 如何在Android上调试内核 开发环境 创建模拟器 下载goldfish内核源码 编译goldfish内核 编译内核遇到的问 ...

  9. linux内核 cpu_die,解密“内核”,和“cpu”又有啥关系?

    感谢大家对天才疯子的阅读与支持! 天才疯子每天与你不见不散,带你装逼带你飞! 核心(Die)又称为内核,是CPU最重要的组成部分.CPU中心那块隆起的芯片就是核心,是由单晶硅以一定的生产工艺制造出来的 ...

最新文章

  1. gVim 取消自动备份(Windows/Linux)
  2. 【DBMS 数据库管理系统】多维数据模型 ( 星型模式 | 雪片模型 | 事实群模型 | 度量 | 分布型 | 代数型 | 整体型 )
  3. socket编程介绍
  4. 直接拿来用!GitHub 上那些值得你 Star 的开源项目!
  5. 实现数据库连接池druid的工具类
  6. 软件开发几个阶段的内容以及产物
  7. 云服务器的计费模式有哪些?
  8. 计算机理论参考文献,计算机理论英文参考文献 计算机理论论文参考文献哪里找...
  9. vue+js input文本框输入时自动填充邮箱后缀组件封装
  10. PS:换背景天空(简单抠图)
  11. 经典语录,至理名言,人生百态
  12. 海尔智家半年报营收净利双增,卡萨帝、三翼鸟贡献几何?
  13. 2023西北大学计算机考研信息汇总
  14. 大学四年努力学好编程
  15. 使用word绘制钟表刻度表盘
  16. Android教程之名词扫盲汇总
  17. xp计算机管理下的服务显示不出来,WinXP系统任务栏不显示打开窗口的三种解决方案...
  18. 工字型钢弹性截面模量计算公式_截面模量的计算公式是什么?
  19. java多语言网页_网页多国语言实现
  20. 服务器电源输出电压不稳定,电源模块输出电压变低的原因和解决方法

热门文章

  1. splunk 提取字段_splunk 学习笔记之三[使用字段查找对照]
  2. python中range的用法_python的range怎么使用
  3. C++新特性探究(6.1):auto和decltype的类型推断差异所在
  4. C语言之文件读写探究(五):rewind、ftell、fseek(文件指针偏移)
  5. ipc原理linux,传统的Linux中IPC通信原理
  6. 图片变色_一张图看懂手机CMF史,附带手机渐变色工艺解析
  7. python123判断字符串结尾_Python学习教程:在字符串的开头和结尾处做文本匹配
  8. java validate注解_JAVA 注解验证字段(例子)
  9. 手写java_手写java锁
  10. 串口 浮点数 结构体_组态软件与串口服务器的配置