OpenHarmony实战开发:Launcher应用安装问题分析报告

news2024/11/16 21:41:51

1 关键字

Launcher;Storage

2 问题描述

问题现象:安装应用到达 20 个以上后,重启设备,Launcher 页面没有任何应用图标

运行环境:硬件 dayu200,软件:3.1release

测试步骤:

  1. 使用 hdc 将测试安装包安装到设备上,安装数量在 20 个以上,尽可能稍多一些
  2. 重启设备
  3. Launcher 桌面显示异常,不显示任何应用图标

3 问题原因

3.1 正常机制

  • Launcher 的桌面应用布局信息是通过 Storage 存储在文件中。
  • Launcher 在系统安装应用后会更新桌面布局配置,调用 putSync 将当前布局写入内存中,然后通过 flushSync 持久化到文件中。
  • 桌面布局配置持久化后,设备重启,Launcher 页面正常加载。

3.2 异常机制

  • 当桌面应用安装过多,桌面布局配置信息超过 8192 字节时,底层通过定长数组接收参数,导致数组越界未定义,从而无法正确获取桌面布局配置信息,属性为空,从而将内存中属性更新为空了。
  • 持久化过程中再将内存中的属性更新到文件中,从而导致文件信息也被更新为空。
  • 重启设备,重新加载文件,桌面布局配置为空,从而无法显示任何应用图标。

Storage 官方文档说明:key 的最大长度限制,需小于 80 字节。value 的最大长度限制,需小于 8192 字节。

4 解决方案

  • 应用层面,可通过对超长字段进行拆分存储,规避该问题
  • 存储方式,可通过使用关系型数据库 @ohos.data.rdb 的 rdbStore 或分布式数据管理 @ohos.data.distributedData 的 kvStore 来进行数据存储
  • 底层实现,系统目前文件存储的最大长度限制为 8192 字节,可通过修改底层存储逻辑,修改限定长度或自动扩容实现

5 定位过程

