如何准确测量 Android 应用中 Activity 和 Fragment 的启动时间

如何准确测量 Android 应用中 Activity 和 Fragment 的启动时间

在 Android 应用开发中,了解每个 Activity 和 Fragment 的启动时间对于性能优化至关重要。本文将介绍几种方法来准确测量 Activity 和 Fragment 的启动时间,并提供实际操作步骤,以帮助提升应用的响应速度和用户体验。


1. 使用 adb shell am start -W 命令

adb shell am start -W 命令是一种简单且直接的方法,用于测量 Activity 的启动时间。该命令启动指定的 Activity 并输出相关的时间数据。以下是如何使用该命令,以及如何解决常见错误。

1.1 命令

adb shell am start -W -n <your.package.name>/<your.package.name.yourActivity>

1.2 输出解释

  • ThisTime: 当前 Activity 的启动时间。
  • TotalTime: 从应用启动到当前 Activity 的总时间。
  • WaitTime: 系统等待时间。

1.3 启动 Activity 并传递参数

使用 -e 选项传递参数

要传递键值对参数,可以使用 -e 选项。-e 选项用于将一个字符串键值对传递给目标 Activity。如果有多个键值对需要传递,可以使用多个 -e 选项。

命令格式
adb shell am start -n <your.package.name>/<your.package.name.YourActivity> -e <key1> <value1> -e <key2> <value2> ...
  • -n <your.package.name>/<your.package.name.YourActivity>:指定要启动的 Activity
  • -e <key> <value>:指定要传递的参数及其对应的值。key 是参数的名称,value 是参数的值。
示例

应用程序包名为 com.example.app,要启动的 Activitycom.example.app.ui.MainActivity,并且需要传递两个参数:user_idsession_token。可以使用以下命令:

adb shell am start -n com.example.app/com.example.app.ui.MainActivity -e user_id 12345 -e session_token abcdef123456
传递多种数据类型

除了使用 -e 选项传递字符串参数,还可以使用以下选项传递其他数据类型的参数:

  • -e:传递字符串键值对。
  • -en:传递整数键值对。
  • -ef:传递浮点数键值对。
  • -el:传递长整型键值对。
  • -eb:传递布尔键值对。
  • -eia:传递整数数组键值对。
  • -efa:传递浮点数数组键值对。
示例

如果需要传递一个整数和一个布尔值参数,可以使用以下命令:

adb shell am start -n com.example.app/com.example.app.ui.MainActivity -en max_retries 5 -eb is_active true

在这个例子中,max_retries 是一个整数参数,is_active 是一个布尔参数。

解析传递的参数

在的 Activity 中,可以通过 Intent 对象来获取传递的参数。例如:

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Intent intent = getIntent();String userId = intent.getStringExtra("user_id");String sessionToken = intent.getStringExtra("session_token");// 使用获取的参数}
}

对于其他数据类型的参数,可以使用对应的 Intent 方法进行获取,例如 getIntExtragetBooleanExtra 等。


通过使用 -e 及相关选项,可以方便地将参数传递给 Activity,在测试和调试过程中模拟各种条件。这使得验证应用程序的不同功能变得更加高效和灵活。

1.4 常见错误及解决方案

1.4.1 java.lang.SecurityException: Permission Denial

错误信息:

java.lang.SecurityException: Permission Denial: starting Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.myapp/.MainActivity mCallingUid=2000 } from null (pid=29129, uid=2000) not exported from uid 10600

原因: 当尝试启动的 Activity 没有配置为导出的或没有适当的权限时,会出现此错误。

解决方案:

  1. 修改 AndroidManifest.xml: 确保目标 Activity 已配置为 exported="true",这样才能允许外部调用。

    <activityandroid:name=".MainActivity"android:exported="true"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter>
    </activity>
    
  2. 检查权限: 确保应用具有适当的权限,或尝试使用 adb 命令以 root 权限运行。

