$r()拼接字符串问题分析报告
$r()拼接字符串问题分析报告1 关键字 资源引用;资源文件;字符串; 2 问题描述 OH版本:3.1 Release IDE版本 DevEco Studio 3.0 Beta3 Build Version: 3.0.0.900 SDK版本 8 问题现象: 以下说明均以ETS(基于TS扩展的声明式开发范式)开发模式为准 Text($r('app.string.string_hello')) U
1 关键字
资源引用;资源文件;字符串;
2 问题描述
OH版本:3.1 Release
IDE版本 DevEco Studio 3.0 Beta3 Build Version: 3.0.0.900 SDK版本 8
问题现象: 以下说明均以ETS(基于TS扩展的声明式开发范式)开发模式为准
Text($r('app.string.string_hello'))
UI展示为 hello
Text($r('app.string.string_hello') + " world")
UI展示为 [Object object]
预期展示为 hello world
3 问题原因
据文档说明,Text组件 (https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/arkui-ts/ts-basic-components-text.md)接口形式:Text(content?: string) 即参数为一个字符串类型,所以开发者可能会使用一些常规的拼接字符串方法,例如“+” ,并理所当然认为界面应该正常展示。 实际上从STK的接口描述来看
interface TextInterface {
/**
* Called when writing text.
* @since 7
*/
(content?: string | Resource): TextAttribute;
}
这说明 content参数除了是字符串外,还可以是一个Resource类型,$r('....')就是这样的Resource类型
通过ace-ets2bundle工具进行 jsbundle转换后可以看到 对于文中开始提到代码,会被转成这样的形式:
Text.create({ "id": 16777218, "type": 10003, params: [] })
Text($r('app.string.string_hello') + " world")
则变成了这样
Text.create({ "id": 16777218, "type": 10003, params: [] } + " world");
{ "id": 16777218, "type": 10003, params: [] } + " world"
的结果就是[Object object],所以界面展示"[Object object]",问题是
Text.create({ "id": 16777218, "type": 10003, params: [] })
同理也应该展示[Object object],是如何变成实际的字符串的?
查看对应的前端开发框架到UI后端引擎和JS引擎的源码 (https://gitee.com/openharmony/ace_ace_engine)
路径: frameworks\bridge\declarative_frontend\jsview\js_text.cpp
void JSText::Create(const JSCallbackInfo& info)
{
std::string data;
if (info.Length() > 0) {
ParseJsString(info[0], data);
}
...
表明create方法接受的参数首先需要解析为一个C++的字符串类型
ParseJsString定义在 frameworks\bridge\declarative_frontend\jsview\js_view_abstract.cpp
bool JSViewAbstract::ParseJsString(const JSRef<JSVal>& jsValue, std::string& result)
{
//参数为字符串或Object类型
if (!jsValue->IsString() && !jsValue->IsObject()) {
LOGE("arg is not String or Object.");
return false;
}
...
//Object类型解析如下
JSRef<JSObject> jsObj = JSRef<JSObject>::Cast(jsValue);
//获取type key 得到的值用来对比是否为 Resource 类型
JSRef<JSVal> type = jsObj->GetProperty("type");
if (!type->IsNumber()) {
LOGW("type is not number");
return false;
}
//获取id key 得到的值 resId会用来获取实际资源字符
JSRef<JSVal> resId = jsObj->GetProperty("id");
if (!resId->IsNumber()) {
LOGW("resId is not number");
return false;
}
....
//获取params key
JSRef<JSVal> args = jsObj->GetProperty("params");
if (!args->IsArray()) {
LOGW("args is not array");
return false;
}
JSRef<JSArray> params = JSRef<JSArray>::Cast(args);
/*
如果是Resource类型 解析如下
ResourceType::STRING 为10003 和create参数内的 ```"type": 10003```指示的type 一致
*/
if (type->ToNumber<uint32_t>() == static_cast<uint32_t>(ResourceType::STRING)) {
//根据 resId 获取资源实际对应的字符
auto originStr = themeConstants->GetString(resId->ToNumber<uint32_t>());
//ReplaceHolder?
ReplaceHolder(originStr, params, 0);
result = originStr;
}
...
}
ReplaceHolder是起什么作用? 查看 ReplaceHolder函数: (frameworks\bridge\declarative_frontend\jsview\js_view_abstract.cpp)
void ReplaceHolder(std::string& originStr, JSRef<JSArray> params, int32_t containCount)
{
auto size = static_cast<int32_t>(params->Length());
//如果params为空
if (containCount == size) {
return;
}
...
/*
RESOURCE_APP_STRING_PLACEHOLDER 是一个正则规则
const std::regex RESOURCE_APP_STRING_PLACEHOLDER(R"(\%((\d+)(\$)){0,1}([dsf]))", std::regex::icase);
以下循环开始查找字符串内的 %d %s $f规则并根据 param里的字符进行替换
*/
while (std::regex_search(start, end, matchs, RESOURCE_APP_STRING_PLACEHOLDER)) {
std::string pos = matchs[2];
std::string type = matchs[4];
...
//单次查找字符替换逻辑
originStr.replace(matchs[0].first - originStr.begin(), matchs[0].length(), replaceContentStr);
start = originStr.begin() + matchs.prefix().length() + replaceContentStr.length();
end = originStr.end();
searchTime++;
}
}
ReplaceHolder实现了字符串格式化输出功能,实现了三种格式替换: %s代表的是格式化字符串,%d格式化整数,%f格式化浮点数字
例如
{
"name":"string_hello",
"value":"hello%d"
}
$r('app.string.string_hello',10) // hello10
{
"name":"string_hello",
"value":"hello%d%s"
}
$r('app.string.string_hello',10,"world") // hello10world
4 解决方案
如果要通过 $r() 引入应用资源里的字符串并实现定制化,采用字符模板(例如"hello%d%s")+ 替换参数的方式实现,而不是常规的字符串拼接。例如
// string_hello:
{
"name":"string_hello",
"value":"hello %s"
}
$r('app.string.string_hello',"world") // hello world更多推荐


所有评论(0)