该问题为底层实现与特殊场景不兼容导致的运行异常:

  • 安装应用 20+ 复现相关问题。

  • 关注系统桌面布局配置 LauncherPreference 文件中的 DesktopApplicationInfo 信息更新情况。
    文件路径:/data/app/el2/100/database/com.ohos.launcher/pad/LauncherPreference
    文件内容:

    <?xml version="1.0" encoding="UTF-8"?>
    <preferences version="1.0">
      <string key="DesktopApplicationInfo">[{"appName":"$string:MainAbility_label","isSystemApp":true,"isUninstallAble":false,"appIconId":16777219,"appLabelId":16777217,"bundleName":"com.example.AircraftWar","abilityName":"com.example.AircraftWar.MainAbility","type":0,"area":[1,1]},{"appName":"$string:MainAbility_label","isSystemApp":true,"isUninstallAble":false,"appIconId":16777219,"appLabelId":16777217,"bundleName":"com.example.Billiards","abilityName":"com.example.Billiards.MainAbility","type":0,"area":[1,1]},{"appName":"$string:app_name","isSystemApp":true,"isUninstallAble":false,"appIconId":16777229,"appLabelId":16777219,"bundleName":"com.example.Browser","abilityName":"MainAbility","type":0,"area":[1,1]},{"appName":"$string:app_name","isSystemApp":false,"isUninstallAble":true,"appIconId":16777219,"appLabelId":16777216,"bundleName":"com.example.baseanimation","abilityName":"com.example.baseanimation.MainAbility","type":0,"area":[1,1]},{"appName":"$string:app_name","isSystemApp":true,"isUninstallAble":false,"appIconId":16777218,"appLabelId":16777216,"bundleName":"com.example.distributedcalc","abilityName":"com.example.distributedcalc.default","type":0,"area":[1,1]},{"appName":"$string:entry_MainAbility","isSystemApp":true,"isUninstallAble":false,"appIconId":16777218,"appLabelId":16777217,"bundleName":"com.example.shopping","abilityName":"com.example.shopping.MainAbility","type":0,"area":[1,1]},{"appName":"$string:app_name","isSystemApp":false,"isUninstallAble":true,"appIconId":16777217,"appLabelId":16777219,"bundleName":"com.huawei.himovie","abilityName":"MainAbility","type":0,"area":[1,1]},{"appName":"$string:entry_Music","isSystemApp":true,"isUninstallAble":false,"appIconId":16777311,"appLabelId":16777219,"bundleName":"com.huawei.himusicdemo","abilityName":"com.huawei.himusicdemo.Music","type":0,"area":[1,1]},{"appName":"$string:app_name","isSystemApp":true,"isUninstallAble":false,"appIconId":150995030,"appLabelId":150994944,"bundleName":"com.ohos.camera","abilityName":"com.ohos.camera.MainAbility","type":0,"area":[1,1]},{"appName":"$string:app_name","isSystemApp":true,"isUninstallAble":false,"appIconId":16777431,"appLabelId":16777225,"bundleName":"com.ohos.contacts","abilityName":"com.ohos.contacts.MainAbility","type":0,"area":[1,1]},{"appName":"$string:messages","isSystemApp":true,"isUninstallAble":false,"appIconId":16777565,"appLabelId":16777321,"bundleName":"com.ohos.mms","abilityName":"com.ohos.mms.MainAbility","type":0,"area":[1,1]},{"appName":"$string:entry_MainAbility","isSystemApp":true,"isUninstallAble":false,"appIconId":134217900,"appLabelId":134217748,"bundleName":"com.ohos.note","abilityName":"com.ohos.note.MainAbility","type":0,"area":[1,1]},{"appName":"$string:app_name","isSystemApp":true,"isUninstallAble":false,"appIconId":16777834,"appLabelId":16777216,"bundleName":"com.ohos.photos","abilityName":"com.ohos.photos.MainAbility","type":0,"area":[1,1]},{"appName":"$string:entry_MainAbility","isSystemApp":true,"isUninstallAble":false,"appIconId":50332153,"appLabelId":50331728,"bundleName":"com.ohos.settings","abilityName":"com.ohos.settings.MainAbility","type":0,"area":[1,1]},{"appName":"$string:app_name","isSystemApp":true,"isUninstallAble":false,"appIconId":16777218,"appLabelId":16777216,"bundleName":"ohos.samples.airquality","abilityName":"ohos.samples.airquality.default","type":0,"area":[1,1]},{"appName":"$string:app_name","isSystemApp":true,"isUninstallAble":false,"appIconId":16777218,"appLabelId":16777216,"bundleName":"ohos.samples.clock","abilityName":"ohos.samples.clock.default","type":0,"area":[1,1]},{"appName":"$string:entry_MainAbility","isSystemApp":true,"isUninstallAble":false,"appIconId":16777219,"appLabelId":16777217,"bundleName":"ohos.samples.webdemo","abilityName":"ohos.samples.webdemo.MainAbility","type":0,"area":[1,1]}]</string>
      <string key="DesktopModeConfig">{"appStartPageType":"Grid","gridConfig":0,"deviceType":"pad"}</string>
      <string key="GridLayoutInfo">{"layoutDescription":{"pageCount":1,"row":5,"column":11},"layoutInfo":[{"bundleName":"com.example.AircraftWar","type":0,"area":[1,1],"page":0,"column":0,"row":0},{"bundleName":"com.example.Billiards","type":0,"area":[1,1],"page":0,"column":1,"row":0},{"bundleName":"com.example.Browser","type":0,"area":[1,1],"page":0,"column":2,"row":0},{"bundleName":"com.example.baseanimation","type":0,"area":[1,1],"page":0,"column":3,"row":0},{"bundleName":"com.example.distributedcalc","type":0,"area":[1,1],"page":0,"column":4,"row":0},{"bundleName":"com.example.shopping","type":0,"area":[1,1],"page":0,"column":5,"row":0},{"bundleName":"com.huawei.himovie","type":0,"area":[1,1],"page":0,"column":6,"row":0},{"bundleName":"com.huawei.himusicdemo","type":0,"area":[1,1],"page":0,"column":7,"row":0},{"bundleName":"com.ohos.camera","type":0,"area":[1,1],"page":0,"column":8,"row":0},{"bundleName":"com.ohos.contacts","type":0,"area":[1,1],"page":0,"column":9,"row":0},{"bundleName":"com.ohos.mms","type":0,"area":[1,1],"page":0,"column":10,"row":0},{"bundleName":"com.ohos.note","type":0,"area":[1,1],"page":0,"column":0,"row":1},{"bundleName":"com.ohos.photos","type":0,"area":[1,1],"page":0,"column":1,"row":1},{"bundleName":"com.ohos.settings","type":0,"area":[1,1],"page":0,"column":2,"row":1},{"bundleName":"ohos.samples.airquality","type":0,"area":[1,1],"page":0,"column":3,"row":1},{"bundleName":"ohos.samples.clock","type":0,"area":[1,1],"page":0,"column":4,"row":1},{"bundleName":"ohos.samples.webdemo","type":0,"area":[1,1],"page":0,"column":5,"row":1}]}</string>
      <string key="SmartDockLayoutInfo">[{"itemType":2,"editable":false,"bundleName":"com.ohos.launcher","abilityName":"com.ohos.launcher.appcenter.MainAbility","appIconId":184549428,"appLabelId":184549400,"appName":"全部应用"},{"itemType":2,"editable":false,"bundleName":"com.ohos.launcher","abilityName":"com.ohos.launcher.recents.MainAbility","appIconId":184549429,"appLabelId":184549401,"appName":"最近任务"},{"itemType":0,"editable":true,"appName":"图库","bundleName":"com.ohos.photos","abilityName":"com.ohos.photos.MainAbility","appIconId":16777834,"appLabelId":16777216},{"itemType":0,"editable":false,"appName":"设置","bundleName":"com.ohos.settings","abilityName":"com.ohos.settings.MainAbility","appIconId":50332153,"appLabelId":50331728}]</string>
    </preferences>
    
  • 关注属性写入内存 putSync 与内存信息持久化到文件 flushSync 调用前后的属性变量。

  • 通过日志定位到问题原因:putSync 调用导致的异常。

  • 分析 api 底层实现:底层实现调用 napi_storage.cpp 的 StorageProxy::SetValueSync。

  • SetValueSync 具体逻辑:声明定长 char 数组用来接收 key(MAX_KEY_LENGTH = 80),获取 value 类型,当类型为 string 时,声明定长 char 数组用来接收 value(MAX_VALUE_LENGTH = 8 * 1024),然后调用 preferences_impl.cpp 的 PutString。

    napi_value StorageProxy::SetValueSync(napi_env env, napi_callback_info info)
    {
        napi_value thiz = nullptr;
        size_t argc = 2;
        napi_value args[2] = { 0 };
    
        LOG_DEBUG("SETVALUE");
        NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thiz, nullptr));
        NAPI_ASSERT(env, argc == 2, "Wrong number of arguments");
        // get value type
        napi_valuetype valueType = napi_undefined;
        NAPI_CALL(env, napi_typeof(env, args[0], &valueType));
        NAPI_ASSERT(env, valueType == napi_string, "type mismatch for key");
    
        // get input key
        char key[MAX_KEY_LENGTH] = { 0 };
        size_t out = 0;
        NAPI_CALL(env, napi_get_value_string_utf8(env, args[0], key, MAX_KEY_LENGTH, &out));
    
        StorageProxy *obj = nullptr;
        NAPI_CALL(env, napi_unwrap(env, thiz, reinterpret_cast<void **>(&obj)));
        NAPI_ASSERT(env, (obj != nullptr && obj->value_ != nullptr), "unwrap null native pointer");
    
        NAPI_CALL(env, napi_typeof(env, args[1], &valueType));
        if (valueType == napi_number) {
            double value = 0.0;
            NAPI_CALL(env, napi_get_value_double(env, args[1], &value));
            int result = obj->value_->PutDouble(key, (double)value);
            NAPI_ASSERT(env, result == E_OK, "call PutDouble failed");
        } else if (valueType == napi_string) {
            char *value = new char[MAX_VALUE_LENGTH];
    
            napi_status status = napi_get_value_string_utf8(env, args[1], value, MAX_VALUE_LENGTH, &out);
            if (status != napi_ok) {
                LOG_DEBUG("napi_get_value_string_utf8 failed");
                LOG_DEBUG(value);
            } else {
                // get value
                int result = obj->value_->PutString(key, value);
            }        
            delete[] value;
            NAPI_ASSERT(env, result == E_OK, "call PutString failed");
        } else if (valueType == napi_boolean) {
            bool value = false;
            NAPI_CALL(env, napi_get_value_bool(env, args[1], &value));
            // get value
            int result = obj->value_->PutBool(key, value);
            NAPI_ASSERT(env, result == E_OK, "call PutBool failed");
        } else {
            NAPI_ASSERT(env, false, "Wrong second parameter type");
        }
        return nullptr;
    }
    

    注:当属性长度超长后会导致数组越界未定义,从而无法正常获取属性值,导致属性值为空,从而导致内存中存放属性有误

  • PutString 具体逻辑:调用 CheckStringValue 进行 value 值校验(校验长度),校验通过调用 PutPreferencesValue。

    int PreferencesImpl::PutString(const std::string &key, const std::string &value)
    {
        int errCode = CheckKey(key);
        if (errCode != E_OK) {
            return errCode;
        }
        errCode = CheckStringValue(value);
        if (errCode != E_OK) {
            return errCode;
        }
        PutPreferencesValue(key, PreferencesValue(value));
        return E_OK;
    }
    
    int PreferencesImpl::CheckKey(const std::string &key)
    {
        if (key.empty()) {
            LOG_ERROR("The key string is null or empty.");
            return E_KEY_EMPTY;
        }
        if (Preferences::MAX_KEY_LENGTH < key.length()) {
            LOG_ERROR("The key string length should shorter than 80.");
            return E_KEY_EXCEED_MAX_LENGTH;
        }
        return E_OK;
    }
    
    int PreferencesImpl::CheckStringValue(const std::string &value)
    {
        if (Preferences::MAX_VALUE_LENGTH < value.length()) {
            LOG_ERROR("The value string length should shorter than 8 * 1024.");
            return E_VALUE_EXCEED_MAX_LENGTH;
        }
        return E_OK;
    }
    
  • PutPreferencesValue 具体逻辑:AwaitLoadFile 获取资源锁,调用 insert_or_assign 存放属性到内存 map 中,调用 push_back 记录已修改属性的 key 值到内存 map 中。

    void PreferencesImpl::PutPreferencesValue(const std::string &key, const PreferencesValue &value)
    {
        AwaitLoadFile();
    
        std::lock_guard<std::mutex> lock(mutex_);
    
        auto iter = map_.find(key);
        if (iter != map_.end()) {
            PreferencesValue &val = iter->second;
            if (val == value) {
                return;
            }
        }
    
        map_.insert_or_assign(key, value);
        modifiedKeys_.push_back(key);
    }
    
  • 属性写入内存后,再由 flushSync 来进行数据的持久化操作,将内存数据写入到文件中。

