基于 Qt、Flutter、React Native 的跨平台 Camera 调用实践

关键词

跨平台相机调用、Qt 多媒体模块、Flutter Camera 插件、React Native CameraX、平台通道通信、原生桥接、图像流封装、相机权限管理

摘要

随着跨平台 UI 框架在移动与桌面终端上的广泛使用,如何在 Qt、Flutter、React Native 等框架中实现统一的相机功能调用,成为很多开发团队必须解决的技术难题。不同框架在 Camera 调用方式、原生能力桥接机制、异步通信模型和平台兼容性支持方面各不相同,且 Camera 通常涉及权限、硬件访问、图像流处理等复杂细节,增加了实现难度。本文将结合真实工程实践,分别介绍如何在 Qt、Flutter 和 React Native 框架下调用系统相机,涵盖拍照与视频预览、图像数据获取、参数控制、权限适配、原生通信桥接等核心能力,同时分析各框架在开发与调试过程中的关键差异与优化策略,帮助开发者在跨平台项目中高效实现影像系统集成。

目录

  1. 跨平台调用 Camera 的工程挑战与通用架构思路
  2. Qt 中的相机接口封装实践:QCamera + QML 拍照与图像流获取
  3. Flutter 框架中的 Camera 调用:插件体系与平台通道通信
  4. React Native 中使用 CameraX 与 AVFoundation:原生桥接策略详解
  5. 图像数据流获取与处理:格式转换、YUV 转 RGB、图像保存
  6. 相机权限适配与运行时校验:多平台统一管理策略
  7. 异步事件回调机制与 UI 控制状态同步处理
  8. 项目实战总结:多框架下的 Camera 开发经验对比与最佳实践

1. 跨平台调用 Camera 的工程挑战与通用架构思路

在当今主流的跨平台开发实践中,Qt、Flutter、React Native 被广泛应用于工业设备界面、消费级移动端应用、桌面工具等多个领域。而 Camera 能力作为多数应用中对底层硬件访问的典型需求,涉及图像流采集、设备权限、图像格式处理和原生组件通信,在跨平台场景中实现难度远高于普通 UI 模块。

以下是工程中常见的挑战点:

1.1 原生能力差异巨大

相机作为系统底层服务,在 Android、iOS、Windows、macOS、Linux 等平台上接口机制差异显著:

  • Android 侧支持 Camera1、Camera2、CameraX,需处理回调线程、安全权限、预览 Surface;
  • iOS 通过 AVFoundation 实现,需要对 CaptureSession、Input、Output 管理;
  • Windows 和 Linux 通常使用 OpenCV/V4L2 接口;
  • 原生模块不直接开放给跨平台框架,需要手动桥接或扩展。

1.2 插件封装差异

各框架对原生模块的封装策略差异明显:

  • Qt 可通过 QCamera/QMediaRecorder 提供统一接口,但在 Android/iOS 上仍依赖底层 HAL;
  • Flutter 提供了标准插件 camera,但扩展性较弱,复杂功能常需写平台通道逻辑;
  • React Native 的开源模块种类多,但需自行维护原生桥接层,兼容性问题多见。

1.3 数据处理链复杂

获取相机图像流后,应用常需对帧数据进行进一步处理:

  • YUV 转 RGB;
  • 压缩编码(JPEG);
  • 数据帧缓存与异步传输;
  • 图像存储或上行推流。

这些处理流程常常在 native 层完成,但在跨平台项目中,必须设计合适的数据通道和传输接口。

1.4 权限适配与异步事件同步

不同平台在摄像头权限管理策略上存在显著差异,且异步事件(如相机打开、预览启动、错误回调)与 UI 控制状态之间的同步关系复杂,需结合平台特性进行兼容适配。

为解决以上挑战,工程实践中通常采用如下通用架构:

  1. 统一插件接口层:定义平台无关的相机控制 API;
  2. 原生桥接封装层:封装 Android/iOS/Windows 的相机能力为统一接口;
  3. 异步通信机制:采用平台通道、MethodChannel 或 NativeModule 实现上下层事件同步;
  4. 图像处理模块分离:将图像处理独立为平台级模块,仅通过指针或缓存区接口共享数据;
  5. 状态机管理生命周期:使用状态机统一管理 Camera 打开、预览、捕获、释放等阶段。

