首页 > 其他分享 >Android ndk string处理

Android ndk string处理

时间:2024-08-10 12:23:34浏览次数:8  
标签:std __ ndk string char ndk1 Android include

1. Android NDK

NDK开发过程中常用的库定义在android-ndk-r25c/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android

libc++_shared.so libc++_static.a libstdc++.a

ndk工具链下载:./bin/sdkmanager --install "ndk;25.0.8775105"

2. 链接问题

问题背景

给定第三方依赖库头文件和实现库,现在需要集成到自己的工程中。

第三方库ThirdLib.h如下:

#ifndef _THIRD_LIB_H_
#define _THIRD_LIB_H_

#include <string>
#include <unistd.h>

std::string makeRandomString(std::string prefix);

#endif

实现库libthirdparty.so给外部调用的符号表如下:

$ nm -D -g -C --defined-only libthirdparty.so
0000000000001014 T makeRandomString(std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> >)

集成代码如下:

// main.cpp
#include <iostream>
#include <ThirdLib.h>
using namespace std;

int main(int argc, char const *argv[])
{
    std::string result_cstring = makeRandomString(std::string("Hello"));
    return 0;
}

对应的Android.bp

cc_binary {
    name: "client",
    srcs: [
        "main.cpp",
    ],
    shared_libs: [
        "libthirdparty",
    ],
    cflags: [
		"-O2",
		"-Wno-unused-parameter",
    ],
}

编译时会遇到如下错误:

