构建使用协议缓冲区(不含 APK)的 Android 可执行 gRPC 服务器



我从这里编译了gRPC Android示例。

我想从 adb 外壳将程序作为可执行文件运行。

将这些行添加到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

我收到以下错误:

[利普罗托布夫致命/home/buga/grpc/third_party/protobuf/src/google/protobuf/stubs/common.cc:79] 此程序是针对协议缓冲区的 3.0.0 版编译的 运行时库,与已安装的版本不兼容 (3.5.1(. 请联系程序作者以获取更新。 如果您编译了 程序自己,确保您的标头来自同一 协议缓冲区版本作为链接时库。 (版本 验证失败 "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::p rotobuf::FatalException:This 程序是针对协议缓冲区的 3.0.0 版编译的 运行时库,与已安装的版本不兼容 (3.5.1(. 请联系程序作者以获取更新。 如果您编译了 程序自己,确保您的标头来自同一 协议缓冲区版本作为链接时库。 (版本 验证失败 "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.solibprotobuf-cpp-lite.so的 protobuf 库版本,似乎他们的版本是 3.0.0。这与我们的版本(3.5.1(冲突,后者被编译成静态库或共享库。

我不太确定为什么会发生这种情况。一旦链接器加载helloworld_proto_lib,它就会覆盖所有加载的 protobuf 符号,并且由于某种原因,与您无关的另一个库会使您的程序崩溃。但这并没有告诉你任何新东西。

这是解决此问题的一种方法:

1. grpc-helloworld.cc 变更

制作主要extern "C",并更改其名称。例如:

extern "C" int my_main() {
std::cout << "qwerty" << std::endl;
return 0;
}

2. 添加文件 grpc-avocado.cc

这将包含可执行文件的实际主节点,它将动态加载库helloworld_proto_libgrpc-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();
}

#include <android/dlext.h>android_dlopen_ext的函数及其标志参数描述如下:https://developer.android.com/ndk/reference/group/libdl 。在上面的代码中,我们传递标志ANDROID_DLEXT_FORCE_LOAD,记录为:

设置后, 不要使用 stat(2( 来检查库是否已经加载。

此标志允许在由于某种原因多个 ELF 文件共享相同的文件名的情况下强制加载库(例如,因为已加载的库已被删除和覆盖(。

请注意,如果库具有与旧库相同的DT_SONAME,并且其他某个库在其DT_NEEDED列表中具有 soname,则第一个库将用于解析任何依赖项

我认为粗体文本解释了为什么这个解决方案有效。

3. 更改 CMake列表.txt

由于您将动态加载helloworld_proto_lib,因此现在可以将其从可执行文件定义中删除,并且不需要任何 proto 标头:

add_executable(avocado
src/main/cpp/grpc-avocado.cc)
target_link_libraries(avocado
android
${log-lib})

生成、推送和运行

您现在可以构建、推送可执行avocado和两个库libgrpc-helloworld.solibhelloworld_proto_lib.so和运行。你不需要LD_LIBRARY_PATH.祝您项目的其余部分好运!