6 知识分享

  • OpenHarmony 文件存储 @ohos.data.storage 目前的长度限制:key 的最大长度限制,需小于 80 字节,value 的最大长度限制,需小于 8192 字节。
  • 数据库存储目前逻辑上是没有长度限制的,长度限制需参考数据库属性配置,sqlite 中 text 存储可变长度的非 Unicode 数据,最大长度为 2^31-1(2,147,483,647)个字符。

最后

小编在之前的鸿蒙系统扫盲中,有很多朋友给我留言,不同的角度的问了一些问题,我明显感觉到一点,那就是许多人参与鸿蒙开发,但是又不知道从哪里下手,因为资料太多,太杂,教授的人也多,无从选择。有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)资料用来跟着学习是非常有必要的。 

为了确保高效学习,建议规划清晰的学习路线,涵盖以下关键阶段:


 鸿蒙(HarmonyOS NEXT)最新学习路线

该路线图包含基础技能、就业必备技能、多媒体技术、六大电商APP、进阶高级技能、实战就业级设备开发,不仅补充了华为官网未涉及的解决方案

路线图适合人群:

IT开发人员:想要拓展职业边界
零基础小白:鸿蒙爱好者,希望从0到1学习,增加一项技能。
技术提升/进阶跳槽:发展瓶颈期,提升职场竞争力,快速掌握鸿蒙技术