ld.lld: error: undefined symbol: makeRandomString(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
>>> referenced by main.cpp:7

问题分析

在编译main.cpp时出现符号找不到,需要链接的符号如下:

makeRandomString(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)

函数名为makeRandomString,正是libthirdparty.so提供的符号,但是libthirdparty.so提供的符号为:

makeRandomString(std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> >)

可以发现符号确实不一样,一个是std::__1前缀,另一个是std::__ndk1前缀。

第三方库的实现库没法改变,那么只能在集成端生成std::__ndk1前缀的库进行调用,根据提示,std::__1符号找不到,是否意味着本地生成的默认就是std::__1前缀的符号呢?编写如下代码进行分析:

// Normal.h
#ifndef _NORMAL_H_
#define _NORMAL_H_

#include <string>
#include <unistd.h>

std::string makeNormalRandomString(std::string prefix);

#endif

// Normal.cpp
#include "Normal.h"

std::string makeNormalRandomString(std::string prefix) {
    int uid = getuid();
    int pid = getpid();
    int tid = gettid();
    int max = 256;
    char buffer[max];
    snprintf(buffer, max, "%s: normal! uid %d, pid %d, tid %d", prefix.c_str(), uid, pid, tid);
    return std::string(buffer);
}

// Android.bp
cc_library_shared {
    name: "libnormal",
    srcs: [
        "Normal.cpp",
    ],
    export_include_dirs: [
        ".",
    ],
    cflags: [
		"-O2",
		"-Wno-unused-parameter",
    ],
}

查看生成的符号表:

$ nm -D -g -C --defined-only ./libnormal.so
0000000000001014 T makeNormalRandomString(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)

本地生成的符号确实就是std::__1前缀,接下来可以借助IDE的力量查看根据什么条件决定这个前缀,前提是需要使用vscode + clangd配置好代码跳转环境,可以参考我的另一篇文章:Android配置C++开发环境 根据提示,我们输入以下符号:

std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > temp;

点击__1进行跳转,以下代码使用的是Android12代码环境:

// external/libcxx/include/string 533行
_LIBCPP_BEGIN_NAMESPACE_STD

// 继续跳转
// external/libcxx/include/__config 802行
#define _LIBCPP_BEGIN_NAMESPACE_STD namespace std { inline namespace _LIBCPP_ABI_NAMESPACE {

// 继续跳转 _LIBCPP_ABI_NAMESPACE
// external/libcxx/include/__config 124行
#ifndef _LIBCPP_ABI_NAMESPACE
# define _LIBCPP_ABI_NAMESPACE _LIBCPP_CONCAT(__,_LIBCPP_ABI_VERSION)
#endif

// 继续跳转 _LIBCPP_ABI_VERSION
// external/libcxx/include/__config 38行
#ifndef _LIBCPP_ABI_VERSION
#  define _LIBCPP_ABI_VERSION 1
#endif

跟踪代码发现,std::__1其中的1是通过_LIBCPP_ABI_VERSION宏定义的,默认值就是1,那么只要提前将_LIBCPP_ABI_VERSION定义为ndk1就可以按照std::__ndk1进行编译。

但是需要找到提供这种符号实现的库,根据ndk字眼猜测是NDK工具链中提供的,在android_sdk/ndk/25.0.8775105/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib目录下使用android_find_symbols.sh搜索std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> >,具体搜索可以参考我的另一篇文章:Android定位需要引用的动态库 得到如下结果:

Summary, you should add these libs:
        libc++_shared

接下来我们就可以写一个中间层,向下对接第三方库,提供std::__ndk1符号;向上对接本地代码,因为中间层代码实现时不可能同时提供std::__1std::__ndk1符号,所以向上对接时需要替换为char*

编写中间层代码,用于实现char*std::__ndk1之间的转换:

// NDKHelper.h
#ifndef _NDK_HELPER_H_
#define _NDK_HELPER_H_
#include <string>

std::string makeNDKString(const char* str);

const char* makeCString(const std::string& ndkString);

#endif


// NDKHelper.cpp
#define _LIBCPP_ABI_VERSION ndk1
#include "NDKHelper.h"
#include <string.h>
#include <string>

std::string makeNDKString(const char* str) {
    return std::string(str);
}

const char* makeCString(const std::string& ndkString) {
    char* cString = new char[ndkString.length() + 1];
    memset(cString, 0, ndkString.length() + 1);
    strcpy(cString, ndkString.c_str());
    return cString;
}

// Android.bp
cc_library_shared {
    name: "libNDKHelper",
    srcs: [
        "NDKHelper.cpp",
    ],
    export_include_dirs: [
        ".",
    ],
    shared_libs: [
        "libc++_shared",
    ],
    cflags: [
		"-O2",
		"-Wno-unused-parameter",
    ],
}

// nm -D -g -C --defined-only ./libNDKHelper.so
// 00000000000010a8 T makeCString(std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> > const&)
// 0000000000001014 T makeNDKString(char const*)

最后需要提供一个包装库可以给本地代码直接调用:

// ThirdLibWrapper.h
#ifndef _THIRD_LIB_WRAPPER_H_
#define _THIRD_LIB_WRAPPER_H_

const char* makeRandomStringWrapper(const char* prefix);

#endif

// ThirdLibWrapper.cpp
#define _LIBCPP_ABI_VERSION ndk1
#include "ThirdLibWrapper.h"
#include "NDKHelper.h"
#include <ThirdLib.h>

const char* makeRandomStringWrapper(const char* prefix) {
    return ::makeCString( ::makeRandomString( ::makeNDKString(prefix) ) );
}

// Android.bp
cc_library_shared {
    name: "libthirdparty_wrapper",
    srcs: [
        "ThirdLibWrapper.cpp",
    ],
    export_include_dirs: [
        ".",
    ],
    shared_libs: [
        "libthirdparty",
        "libNDKHelper",
    ],
    cflags: [
		"-O2",
		"-Wno-unused-parameter",
    ],
}

最后,本地集成的代码:

// main.cpp
#include <iostream>
#include <ThirdLibWrapper.h>
using namespace std;

int main(int argc, char const *argv[])
{
    const char* result = makeRandomStringWrapper("Hello");
    printf("result is %s\n", result);
    delete[] result;
    result = nullptr;
    return 0;
}

// Android.bp
cc_binary {
    name: "client",
    srcs: [
        "main.cpp",
    ],
    shared_libs: [
        "libthirdparty_wrapper",
    ],
    cflags: [
		"-O2",
		"-Wno-unused-parameter",
    ],
}

完整的demo可以参考:ndk_string_test.zip

标签:std,__,ndk,string,char,ndk1,Android,include
From: https://blog.csdn.net/2401_85111291/article/details/141088967

相关文章

  • [AGC036E] ABC String
    又是一个逐步简化的模型,好烦了又不会做这种题了呜啊呜啊。首先相邻且相同的字符,我们可以缩在一起。不妨假设\(c_a\leqc_b\leqc_c\),我们考虑逐步删除来达到三个字符相同的情况。按照\(A\)将整个字符串划分成若干段,每一段一定形如\(BC\)交错的情形。注意到中间字符串长......
  • Scanner类、String类和StringBuffer类的相关使用
     一、Scanner:主要用于键盘录入的  构造方法:    Scanner(InputStreamsource)构造一个新的Scanner,产生从指定输入流扫描的值。 1、next()和nextLine()区别: Stringline=sc.next();//不会接收特殊字符,比如空格回车这样的符号 Stringline=sc.nex......
  • LeetCode | 541 Reverse String II
    分析以2k作为游标步长,反转游标前半部分的字符串,后半部分保留;尾部部分余留长度,如果在[k,2k)直接处理情况跟前述一样,如果不是则直接返回。在这道题里面,还是回到数组部分提到的循环不变量法则,在2k长度这个游标移动过程中,处理完全一致:2k步长移动,只处理[2i,2i+k]部分,即便是尾部也是如......
  • Android开发基础08-掌握kotlin语言
    Kotlin是一种现代化的编程语言,作为Android开发的官方支持语言,越来越多的开发者选择使用Kotlin进行Android应用开发。在开始学习Android开发之前,掌握Kotlin语言的基础知识至关重要。1.基础知识a.开发环境设置安装JDK(JavaDevelopmentKit):Kotlin运行于JVM之上,因此需要先安......
  • Android开发基础07-掌握Java语言
    Android开发广泛使用Java作为编程语言,熟练掌握Java语言是十分必要的。1.基础入门知识a.设置开发环境安装JDK(JavaDevelopmentKit):JDK是进行Java开发的必备工具,务必下载安装并配置相应的环境变量。安装IDE(IntegratedDevelopmentEnvironment):推荐使用IntelliJIDEA、E......
  • Android 13 移植EthernetSettings/Ethernet更新
    移植EthernetSettingsAndroid13在Settings搜索没有发现以太网设置,应该是移除了,但是客户的设备需要,所以移植Android11的.以太网相关的功能在Android13中进行模块化,提取到packages/modules/Connectivity/中,EthernetManager相关代码从framework移到packages/modules/Conne......
  • LeetCode | 344 Reverse String
    分析字符数组本质上还是数组,双指针本质上是遍历,遍历过程只处理两个独立数据,移动过程将问题分为已经解决和未解决的两部分。在这个题目中值得注意的是,关于字符数组进行数据原地交换采用的是异或^的方式主类packagecom.github.dolphinmind.string;/***@authordolphinmind......
  • Android dex、odex、oat、vdex、art区别
    1.dexjava程序编译成class后,dx工具将所有class文件合成一个dex文件,dex文件是jar文件大小的50%左右.2.odex(Android5.0之前)全称:OptimizedDEX;即优化过的DEX.Android5.0之前APP在安装时会进行验证和优化,为了校验代码合法性及优化代码执行速度,验证和优化后,会产生ODEX文件,运行Apk的......
  • iOS开发基础149-由UUIDString引发的思考
    问题1:[[UIDevicecurrentDevice]identifierForVendor].UUIDString什么情况下值会变化?[[UIDevicecurrentDevice]identifierForVendor].UUIDString是一个用于标识设备的唯一标识符(UUID),针对同一应用程序供应商(即同一开发者的应用程序集合),在设备上不变。然而,有一些情况会导致这个......
  • JavaScript toLocaleString() 方法
    定义和用法:toLocaleString()方法可根据本地时间把Date对象转换为字符串,并返回结果。语法:dateObject.toLocaleString()返回值dateObject的字符串表示,以本地时间区表示,并根据本地规则格式化。问题//Javascript中newDate().toLocaleString()在不同浏览器中的结果不一致的解决......