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

时间:2018-09-06 12:12:54

标签: android c++ android-ndk protocol-buffers grpc

我从here编译了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})

然后我推送了生成的可执行文件和libs文件并尝试运行它:

LD_LIBRARY_PATH=. ./avocado

我遇到以下错误:

  

[libprotobuf致命   /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 :: protobuf :: FatalException类型的异常:   程序是根据协议缓冲区的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”。)   中止

我在做什么错了?

我们意识到protobuf库有一个名为libprotobuf-cpp-full.solibprotobuf-cpp-lite.so的版本,看来它们的版本是3.0.0。这与我们编译为静态库或共享库的版本(3.5.1)冲突。

1 个答案:

答案 0 :(得分: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();
}

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

  

设置后,请勿使用stat(2)检查库是否已加载。

     

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

     

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

我认为粗体字解释了此解决方案为何起作用。

3。更改CMakeLists.txt

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

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。祝您在项目的其余部分中一切顺利!