接下来将以 Qt、Flutter 和 React Native 为例,逐一展开跨平台相机调用实践。

2. Qt 中的相机接口封装实践:QCamera + QML 拍照与图像流获取

Qt 框架作为跨桌面与嵌入式领域的重要技术路线,提供了内建的 Qt Multimedia 模块,支持在 QML 或 C++ 层直接调用相机功能,并封装了视频帧、拍照、录像等主流能力。

2.1 基本构成模块

Qt 相机功能主要依赖以下组件:

  • QCamera:相机设备控制器,负责管理设备生命周期;
  • QCameraViewfinder:预览显示组件;
  • QCameraImageCapture:拍照能力;
  • QMediaRecorder:录像能力;
  • QVideoProbe:实时获取视频帧数据。

2.2 QML 示例:基本拍照功能

在 QML 层可快速实现预览与拍照功能:

Camera {
    id: camera
    captureMode: Camera.CaptureStillImage
}

VideoOutput {
    source: camera
    anchors.fill: parent
}

ImageCapture {
    id: imageCapture
    camera: camera
    onImageSaved: {
        console.log("Image saved to", path)
    }
}

Button {
    text: "Take Photo"
    onClicked: imageCapture.capture()
}

该方案适用于简单 UI 项目,适配 Android/iOS 效果良好。

2.3 C++ 层图像帧处理(YUV 转 RGB)

若需要直接访问帧数据,可使用 QVideoProbe

QVideoProbe* probe = new QVideoProbe;
probe->setSource(camera);
connect(probe, &QVideoProbe::videoFrameProbed, this, [&](const QVideoFrame& frame) {
    QVideoFrame cloneFrame(frame);
    cloneFrame.map(QAbstractVideoBuffer::ReadOnly);
    const uchar* data = cloneFrame.bits();
    // 将 data 转换为 RGB 图像数据
    cloneFrame.unmap();
});

但在实际项目中,Qt 提供的 QVideoFrame 在不同平台上存在实现差异:

  • Android 平台下部分设备返回 NV21;
  • Linux 上通过 V4L2 对接;
  • iOS 平台返回 CVPixelBuffer 封装数据,不完全兼容。

因此建议配合平台特性做数据格式适配处理。

2.4 工程实战注意事项

  • Qt 6 版本对 Android/iOS Camera 支持增强,建议优先使用;
  • iOS 权限需在 Info.plist 添加 NSCameraUsageDescription
  • Android 权限需在 AndroidManifest.xml 添加 CAMERA,并在运行时请求;
  • 对于嵌入式 Linux 设备,建议结合 GStreamer/V4L2 使用 Qt 实现图像流绑定;
  • 若需接入 AI 模型做帧分析,建议从 QVideoFrame 提取裸数据后送入自建 Pipeline。

Qt 提供了相对完整的 Camera 支持体系,适合构建工业控制、桌面拍照、嵌入式采集系统等场景。

3. Flutter 框架中的 Camera 调用:插件体系与平台通道通信

Flutter 是当前主流的跨平台 UI 框架之一,广泛应用于移动端和部分桌面端应用场景。其原生能力调用依赖插件机制,通过 PlatformChannel 与 Android/iOS 原生模块通信。相机能力在 Flutter 中的实现主要依赖官方插件 camera,支持预览、拍照、录像、图像帧捕获等功能,但在一些高级特性和底层参数控制方面仍需通过自定义插件或扩展原生层实现。

3.1 Flutter camera 插件基本结构

官方插件 camera 基于 Dart 编写上层接口,通过 platform channel 分别在 Android 使用 Camera2 和在 iOS 使用 AVFoundation 进行底层实现。

  • Android 侧:使用 CameraManager + CameraDevice + ImageReader
  • iOS 侧:基于 AVCaptureSession + AVCaptureDeviceInput + AVCaptureVideoDataOutput
  • 数据流传输:通过 EventChannel 将帧流事件送入 Dart 层;
  • 拍照/录像控制:使用 MethodChannel 发起动作请求、返回结果。

3.2 基础拍照调用流程

Flutter 使用插件方式调用 Camera 通常包括以下步骤:

List<CameraDescription> cameras = await availableCameras();
CameraController controller = CameraController(
  cameras[0], // 选择前/后摄
  ResolutionPreset.high,
);
await controller.initialize();