1.4.2 java.lang.SecurityException: Permission Denial: starting Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.myapp/.MainActivity } from null (pid=29219, uid=2000) not exported from uid 10600

错误信息:

java.lang.SecurityException: Permission Denial: starting Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.myapp/.MainActivity mCallingUid=2000 } from null (pid=29219, uid=2000) not exported from uid 10600

原因: 该错误通常表示目标 Activity 没有正确配置为导出或启动。即使 Activity 是导出的,它可能没有正确设置 intent-filterlaunchMode

解决方案:

  1. 配置 intent-filter: 确保 Activity 正确配置了 intent-filter,如果需要,它应包括 MAINLAUNCHER 动作和类别。

    <activityandroid:name=".MainActivity"android:exported="true"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter>
    </activity>
    
  2. 检查启动模式: 如果 Activity 是内部活动或不需要被外部启动,考虑使用其他方法测试,或者设置 android:exported="true" 确保外部调用。

1.4.3 ActivityNotFoundException

错误信息:

android.content.ActivityNotFoundException: Unable to find explicit activity class {com.example.myapp/.MainActivity}; have you declared this activity in your AndroidManifest.xml?

原因: 该错误表示系统无法找到指定的 Activity。这可能是因为在 AndroidManifest.xml 中没有正确声明该 Activity,或包名和类名错误。

解决方案:

  1. 检查 AndroidManifest.xml: 确保目标 Activity 已在 AndroidManifest.xml 中正确声明。

    <activityandroid:name=".MainActivity"android:exported="true"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter>
    </activity>
    
  2. 核对包名和类名: 确保 adb 命令中的包名和类名与实际声明的一致。


2. 手动记录启动时间

为了获得更准确的数据,可以在代码中手动记录每个 Activity 的启动时间。这种方法能够提供更符合实际使用场景的数据。

2.1 代码

在每个 Activity 的 onCreate 方法中记录启动时间:

public class MainActivity extends AppCompatActivity {private static final String TAG = "MainActivity";@Overrideprotected void onCreate(Bundle savedInstanceState) {long startTime = System.currentTimeMillis();super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);long elapsedTime = System.currentTimeMillis() - startTime;Log.d(TAG, "Activity startup time: " + elapsedTime + " ms");}
}

2.2 捕获日志

使用 adb logcat 捕获启动时间日志:

adb logcat -s MainActivity

3. 使用 Application.ActivityLifecycleCallbacks 接口记录activity启动时长

为了自动记录每个 Activity 的启动时间,避免在每个 Activity 中重复编写代码,可以使用 Application.ActivityLifecycleCallbacks 接口。

3.1使用注解标记中文名

首先,我们创建一个自定义注解来标记 Activity 的中文名。

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;@Retention(RetentionPolicy.RUNTIME)
public @interface ChineseName {String value();
}

然后,在每个 Activity 类上使用这个注解。

import android.app.Activity;
import android.os.Bundle;@ChineseName("我的Activity中文名")
public class MyActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// Activity 其他代码...}
}

3.2创建生命周期回调类

实现 Application.ActivityLifecycleCallbacks

创建一个实现 Application.ActivityLifecycleCallbacks 接口的类,以记录每个 Activity 的启动时间,并使用反射获取中文名。

import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import android.util.Log;public class ActivityLifecycleHandler implements Application.ActivityLifecycleCallbacks {private static final String TAG = "ActivityLifecycleHandler";@Overridepublic void onActivityCreated(Activity activity, Bundle savedInstanceState) {long startTime = System.currentTimeMillis();activity.getWindow().getDecorView().post(() -> {long endTime = System.currentTimeMillis();long duration = endTime - startTime;String activityName = getChineseName(activity);Log.d(TAG, activityName + " creation time: " + duration + " ms");});}@Overridepublic void onActivityStarted(Activity activity) {}@Overridepublic void onActivityResumed(Activity activity) {}@Overridepublic void onActivityPaused(Activity activity) {}@Overridepublic void onActivityStopped(Activity activity) {}@Overridepublic void onActivitySaveInstanceState(Activity activity, Bundle outState) {}@Overridepublic void onActivityDestroyed(Activity activity) {}private String getChineseName(Activity activity) {ChineseName annotation = activity.getClass().getAnnotation(ChineseName.class);if (annotation != null) {return annotation.value();} else {return activity.getClass().getSimpleName();}}
}