2.视频学习资料+学习PDF文档

HarmonyOS Next 最新全套视频教程

  纯血版鸿蒙全套学习资料(面试、文档、全套视频等)              

​​

总结

参与鸿蒙开发,你要先认清适合你的方向,如果是想从事鸿蒙应用开发方向的话,可以参考本文的学习路径,简单来说就是:为了确保高效学习,建议规划清晰的学习路线

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2076233.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DrawDB数据库设计工具本地部署结合内网穿透实现团队异地协作办公

文章目录 前言1. Windows本地部署DrawDB2. 安装Cpolar内网穿透3. 实现公网访问DrawDB4. 固定DrawDB公网地址 &#x1f4a1; 推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击跳转到网站】 前言…

Redis集群技术

目录 一、Redis安装部署 二、Redis主从复制 三、Redis哨兵模式 1、开启Redis哨兵模式 2、Redis哨兵模式选举MASTER全过程 3、Redis哨兵模式数据丢失风险的解决方案 四、Redis Cluster 1、Redis集群部署 2、Redis集群扩容 1、加入MASTER集群 2、分配槽位 3、添加slav…

Linux:开发工具(3)

一、版本控制器git 安装git指令 yum install git 1.1 举例理解版本控制器 举个例子&#xff1a; 比方说老师要求我们交一份实验报告&#xff0c;而你的室友张三是一个非常勤奋的人&#xff0c;所以他很早就写好了第一版交给老师&#xff0c;但老师的要求很高&#xff0c;他觉得…