await controller.takePicture('path/to/image.jpg');

预览通过 CameraPreview 控件绑定:

CameraPreview(controller);

3.3 图像帧获取与处理机制

插件中提供了 startImageStream() 方法,可实现帧数据获取:

controller.startImageStream((CameraImage image) {
  // image.planes[0].bytes 是原始图像数据(YUV 格式)
});

注意事项:

  • 帧数据格式通常为 YUV420,在 Android/iOS 上格式结构不同;
  • 若需转为 RGB 图像或 Bitmap 供 AI 模型使用,需结合 dart:ffi 接口调用 native 图像转换库(如 libyuv);
  • startImageStream() 与拍照/录像互斥,建议在业务中做状态切换管理。

3.4 高级参数扩展

官方 camera 插件对对焦、曝光、白平衡控制支持有限。若项目需实现如下功能:

  • 自定义对焦区域;
  • 曝光/ISO 手动设置;
  • 图像帧精确时间戳控制;
  • 相机开关控制与低层事件监听;

通常需要二次开发插件,扩展平台原生部分。扩展方法如下:

  1. 在 Android 端扩展 Java 代码,使用 Camera2 控制新参数;
  2. 在 iOS 端扩展 Swift/Obj-C,设置 AVCaptureDevice 控制属性;
  3. 在 Dart 中通过 MethodChannel.invokeMethod(...) 与原生通信;
  4. 注册自定义回调事件,通过 EventChannel 推送至 Dart 层。

通过该模式,可以在不破坏原有 Flutter 架构的前提下,实现高级相机能力接入。

3.5 项目实践总结

  • 在 AI 图像处理类项目中,建议将原始图像数据转发至 native 层进行处理,Dart 层只负责控制与回显;
  • 若有拍照延迟问题,检查预览帧率是否和拍照分辨率一致,避免切换过程触发 Session 重启;
  • 多机型测试时需关注 Android HAL 实现差异,部分低端设备对 Camera2 支持不完整;
  • 权限管理建议使用 permission_handler 插件统一适配 Android/iOS 权限弹窗和状态查询;
  • 若相机被其他应用占用(如扫码 SDK),需加入 Camera open/close 自动重试与状态监听。

Flutter camera 插件适合中轻量级影像需求场景,如人脸拍照、证件识别、视频采集等。更高阶的功能需通过原生桥接扩展实现。

4. React Native 中使用 CameraX 与 AVFoundation:原生桥接策略详解

React Native 本身不提供 Camera 能力,而是依赖第三方模块进行封装。当前社区较为活跃的库包括:

以下以 react-native-vision-camera 为主,介绍 CameraX/AVFoundation 的桥接实现。

4.1 Vision Camera 插件架构简介

该插件基于 Jetpack CameraX(Android)与 AVFoundation(iOS)实现,支持如下能力:

  • 相机预览与拍照;
  • 实时帧数据回调(支持帧间隔控制);
  • 自动曝光、对焦控制;
  • 与原生模块交互友好,可集成图像处理、CV 模型;
  • 桥接层使用 TurboModule + JSI 提升性能。

4.2 Android 平台接入流程(CameraX)

Android 使用 CameraX 提供高层封装,以下是原生部分的核心结构:

  • CameraProvider: 管理相机生命周期;
  • Preview, ImageCapture, ImageAnalysis: 不同用途的数据流绑定器;
  • 使用 Executor 实现线程分发,避免主线程阻塞;
  • 数据通过 ImageProxy 回调传入分析模块。

拍照示例:

val imageCapture = ImageCapture.Builder()
    .setTargetResolution(Size(1280, 720))
    .build()

val photoFile = File(outputDir, "photo.jpg")
val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
imageCapture.takePicture(outputOptions, executor, object : ImageCapture.OnImageSavedCallback {
    override fun onImageSaved(output: OutputFileResults) { ... }
    override fun onError(exc: ImageCaptureException) { ... }
})

4.3 iOS 平台接入流程(AVFoundation)

  • 创建 AVCaptureSession,添加输入(AVCaptureDeviceInput)与输出(AVCapturePhotoOutput);
  • 启动 Session,配置帧率与分辨率;
  • 拍照使用 capturePhotoWithSettings,帧数据处理需监听 AVCaptureVideoDataOutputSampleBufferDelegate
  • 使用 VisionCamera 插件时,已封装为 JSI 回调形式,可直接在 JS 层访问帧数据。