3.3 注册生命周期回调

Application 类中注册生命周期回调:

public class MyApp extends Application {@Overridepublic void onCreate() {super.onCreate();registerActivityLifecycleCallbacks(new AppLifecycleHandler());}
}

3.4 修改 AndroidManifest.xml

确保在 AndroidManifest.xml 中指定自定义的 Application 类:

<applicationandroid:name=".MyApp"...>...
</application>

3.5持续日志输出

使用 adb logcat 命令捕获启动时间的日志输出。

adb logcat -s AppLifecycleHandler

每次切换页面或启动新的 Activity 时,控制台会输出类似如下的日志信息:

07-04 14:23:45.123 1234-1234/com.example.myapp D/AppLifecycleHandler: MainActivity startup time: 150 ms
07-04 14:23:47.567 1234-1234/com.example.myapp D/AppLifecycleHandler: SettingsActivity startup time: 180 ms
07-04 14:23:47.567 1234-1234/com.example.myapp D/AppLifecycleHandler: 我的Activity中文名 startup time: 180 ms

这些日志显示了每个 Activity 的启动时间。通过这些数据,可以分析和优化应用性能。

4.记录所有 Fragment 启动时间

同样,可以通过覆盖 Fragment 的生命周期方法来记录 Fragment 的启动时间。

4.1使用注解标记中文名

同样地,为 Fragment 使用相同的 ChineseName 注解。

import androidx.fragment.app.Fragment;@ChineseName("我的Fragment中文名")
public class MyFragment extends Fragment {// Fragment 代码...
}

4.2覆盖 Fragment 生命周期方法

首先,创建一个基类 BaseFragment,覆盖 FragmentonCreateView 方法,以记录每个 Fragment 的启动时间。

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;public class FragmentLifecycleHandler extends FragmentManager.FragmentLifecycleCallbacks {private static final String TAG = "FragmentLifecycleHandler";@Overridepublic void onFragmentViewCreated(@NonNull FragmentManager fm, @NonNull Fragment f, @NonNull View v, @Nullable Bundle savedInstanceState) {long startTime = System.currentTimeMillis();f.getView().post(() -> {long endTime = System.currentTimeMillis();long duration = endTime - startTime;String fragmentName = getChineseName(f);Log.d(TAG, fragmentName + " view creation time: " + duration + " ms");});}private String getChineseName(Fragment fragment) {ChineseName annotation = fragment.getClass().getAnnotation(ChineseName.class);if (annotation != null) {return annotation.value();} else {return fragment.getClass().getSimpleName();}}
}

4.3完整代码示例

所有的 Fragment 类继承自 BaseFragment

public class MyFragment extends BaseFragment {@Nullable@Overridepublic View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {return inflater.inflate(R.layout.fragment_my, container, false);}
}

4.4持续日志输出

使用 adb logcat 命令捕获启动时间的日志输出。

adb logcat -s FragmentLifecycle

每次加载 Fragment 时,控制台会输出类似如下的日志信息:

07-04 14:23:45.123 1234-1234/com.example.myapp D/FragmentLifecycle: MyFragment startup time: 120 ms

这些日志显示了每个 Fragment 的启动时间。通过这些数据,可以分析和优化应用性能。

5. 使用第三方工具

除了自定义实现外,还有许多第三方工具和库可以帮助测量和优化应用的启动时间。以下是一些常用的工具和库:

5.1 阿里云移动监控(Aliyun Mobile Monitoring)

阿里云移动监控 提供了全面的性能监控解决方案,包括启动时间、崩溃分析、用户行为分析等功能。