将 github 仓库同步到个人服务器

场景 把本地项目推送到Github仓库后&#xff0c;如果想部署到服务器上&#xff0c;可以尝试此方案 我的博客是hugo搭建的&#xff0c;每次更新文章会手动推送到Github仓库&#xff0c; 然后利用Actions 脚本自动推送到cf或者vercel 之前我写过一篇 将 hugo 博客搬迁到服务器…

STM案例一:灯闪烁

一、使用元件 STlink&#xff0c;STM32F103C8T6 二、接线方法 STM32与STLINK的接线方法为&#xff1a; GND-->GND DCLK-->SWCLK DIO-->SWDIO 3.3-->3.3V 三、配置调试器 选择魔术棒按钮&#xff0c;单击Debug&#xff0c;选择ST-link Debug&#xff0c;选…

免费JSON在线解析工具网址

1&#xff0c;https://tool.juhe.cn/ JSON在线解析 (juhe.cn) 2&#xff0c;https://www.sojson.com/ JSON在线 | JSON解析格式化—SO JSON在线工具

阿卡迈2.0逆向

阿卡迈&#xff08;akamai&#xff09;我也是第一次第一次搞 阿卡迈检测挺多例如 ua 屏幕 document 自动化检测等等 &#xff0c;还有tls指纹 仅供学习参考吧 当cookie的_abck为 0表示通过&#xff0c;1表示不通过&#xff01;&#xff01;&#xff01; 测试了两个2.0站全都通…

一款特别牛逼还免费的屏幕演示批注注释工具,教学演示工具

功能强大&#xff0c;还免费&#xff01;PEVO Annotator 是一款功能强大的屏幕标注软件&#xff0c;广泛应用于教学演示、屏幕录像等场景。该软件支持多种操作系统&#xff0c;包括 macOS 和 Windows 。它提供了丰富的工具和功能&#xff0c;如画笔、直线、圆形、方形等形状绘制…

【海思3516DV500 | 开发环境】编译整个SDK全过程以及问题汇总

一、开发环境 上表是海思提供的Hi3519DV500 开发环境的各部分软件描述&#xff0c;实际用起来你就知道&#xff0c;可能有出入&#xff0c;尝试换Ubuntu 20.04折腾了好几天都不行&#xff0c;使用推荐的Ubuntu 18.04.2 64bit一个上午就行。 基本的硬件要求如下&#xff1a; ●…