4.4 与 JS 层交互机制

Vision Camera 采用基于 JSI 的同步机制,图像帧通过 C++ 层写入共享缓存,由 JS 使用 worklet 调用:

camera.startRecording({
  onFrame: (frame) => {
    // 获取 frame.bytes 并送入 WebAssembly 模型处理
  }
});

优点在于无需多次 JS ↔ Native 往返,显著提升性能,适合高频帧图像处理(如姿态识别、人脸关键点检测)。

4.5 实战建议

  • Android 端需适配不同机型分辨率与 Sensor 输出差异,CameraX 在部分定制系统中表现不稳定;
  • iOS 中需注意权限弹窗顺序,未授权状态下调用相机会 silent fail;
  • 推荐使用 Jetpack Compose + CameraX 独立封装业务模块,提升逻辑隔离度;
  • 若业务对性能要求极高,考虑使用 C++ 进行帧分析后,将结果回传至 JS 层。

React Native 在摄像头调用上通过 Vision Camera 实现了较强的原生接近性与扩展性,适合中到高复杂度场景,包括 AI 实时视觉分析、直播推流等业务形态。

5. 图像数据流获取与处理:格式转换、YUV 转 RGB、图像保存

无论使用哪种跨平台框架,相机图像的最终用途往往包括图像分析、模型推理、图像增强或存储。为了达到这些目标,从 Camera 设备获取数据后必须经历若干关键步骤:数据结构转换、颜色格式转换(YUV→RGB)、内存对齐处理、图像压缩与编码。

5.1 常见图像帧格式分析

当前主流平台输出图像帧格式:

平台输出格式通常用途
AndroidYUV_420_888, NV21预览/分析
iOSkCVPixelFormatType_420YpCbCr8BiPlanarFullRange (NV12)预览/分析
QtQVideoFrame(平台相关)预览/处理
FlutterCameraImage(YUV分三平面)图像分析

不同平台使用不同格式封装图像帧,直接送入算法模型或保存为图片前,必须进行标准化转换,通常目标为 RGB 格式或 JPEG 编码格式。

5.2 YUV 转 RGB 常用方案

在性能敏感或需要交叉平台部署场景中,推荐使用 libyuvOpenCV 进行高效格式转换。

方案一:libyuv(C++)

  • 轻量级;
  • 支持 YUV420P、NV21、NV12 到 RGB24/BGR24/RGBA;
  • 可嵌入 Android NDK/iOS 项目,也可结合 Flutter 插件封装。
libyuv::NV21ToRGB24(
    yuvData, strideY,
    uvData, strideUV,
    rgbBuffer, rgbStride,
    width, height);

方案二:OpenCV

适用于需要进一步图像处理操作的场景。

cv::Mat yuv(height + height / 2, width, CV_8UC1, yuvData);
cv::Mat rgb;
cv::cvtColor(yuv, rgb, cv::COLOR_YUV2RGB_NV21);

注意事项:

  • 转换过程中需考虑内存对齐,部分平台图像行宽可能超过逻辑像素宽;
  • 图像翻转、方向矫正应基于拍照时获得的 Exif 信息或传感器朝向信息进行处理;
  • 转换过程建议在异步线程中执行,避免主线程阻塞。

5.3 图像存储与编码

图像保存通常采用 JPEG 编码,具体方式如下:

  • Android:使用 YuvImage 类结合 compressToJpeg
  • iOS:使用 CIImage + UIImageJPEGRepresentation
  • QtQImage::save()
  • Flutter/React Native:通过平台通道,将 Uint8List 类型数据交由 native 层编码并保存。

保存路径建议统一使用 getApplicationDocumentsDirectory() 或 Android/iOS 提供的缓存路径,避免跨平台路径问题。

为保证图像完整性与后续处理准确性,建议在保存图像时附带如下元数据:

  • 拍照时间戳;
  • 拍照方向;
  • 相机 ID(前摄/后摄);
  • 实际参数(如曝光时间、ISO、焦距);
  • 分辨率与压缩质量参数。