使用步骤:
  1. 集成 SDK
    • 在阿里云控制台创建一个项目,并下载阿里云移动监控 SDK。
    • 将 SDK 集成到的 Android 应用中。具体步骤可以参考 阿里云官方文档。
  2. 配置性能监控
    • 在阿里云控制台中配置性能监控,包括启动时间监控和用户行为分析。
  3. 查看性能数据
    • 登录阿里云控制台,访问 阿里云移动监控 部分,可以查看应用的启动时间、崩溃报告以及用户行为分析数据。

5.2 腾讯云移动分析(Tencent Cloud Mobile Analytics)

腾讯云移动分析 提供了全面的应用性能监控和用户行为分析功能,适用于中国国内的应用开发者。

使用步骤:
  1. 集成 SDK
    • 在腾讯云控制台创建一个项目,并下载腾讯云移动分析 SDK。
    • 将 SDK 集成到的 Android 应用中。具体步骤可以参考 腾讯云官方文档.
  2. 配置性能监控
    • 在腾讯云控制台中配置性能监控和用户行为分析功能。
  3. 查看性能数据
    • 登录腾讯云控制台,访问 腾讯云移动分析 部分,可以查看应用的启动时间、崩溃报告以及用户行为数据。

5.3 百度移动统计(Baidu Mobile Statistics)

百度移动统计 提供了应用性能监控和用户行为分析功能,帮助开发者了解应用的性能和用户行为。

使用步骤:
  1. 集成 SDK
    • 在百度统计控制台创建一个项目,并下载百度移动统计 SDK。
    • 将 SDK 集成到的 Android 应用中。具体步骤可以参考 百度统计官方文档.
  2. 配置性能监控
    • 在百度统计控制台中配置性能监控功能和用户行为分析。
  3. 查看性能数据
    • 登录百度统计控制台,访问 百度移动统计 部分,可以查看应用的启动时间、崩溃报告和用户行为数据。

5.3 Android Profiler

Android Profiler 是 Android Studio 提供的一套工具,可以帮助实时监控应用的性能,包括启动时间、内存使用、CPU 使用等。

使用步骤:
  1. 启动 Android Studio Profiler

    • 打开 Android Studio,运行的应用。
    • 选择 View > Tool Windows > Profiler,然后选择的应用进程。
  2. 监控应用启动时间

    • 在 Profiler 窗口中,选择 CPU 视图。
    • 启动应用时,Profiler 会记录启动过程中 CPU 的使用情况,可以帮助了解启动时间及其瓶颈。
  3. 分析性能数据

    • Profiler 提供的时间线视图和详细的性能指标可以帮助识别启动过程中的性能问题。
    • 可以查看应用的活动生命周期、内存使用情况以及 CPU 占用情况,从而找到优化的切入点。

5.4 LeakCanary

LeakCanary 是一个开源的内存泄漏检测库,可以帮助检测和修复内存泄漏,从而间接优化应用的启动时间和整体性能。

使用步骤:
  1. 集成 LeakCanary

    • build.gradle 文件中添加 LeakCanary 的依赖:

      debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.10'
      
  2. 自动检测内存泄漏

    • LeakCanary 会自动监控应用的内存泄漏并在发现泄漏时发出警告。
  3. 修复内存泄漏

    • 根据 LeakCanary 提供的报告,识别并修复内存泄漏问题,从而提高应用的性能和启动速度。

备注

Firebase Performance Monitoring

Firebase Performance Monitoring 是一个由 Google 提供的性能监控工具,它可以帮助开发者监控应用的性能,包括启动时间、网络请求时间等。然而,由于某些 Google 服务在中国大陆可能会受到访问限制,Firebase Performance Monitoring 的功能和数据传输可能会受到影响。

官方地址:

  • Firebase Performance Monitoring 官方文档

总结