Qt 解析多级XML

补充笔记&#xff1a;Qt 史上最详细 - “操作XML文件” - 笔记 一、如下图为XML文件 annotation为根节点&#xff0c; first第一节点&#xff0c;second第二节点&#xff0c;third第三节点。 二、实现解析xml文件&#xff0c;并将解析数据用QTreeView显示&#xff0c;实现代码…

AD9161调试问题

AD961同步信号SYNC无法保持高逻辑电 现象 配置ADF4351时钟芯片使其产生频率为1.6GHz的时钟为AD9161提供工作时钟&#xff0c;JESD的线速率配置为8Gbps&#xff0c;并配置AD9161的寄存器使其工作在2x内插模式以实现IQ信号的模数转换模式。同时根据AD9161芯片手册对ADF4351提供的…

NoSql数据库 Redis集群详解

目录 一、NoSql数据库简介 1.1 数据库主要分为两大类&#xff1a;关系型数据库与 NoSQL 数据库 1.2 为什么还要用 NoSQL 数据库呢&#xff1f; 1.3 RDBMS和NOSQL的特点及优缺点&#xff1a; 二 Remote Dictionary Server 简介&#xff08;redis&#xff09; 2.1 什么是redis …

如何使用ssm实现物资进销存jsp

TOC ssm263物资进销存jsp 绪论 1.1 研究背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进行科学化&#xff0c;规范化管理。这样的…

JavaScript学习文档(4):循环结构、初识数组、数组案例、操作数组

目录 一、循环结构 1、for语句 2、for循环案例 3、for循环嵌套及案例 4、while语句 5、continue关键字 6、break关键字 二、初识数组 1、什么是数组 2、为什么要数组 3、数组的基本使用 三、数组案例 1、数组求和及平均值 2、数组求最大值和最小值 四、操作数组…

做集运初期到底要不要上系统?

在集运业务的起步阶段&#xff0c;很多老板都会有这样的疑问&#xff1a;“我的包裹量还不多&#xff0c;真的需要投入资金和时间来上系统吗&#xff1f;”这个问题听起来合理&#xff0c;但实际上却忽视了业务发展中更为重要的几个方面。 1.用户体验&#xff1a;服务的核心 首…

C语言经典案例分享

题目&#xff1a;输入三个整数 x、y、z&#xff0c;请把这三个数由小到大输出。 程序分析&#xff1a;我们想办法把最小的数放到 x 上&#xff0c;先将 x 与 y 进行比较&#xff0c;如果 x>y 则将 x 与 y 的值进行交换&#xff0c;然后再用 x 与 z 进行比较&#xff0c;如果…

NVIDIA Jetson AGX Orin源码编译安装CV-CUDA

目录 1 下载源码并配置 2 编译安装CV-CUDA 2.1 安装相应依赖包 2.2 升级gcc到gcc-11 2.3 build 2.4 升级cmake 2.5 再次build 2.5.1 报错 /usr/include/c/11/bits/std_function.h:435:145: error: parameter packs not expanded with ‘...’: 3 直接使用安装包 参考…

力扣: 两两交换链表中的节点

文章目录 需求代码代码解释结尾 需求 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 示例 1&#xff1a; 输入&#xff1a;…

jmeter性能测试之CSV 数据文件设置

文章目录 业务场景使用步骤步骤1&#xff1a;准备数据步骤二: 编写csv文件步骤三:添加CSV 数据文件设置步骤四:定义接口&#xff0c;选择文件上传&#xff0c;文件名称通过“浏览”添加即可 业务场景 有一个文件上传的接口&#xff0c;希望每个线程上传不同的文件&#xff08;比…

FOFA搜索引擎的语法

FOFA 是一款网络空间搜索引擎&#xff0c;允许用户通过自定义查询语法来检索全球范围内的互联网资产。FOFA 提供了丰富的语法&#xff0c;可以帮助用户筛选目标。以下是 FOFA 语法的基本介绍&#xff1a; 1. 基本语法格式 FOFA 的查询语句遵循以下格式&#xff1a; 字段&quo…