通过标准化图像处理链,可以实现跨平台系统中的图像统一存储与分析,为 AI 模型或图像后处理打下基础。

6. 相机权限适配与运行时校验:多平台统一管理策略

Camera 权限问题是跨平台相机调用最常见的稳定性障碍,尤其在 Android/iOS 近年来对运行时权限和隐私机制管控加强后,未正确处理权限的应用极易出现崩溃、预览黑屏或 silent fail 等异常行为。

6.1 Android 权限申请机制(API ≥ 23)

Android 使用运行时动态权限模型,涉及的核心权限包括:

  • android.permission.CAMERA:访问摄像头;
  • android.permission.RECORD_AUDIO:开启视频时需录音;
  • android.permission.WRITE_EXTERNAL_STORAGE:保存图像/视频到文件系统。

常用处理流程:

if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA)
    != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.CAMERA), REQUEST_CODE);
}

SDK 层需封装统一权限检查与回调机制,推荐使用开源权限库如 EasyPermissions 或 Kotlin 协程封装。

6.2 iOS 权限申请机制(iOS ≥ 10)

iOS 使用 Info.plist + 用户授权机制:

  • NSCameraUsageDescription:摄像头使用说明;
  • NSMicrophoneUsageDescription:麦克风说明(用于录像);

权限状态检查:

switch AVCaptureDevice.authorizationStatus(for: .video) {
case .authorized:
    // OK
case .notDetermined:
    AVCaptureDevice.requestAccess(for: .video) { granted in ... }
default:
    // 被拒绝或受限
}

一旦用户拒绝授权,系统将不会弹出授权框,需引导用户前往系统设置开启。

6.3 跨平台权限封装实践

各框架推荐权限处理方法如下:

框架推荐方案
Qt使用 QAndroidJniObject + Objective-C 调用系统接口
Flutter使用 permission_handler 插件
React Native使用 react-native-permissions 或原生模块包装

建议在 SDK 初始化阶段添加权限检查逻辑:

if (!await Permission.camera.request().isGranted) {
  // 弹窗提示并引导至设置页
}

6.4 异常处理策略与提示机制

跨平台项目中由于权限缺失而导致相机初始化失败,需实现如下保护策略:

  • 拦截所有 openCamera() 或 startPreview() 异常并提供 UI 级提示;
  • 权限被拒绝时提供设置引导,例如通过 openAppSettings() 函数跳转;
  • 统一权限状态上报机制,便于后续诊断与日志分析;
  • 在调试版本中增加日志输出权限检查流程细节(授权状态、是否跳过、用户行为等);
  • 防止重复弹窗与权限死循环,避免用户体验下降。

通过以上封装与处理机制,Camera 权限在各主流平台上的适配与错误处理能力将得到有效提升,最大程度保障影像模块在多端系统中的稳定运行。

7. 异步事件回调机制与 UI 控制状态同步处理

在跨平台 Camera 系统中,异步事件控制是一个普遍而关键的问题。相机启动、预览开启、对焦完成、图像捕获、错误通知等均是异步行为,如何与界面层交互、保证用户操作响应及时性、避免状态错乱,是系统设计的核心之一。跨平台框架在异步机制支持上差异明显,需要按平台特性进行优化。

7.1 常见异步事件类型

Camera 生命周期中主要异步事件如下:

异步事件类型说明
open 状态相机初始化完成、硬件连接状态
preview started预览成功开启
autofocus done自动对焦完成
capture done拍照完成、图像已存储/返回
recording started视频录制开始
error相机硬件故障、初始化失败、权限错误等

以上事件可能源自不同线程、不同模块,需进行统一派发和 UI 状态同步。

7.2 Flutter 中异步事件处理策略

Flutter 推荐使用 StreamEventChannel 与原生层保持通信。

_eventChannel.receiveBroadcastStream().listen((dynamic event) {
  switch (event['type']) {
    case 'cameraOpened':
      setState(() => isCameraReady = true);
      break;
    case 'error':
      showDialog(...);
      break;
  }
});

  • 建议对状态使用 ValueNotifierProvider 等状态管理框架;
  • 预览开始、拍照完成等关键事件需明确回调路径,否则 UI 按钮状态无法及时同步;
  • 异步图像流建议另起 isolate 管理,防止主 UI 卡顿。