使用这些第三方工具,可以更全面地监控和优化应用的启动时间和整体性能。三方库 和 Android Profiler 提供了实时的性能数据和分析功能,而 LeakCanary 可以帮助发现并修复内存泄漏问题。这些工具和库的结合使用,可以帮助提升应用的用户体验和性能。

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

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

相关文章

Spark SQL----内置函数JSON Functions

Spark SQL----内置函数JSON Functions JSON Functions 例子&#xff1a; -- from_json SELECT from_json({"a":1, "b":0.8}, a INT, b DOUBLE); --------------------------- |from_json({"a":1, "b":0.8})| -----------------------…

c++之auto

auto auto与for结合begin(),end()说明 auto c11标准引入auto类型说明符必须有初始值通过初始值来推断变量的类型 #include<cstdio> using namespace std; int main(){int v1 10;auto v2 v1;printf("v2%d\n",v2);double v310.5;auto v4 v3;printf("v4…

SF-HCI-SAP问题收集17:值映射布尔型EC数据

Complacency is the enemy of study 学习的敌人是自己的满足。 SAP SuccessFactors Employee Central 到 SAP ERP 的员工主数据复制 successfactor employee center主数据同步&#xff0c;一直以来排错比较难&#xff0c;难的地方是这个提示消息比较隐晦&#xff0c;而且同步的…

数据结构与算法学习(1)

#学习自用# 算法性能分析 时间复杂度O() 时间复杂度就是算法计算的次数。 for(int i0;i<n;i) {ans; } ans; 这串代码时间复杂度为O(n)&#xff0c;实际时间复杂度为O(n1)。如果把i改为i2&#xff0c;时间复杂度仍然为为O(n)&#xff0c;实际时间复杂度变为O(n/2 1)。时…

云原生技术架构详解

云原生技术最全详解(图文全面总结) 容器技术 容器技术&#xff1a;是将应用程序、及其所有依赖项&#xff0c;打包到一个独立的、可移植的容器中。 如下图所示: 容器技术的实现&#xff0c;最典型的就是以Docker为代表的。 如下图所示&#xff1a; 主要解决&#xff1a; 1、…

AI常见名词盘点(持续更新)

目录 知识库 知识库的定义 知识库的分类 AI知识库的特点 小结 Embedding 向量化表示 维度降低 语义关系 小结 提示词工程&#xff08;Prompt Engineering&#xff09; 定义 目的与应用 关键性质 工程化思想 应用示例 小结 RAG 检索增强生成 定义与重要性 RA…

Ubuntu设置nacos开机以单机模式自启动

首先&#xff0c;需要安装jdk Ubuntu 安装JDK 创建Systemd服务单元文件 sudo vim /etc/systemd/system/nacos.service按i进入编辑模式&#xff0c;写入下面信息 [Unit] Descriptionnacos server Afternetwork.target[Service] Typeforking Environment"JAVA_HOME/opt/j…

Java8 - Optional 处理可能为空值的容器类

1. 创建一个 Optional 对象 Optional.of、Optional.ofNullable 、Optional.empty是Optional类的三个静态方法&#xff0c;用于创建Optional对象。 1. Optional.of 方法 Optional.of 方法用于创建一个包含非空值的Optional对象&#xff0c;如果传入的值为null&#xff0c;则会…

Kafka集群安装部署

简介 Kafka是一款分布式的、去中心化的、高吞吐低延迟、订阅模式的消息队列系统。 同RabbitMQ一样&#xff0c;Kafka也是消息队列。不过RabbitMQ多用于后端系统&#xff0c;因其更加专注于消息的延迟和容错。 Kafka多用于大数据体系&#xff0c;因其更加专注于数据的吞吐能力…

用freertos后NVIC里系统时钟部分报错,如何解决?

&#x1f3c6;本文收录于《CSDN问答解答》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&…

百日筑基第十天-重温Spring

百日筑基第十天-重温Spring Spring AOP 也就是 Aspect-oriented Programming&#xff0c;译为面向切面编程&#xff0c;是计算机科学中的一个设计思想&#xff0c;旨在通过切面技术为业务主体增加额外的通知&#xff08;Advice&#xff09;&#xff0c;从而对声明为**“切点”…

