android - 构建使用协议缓冲区(无 APK)的 Android 可执行 gRPC 服务器
问题描述
我从这里编译了 gRPC Android 示例。
我想从 adb shell 将程序作为可执行文件运行。
将这些行添加到grpc-helloworld.cc
:
#include <iostream>
int main() {
std::cout << "qwerty" << std::endl;
return 0;
}
这些行对其CMakeLists.txt
:
add_executable(avocado
src/main/cpp/grpc-helloworld.cc)
target_include_directories(avocado
PRIVATE ${HELLOWORLD_PROTO_HEADERS})
target_link_libraries(avocado
helloworld_proto_lib
android
${log-lib})
然后我推送生成的可执行文件和库文件并尝试运行它:
LD_LIBRARY_PATH=. ./avocado
我收到以下错误:
[libprotobuf FATAL /home/buga/grpc/third_party/protobuf/src/google/protobuf/stubs/common.cc:79] 这个程序是针对 Protocol Buffer 运行时库的 3.0.0 版本编译的,它与安装版本(3.5.1)。联系程序作者获取更新。如果您自己编译程序,请确保您的标头来自与链接时库相同版本的 Protocol Buffers。(“out/soong/.intermediates/frameworks/av/drm/libmediadrm/libmediadrm/android_arm64_armv8-a_kryo300_shared_core/gen/proto/frameworks/av/drm/libmediadrm/protos/plugin_metrics.pb.cc”中的版本验证失败。)终止带有 google::protobuf::FatalException 类型的未捕获异常:该程序是针对 Protocol Buffer 运行时库的 3.0.0 版本编译的,这与安装的版本(3.5.1)不兼容。联系程序作者获取更新。如果您自己编译程序,请确保您的标头来自与链接时库相同版本的 Protocol Buffers。(“out/soong/.intermediates/frameworks/av/drm/libmediadrm/libmediadrm/android_arm64_armv8-a_kryo300_shared_core/gen/proto/frameworks/av/drm/libmediadrm/protos/plugin_metrics.pb.cc”中的版本验证失败。)中止
我究竟做错了什么?
我们意识到有一个名为libprotobuf-cpp-full.so
and的 protobuf 库libprotobuf-cpp-lite.so
版本,它们的版本似乎是 3.0.0。这与我们编译成静态库或共享库的版本(3.5.1)冲突。
解决方案
我不太确定为什么会这样。一旦链接器加载helloworld_proto_lib
,它会覆盖所有加载的 protobuf 符号,并且由于某种原因,与您无关的另一个库会使您的程序崩溃。但这并不能告诉你任何新的东西。
这是解决此问题的一种方法:
1. grpc-helloworld.cc 的变化
制作 main extern "C"
,并可能更改其名称。例如:
extern "C" int my_main() {
std::cout << "qwerty" << std::endl;
return 0;
}
2.添加文件grpc-avocado.cc
这将包含可执行文件的实际 main,它将动态加载库helloworld_proto_lib
和grpc-helloworld
. 这是如何做到的:
#include <iostream>
#include <android/dlext.h>
#include <dlfcn.h>
int main() {
android_dlextinfo extinfo;
extinfo.flags = ANDROID_DLEXT_FORCE_LOAD;
void* proto_lib = android_dlopen_ext("/path/to/libhelloworld_proto_lib.so", RTLD_LAZY, &extinfo);
void* helloworld = dlopen("/path/to/libgrpc-helloworld.so", RTLD_LAZY);
int (*my_main)() = (int (*)())dlsym(helloworld, "my_main");
return my_main();
}
android_dlopen_ext
的函数#include <android/dlext.h>
及其标志参数在此处进行了描述:https ://developer.android.com/ndk/reference/group/libdl 。在上面的代码中,我们传递了 flag ANDROID_DLEXT_FORCE_LOAD
,它被记录为:
设置后,不要使用 stat(2) 检查库是否已经加载。
此标志允许在由于某种原因多个 ELF 文件共享相同文件名的情况下强制加载库(例如,因为已加载的库已被删除和覆盖)。
请注意,如果该库具有与旧库相同的 DT_SONAME 并且某个其他库在其 DT_NEEDED 列表中具有 soname,则第一个将用于解决任何依赖项。
我认为粗体字是解释为什么这个解决方案有效的原因。
3.更改CMakeLists.txt
由于您将helloworld_proto_lib
动态加载,因此您现在可以将其从可执行定义中删除,并且不需要任何 proto 标头:
add_executable(avocado
src/main/cpp/grpc-avocado.cc)
target_link_libraries(avocado
android
${log-lib})
构建、推送和运行
您现在可以构建、推送可执行文件avocado
和两个库libgrpc-helloworld.so
、libhelloworld_proto_lib.so
和运行。你不需要LD_LIBRARY_PATH
。祝你项目的其余部分好运!
推荐阅读
- magento2 - Magneto2 在我的帐户下的前端没有客户订单历史记录,订单只能从管理员看到?
- python - 查找满足条件的 2D numpy 数组的索引
- java - 获取 java.io.IOException: head 是加载 .woff 和 .woff2 类型字体时的强制异常
- ios - 如何用swift动态屏蔽文本字段
- python - 将列列表转换为字符串
- python - 如何在 Python 中使用 n-gram 重叠对句子进行聚类?
- c# - 如果日期比单元格中的内容早 1 个月,如何更改 DataGridView 中单元格的颜色?
- android - 发布后Android库无法解析依赖项
- r - how to extract year, tz-convert and get millisecond part from a nanotime timestamp?
- split - 如何 Sqoop 从 Oracle 导入压缩分区 Hive 表