7.3 React Native 中异步事件管理

推荐使用 NativeEventEmitter 监听原生模块回调:

const eventEmitter = new NativeEventEmitter(NativeModules.CameraManager);
eventEmitter.addListener('onCameraReady', () => setReady(true));

Vision Camera 插件则使用 JSI 直连 JS,引入 useFrameProcessor()

useFrameProcessor((frame) => {
  'worklet'
  // 帧级事件处理,JSI 直达,性能高
}, [dependency]);

  • 建议使用 React.useState() 维护 UI 状态,结合 useEffect 监听状态变化;
  • 若使用传统桥接,注意防止桥间数据积压,导致事件延迟或丢失;
  • 所有异步事件处理应封装为受控流程,避免多次调用导致状态错乱。

7.4 Qt 异步信号槽机制(Signal/Slot)

Qt 原生支持信号槽机制,事件模型相对简单可靠。例如:

connect(camera, &QCamera::stateChanged, this, &MyClass::onCameraStateChanged);
connect(imageCapture, &QCameraImageCapture::imageSaved, this, &MyClass::onImageSaved);

  • 所有状态变更建议使用枚举定义状态机管理;
  • UI 侧通过 QML Connections 绑定信号;
  • 多线程场景下,推荐使用 Qt::QueuedConnection 保证线程安全。

7.5 实战建议与常见错误规避

  • 所有异步事件必须由底层统一管理状态流,不能仅靠 UI 判断;
  • 建议封装 Camera 状态机:初始化 → 准备 → 预览 → 捕获 → 完成 → 释放;
  • 拍照和录像动作需禁用 UI 交互按钮,待回调后重启;
  • 异步事件回调失败需主动回传错误码,提示用户重试或退出;
  • 系统级资源抢占(如扫码、通话)后 Camera 被关闭,需重建 Session。

通过严格的异步回调管理与 UI 状态机设计,可显著提升用户操作连贯性、系统稳定性,避免异常状态造成的体验崩溃。

8. 项目实战总结:多框架下的 Camera 开发经验对比与最佳实践

经过对 Qt、Flutter、React Native 三大跨平台框架的 Camera 实战探索,可以总结出以下关键经验与选型建议:

8.1 开发效率与封装能力对比

框架原生能力封装插件质量高阶控制能力UI 集成复杂度
Qt中等自带完整类库较强(依赖平台实现)较低(QML 强绑定)
Flutter中等官方 camera 插件+部分社区扩展中等(可扩展)中等(需状态管理)
React Native较强(需原生桥)Vision Camera 最佳强(JSI 直连原生)高(需精细状态同步)
  • 对于偏向工业控制或嵌入式应用,推荐使用 Qt;
  • 移动端对实时处理有要求(如人脸检测、AR 拍照),推荐 React Native + Vision Camera;
  • 希望快速交付、跨平台一致性好,Flutter 是较佳折中方案。

8.2 跨平台 Camera 封装的关键策略

  1. 接口抽象统一:封装平台无关的 API 层,避免 UI 层耦合平台差异;
  2. 状态机管理:Camera 所有操作必须受控于统一状态流转机制;
  3. 插件能力隔离:复杂图像流、AI 模型处理推荐在 native 模块中完成;
  4. 异常恢复机制:Camera 被打断或失败时,提供自动恢复与兜底策略;
  5. 权限系统统一:抽象出平台权限请求与校验模块,避免重复实现。

8.3 性能与兼容性优化建议

  • 图像流处理推荐使用原生层共享内存或 native callback,避免 JS/Dart 层频繁拷贝;
  • 图像格式建议统一为 RGB 或 JPEG,便于存储与上传;
  • 拍照分辨率建议根据设备支持能力动态选择,避免超出范围;
  • Camera 开关与释放操作应严格限制,避免资源反复申请释放造成内存问题;
  • 多机型测试必须覆盖 Android 低端机与 iOS 旧版本,规避 HAL 实现差异问题。

通过上述封装设计与实战策略,跨平台 Camera 系统可在多端架构下实现稳定、可控、高性能的拍照与图像处理能力,满足主流图像采集类业务在工业级或消费级产品中的部署需求。

本文转自 https://zhxin.blog.csdn.net/article/details/148676008,如有侵权,请联系删除。