YOLOv8模型调参---数据增强

目录 1.数据预处理 2.数据增强 2.1 数据增强的作用 2.2 数据增强方式与适用场景 2.2.1离线增强&#xff08;Offline Augmentation&#xff09; 2.2.2 在线增强&#xff08;Online Augmentation&#xff09; 3. 数据增强的具体方法 4. YOLOv8的数据增强 4.1 YOLOv8默认…

Http 实现请求body体和响应body体的双向压缩方案

目录 一、前言 二、方案一(和http header不进行关联) 二、方案二(和http header进行关联) 三、 客户端支持Accept-Encoding压缩方式,服务器就一定会进行压缩吗? 四、参考 一、前言 有时请求和响应的body体比较大,需要进行压缩,以减少传输的带宽。 二、方案一(和…

《信息记录材料》是什么级别的期刊?是正规期刊吗?能评职称吗?

​问题解答 问&#xff1a;《信息记录材料》是不是核心期刊&#xff1f; 答&#xff1a;不是&#xff0c;是知网收录的第一批认定学术期刊。 问&#xff1a;《信息记录材料》级别&#xff1f; 答&#xff1a;国家级。主管单位&#xff1a;全国磁性记录材料信息站 主办单位…

Oracle PL / SQL 函数

FUNCTION是返回值的PL / SQL块或方法&#xff0c;因此它可以在赋值的右侧使用。这里是一个例子&#xff1a; n_value : to_number(123.45); 由于FUNCTION返回一个值&#xff0c;因此也可以在SQL语句中使用它&#xff0c;如下例所示&#xff1a; select to_number(1) from dual;…

社区活动|FlowUs知识库的发展|先进技术的落地应用|下一代生产力工具你用了吗

在当今快速发展的数字化时代&#xff0c;技术的进步不断推动着工作方式和知识管理的革新。FlowUs&#xff0c;作为一款前沿的知识管理和协作平台&#xff0c;正站在这一变革的浪潮之巅&#xff0c;引领着智能工作的新潮流。 智能化的智能学习引导工具 FlowUs不仅仅是一个工具&…

Windows系统常用工具及命令和bat文件介绍

常用的windos工具 命令工具名称描述powershellwindows的shell工具eventvwr事件查看器可以查看系统日志taskmgr任务管理器查看已经运行的进程和性能、应用历史记录、开机启动等信息services.msc服务管理可以查看本地的服务regedt注册表编辑器mstsc远程桌面连接devmgmt.msc设备管…

昇思25天学习打卡营第7天|深度学习流程全解析:从模型训练到评估

目录 构建数据集 定义神经网络模型 定义超参、损失函数和优化器 超参 损失函数 优化器 训练与评估 构建数据集 首先从数据集 Dataset加载代码&#xff0c;构建数据集。 代码如下&#xff1a; #引入了必要的库和模块&#xff0c;像 mindspore 以及相关的数据处理模块等等。…

Vue2-Vue Router前端路由实现思路

1.路由是什么&#xff1f; Router路由器&#xff1a;数据包转发设备&#xff0c;路由器通过转发数据包&#xff08;数据分组&#xff09;来实现网络互连 Route路由&#xff1a;数据分组从源到目的地时&#xff0c;决定端到端路径的网络范围的进程 | - 网络层 Distribute分发…

无人机5公里WiFi低延迟图传模组,抗干扰、长距离、低延迟,飞睿智能无线通信新标杆

在科技日新月异的今天&#xff0c;我们见证了无数通信技术的飞跃。从开始的电报、电话&#xff0c;到如今的4G、5G网络&#xff0c;再到WiFi的广泛应用&#xff0c;每一次技术的革新都极大地改变了人们的生活方式。飞睿智能5公里WiFi低延迟图传模组&#xff0c;它以其独特的优势…