#星光不负 码向未来# 从零到一教你在鸿蒙中实现微信分享–全流程 原创

鸿蒙之虎君莫笑
发布于 2025-10-29 10:06
浏览
0收藏

#星光不负 码向未来# 从零到一教你在鸿蒙中实现微信分享–全流程

前言

大家好,我是青蓝逐码组织的君莫笑。

作为一名移动开发者,我见证了鸿蒙生态从初生到茁壮成长的全过程。还记得刚接触HarmonyOS时,很多第三方SDK的适配都不完善,开发过程中总会遇到各种"无路可走"的窘境。但就在这短短时间内,鸿蒙生态的发展速度让人惊叹——微信SDK的全面接入就是最好的证明。

当我第一次成功在鸿蒙应用中实现微信分享功能时,那种成就感难以言表。这不仅代表着一个功能的实现,更象征着鸿蒙生态的日益完善。今天,我想把这个从零到一的完整实践经验分享给大家,帮助更多开发者少走弯路。

本文将详细讲解如何在HarmonyOS中接入微信分享功能,主要包括微信分享文本、分享H5网页、分享小程序三大核心场景。整个过程我会提供完整的代码示例和踩坑经验,最后你可以将这些代码封装成一个通用的工具类,在项目中直接复用。

一、接入微信SDK

1.1 安装SDK

参考微信官方文档

在终端输入以下命令即可依赖微信的SDK:

ohpm i @tencent/wechat_open_sdk

开发心得:当我看到腾讯官方发布了鸿蒙版微信SDK时,真的非常激动。这意味着鸿蒙应用可以和Android、iOS应用一样,轻松实现社交分享功能了。OHPM包管理器的使用体验也很流畅,和NPM一样简单。

1.2 微信开放平台配置

使用微信SDK前,需要先在微信开放平台创建鸿蒙应用。如果你已经有Android应用,好消息是可以直接复用AppID,只需要添加鸿蒙平台即可。

注意事项

  • 应用签名必须与实际运行的应用签名一致
  • 包名需要和module.json5中的bundleName保持一致
  • 建议开发和发布使用不同的签名配置

二、配置项目文件

2.1 修改module.json5

在module.json5的abilities配置中加入以下字段:

#星光不负 码向未来# 从零到一教你在鸿蒙中实现微信分享–全流程-鸿蒙开发者社区

#星光不负 码向未来# 从零到一教你在鸿蒙中实现微信分享–全流程-鸿蒙开发者社区

2.2 创建WXApi实例

在项目入口文件中尽早创建WXApi实例:

import wxOpenSdk from '@tencent/wechat_open_sdk';

// WXApi 是第三方app和微信通信的openApi接口
// 实例通过WXAPIFactory获取,需要传入应用申请到的AppID
export const WXApi = wxOpenSdk.WXAPIFactory.createWXAPI('你的APP_ID');

说明

  • AppID和微信平台Android应用的AppID相同,创建鸿蒙应用后直接复用即可
  • 建议在应用启动时就创建实例,确保后续调用时SDK已初始化完成

三、封装微信分享工具类

接下来是核心部分——封装一个通用的微信分享工具类。我采用单例模式设计,确保全局只有一个实例,避免重复初始化。

3.1 工具类基础结构

/**
 * 微信分享
 */
export class WxShareViewModel {
  // 此处换成真实appid
  WXApi = wxOpenSdk.WXAPIFactory.createWXAPI('')
  static instance: WxShareViewModel | null = null
  isWXApp: boolean = this.WXApi.isWXAppInstalled()

  private constructor() {

  }

  static getInstance() {
    if (!WxShareViewModel.instance) {
      WxShareViewModel.instance = new WxShareViewModel()
    }
    return WxShareViewModel.instance
  }
}

设计说明

  • 使用单例模式确保全局唯一实例
  • isWXApp属性检测用户是否安装了微信
  • 私有构造函数防止外部直接实例化

3.2 实现文本分享功能

/**
 * 分享文本
 * @param text 文本内容
 */
textShare(text: string) {
  this.isWXAPPCallback(() => {
    let textObject = new wxOpenSdk.WXTextObject
    textObject.text = text
    let mediaMessage = new wxOpenSdk.WXMediaMessage()
    mediaMessage.mediaObject = textObject
    let req = new wxOpenSdk.SendMessageToWXReq()
    req.scene = wxOpenSdk.SendMessageToWXReq.WXSceneSession
    req.message = mediaMessage
    this.WXApi.sendReq(getContext(this) as common.UIAbilityContext, req)
  })
}

3.3 实现H5网页分享(重点难点)

H5分享是最复杂的功能,因为涉及到图片处理。这里有个重要限制:微信SDK要求分享图片必须小于64KB,否则会静默失败。

3.3.1 核心难点:图片压缩

踩坑经历:我最初直接将网络图片传给SDK,结果分享功能毫无反应,也没有任何错误提示。经过反复调试和查阅文档,才发现是图片大小超限导致的。

解决方案:采用华为官方推荐的智能压缩算法,结合二分法和缩放技术,确保图片压缩到64KB以下。

下面是我改造后的压缩工具函数:

import { image } from '@kit.ImageKit';

/**
 * 图片压缩,保存
 * @param sourcePixelMap:原始待压缩图片的PixelMap对象
 * @param maxCompressedImageSize:指定图片的压缩目标大小,单位kb
 * @returns compressedImageInfo:返回最终压缩后的图片信息
 */
export async function compressedImage(sourcePixelMap: image.PixelMap,
  maxCompressedImageSize: number): Promise<ArrayBuffer> {
  // 创建图像编码ImagePacker对象
  const imagePackerApi = image.createImagePacker();
  const IMAGE_QUALITY = 0;
  const packOpts: image.PackingOption = { format: "image/jpeg", quality: IMAGE_QUALITY };
  // 通过PixelMap进行编码。compressedImageData为打包获取到的图片文件流。
  let compressedImageData: ArrayBuffer = await imagePackerApi.packing(sourcePixelMap, packOpts);
  // 压缩目标图像字节长度
  const maxCompressedImageByte = maxCompressedImageSize * 1024;
  // 图片压缩。先判断设置图片质量参数quality为0时,packing能压缩到的图片最小字节大小是否满足指定的图片压缩大小。如果满足,则使用packing方式二分查找最接近指定图片压缩目标大小的quality来压缩图片。如果不满足,则使用scale对图片先进行缩放,采用while循环每次递减0.4倍缩放图片,再用packing(图片质量参数quality设置0)获取压缩图片大小,最终查找到最接近指定图片压缩目标大小的缩放倍数的图片压缩数据。
  if (maxCompressedImageByte > compressedImageData.byteLength) {
    // 使用packing二分压缩获取图片文件流
    compressedImageData =
      await packingImage(compressedImageData, sourcePixelMap, IMAGE_QUALITY, maxCompressedImageByte);
  } else {
    // 使用scale对图片先进行缩放,采用while循环每次递减0.4倍缩放图片,再用packing(图片质量参数quality设置0)获取压缩图片大小,最终查找到最接近指定图片压缩目标大小的缩放倍数的图片压缩数据
    let imageScale = 1;
    const REDUCE_SCALE = 0.4;
    // 判断压缩后的图片大小是否大于指定图片的压缩目标大小,如果大于,继续降低缩放倍数压缩。
    while (compressedImageData.byteLength > maxCompressedImageByte) {
      if (imageScale > 0) {
        // 性能知识点: 由于scale会直接修改图片PixelMap数据,所以不适用二分查找scale缩放倍数。这里采用循环递减0.4倍缩放图片,来查找确定最适合的缩放倍数。如果对图片压缩质量要求不高,建议调高每次递减的缩放倍数reduceScale,减少循环,提升scale压缩性能。
        imageScale = imageScale - REDUCE_SCALE;
        await sourcePixelMap.scale(imageScale, imageScale);
        compressedImageData = await packing(sourcePixelMap, IMAGE_QUALITY);
      } else {
        // imageScale缩放小于等于0时,没有意义,结束压缩。这里不考虑图片缩放倍数小于reduceScale的情况。
        break;
      }
    }
  }
  return compressedImageData;
}


/**
 * packing压缩
 * @param sourcePixelMap:原始待压缩图片的PixelMap
 * @param imageQuality:图片质量参数
 * @returns data:返回压缩后的图片数据
 */
async function packing(sourcePixelMap: image.PixelMap, imageQuality: number): Promise<ArrayBuffer> {
  const imagePackerApi = image.createImagePacker();
  const packOpts: image.PackingOption = { format: "image/jpeg", quality: imageQuality };
  const data: ArrayBuffer = await imagePackerApi.packing(sourcePixelMap, packOpts);
  return data;
}

/**
 * packing二分方式循环压缩
 * @param compressedImageData:图片压缩的ArrayBuffer
 * @param sourcePixelMap:原始待压缩图片的PixelMap
 * @param imageQuality:图片质量参数
 * @param maxCompressedImageByte:压缩目标图像字节长度
 * @returns compressedImageData:返回二分packing压缩后的图片数据
 */
async function packingImage(compressedImageData: ArrayBuffer, sourcePixelMap: image.PixelMap, imageQuality: number,
  maxCompressedImageByte: number): Promise<ArrayBuffer> {
  // 图片质量参数范围为0-100,这里以10为最小二分单位创建用于packing二分图片质量参数的数组。
  const packingArray: number[] = [];
  const DICHOTOMY_ACCURACY = 10;
  // 性能知识点: 如果对图片压缩质量要求不高,建议调高最小二分单位dichotomyAccuracy,减少循环,提升packing压缩性能。
  for (let i = 0; i <= 100; i += DICHOTOMY_ACCURACY) {
    packingArray.push(i);
  }
  let left = 0;
  let right = packingArray.length - 1;
  // 二分压缩图片
  while (left <= right) {
    const mid = Math.floor((left + right) / 2);
    imageQuality = packingArray[mid];
    // 根据传入的图片质量参数进行packing压缩,返回压缩后的图片文件流数据。
    compressedImageData = await packing(sourcePixelMap, imageQuality);
    // 判断查找一个尽可能接近但不超过压缩目标的压缩大小
    if (compressedImageData.byteLength <= maxCompressedImageByte) {
      left = mid + 1;
      if (mid === packingArray.length - 1) {
        break;
      }
      // 获取下一次二分的图片质量参数(mid+1)压缩的图片文件流数据
      compressedImageData = await packing(sourcePixelMap, packingArray[mid + 1]);
      // 判断用下一次图片质量参数(mid+1)压缩的图片大小是否大于指定图片的压缩目标大小。如果大于,说明当前图片质量参数(mid)压缩出来的图片大小最接近指定图片的压缩目标大小。传入当前图片质量参数mid,得到最终目标图片压缩数据。
      if (compressedImageData.byteLength > maxCompressedImageByte) {
        compressedImageData = await packing(sourcePixelMap, packingArray[mid]);
        break;
      }
    } else {
      // 目标值不在当前范围的右半部分,将搜索范围的右边界向左移动,以缩小搜索范围并继续在下一次迭代中查找左半部分。
      right = mid - 1;
    }
  }
  return compressedImageData;
}

3.3.2 实现网络图片下载

接下来实现网络图片下载功能,下载完成后自动压缩:

import http from '@ohos.net.http';
import { image } from '@kit.ImageKit';
import { BusinessError } from '@ohos.base';

export function loadImageUrl(url: string, successCallBack: (ImgArrayBuffer: ArrayBuffer) => void) {
      http.createHttp()
        .request(url,
          {
            method: http.RequestMethod.GET,
          },
          async (error: BusinessError, data: http.HttpResponse) => {
            if (http.ResponseCode.OK === data.responseCode) {
              let imageBuffer: ArrayBuffer = data.result as ArrayBuffer;
              const imageSource = image.createImageSource(imageBuffer)
              imageSource.createPixelMap()
                .then((pixelMap: image.PixelMap) => {
                  compressedImage(pixelMap, 64)
                    .then((arrayBuffer) => {
                      successCallBack(arrayBuffer)
                    })
                })
            }
          })
    }

实现要点

  1. 使用http.createHttp()下载网络图片
  2. 将下载的ArrayBuffer转换为ImageSource
  3. 再转换为PixelMap格式(压缩函数的输入格式)
  4. 调用压缩函数将图片压缩到64KB以下
  5. 通过回调返回压缩后的ArrayBuffer

3.3.3 实现微信安装检测

在每次分享前,需要检测用户是否安装了微信:


    isWXAPPCallback(callback: Function) {
      if (!this.isWXApp) {
        AlertDialog.show({ message: JSON.stringify("请先安装微信") })
      } else {
        callback()
      }
    }

这是一个简单但必要的用户体验优化,避免未安装微信的用户点击分享后毫无反应。

3.3.4 完整的H5分享实现

/**
 * 分享h5
 * @param Url 网页地址
 * @param title 标题
 * @param description 描述
 * @param imgUrl 图片网络地址
 */
h5Share(Url: string, title: string, description: string, imgUrl: string) {
  this.isWXAPPCallback(async () => {
    const webpageObject = new wxOpenSdk.WXWebpageObject()
    webpageObject.webpageUrl = Url
    const mediaMessage = new wxOpenSdk.WXMediaMessage()
    mediaMessage.mediaObject = webpageObject
    mediaMessage.title = title
    mediaMessage.description = description
    loadImageUrl(imgUrl, async (buffer) => {
      mediaMessage.thumbData = new Uint8Array(buffer)
      const req = new wxOpenSdk.SendMessageToWXReq()
      req.scene = wxOpenSdk.SendMessageToWXReq.WXSceneSession
      req.message = mediaMessage
      this.WXApi.sendReq(getContext(this) as common.UIAbilityContext, req)
    })
  })
}

代码解析

  1. 创建WXWebpageObject对象,设置网页URL
  2. 创建WXMediaMessage消息对象,设置标题、描述
  3. 下载并压缩图片,设置为缩略图
  4. 创建SendMessageToWXReq请求,发送给微信
  5. WXSceneSession表示分享到微信好友(还可以改为WXSceneTimeline分享到朋友圈)

3.4 实现小程序分享

小程序分享和H5分享类似,只是传递的参数不同:


    /**
     * 分享小程序
     * @param userName 小程序的原始 id(gh_xxxx形式的id)
     * @param path 小程序的 path
     * @param miniprogramType 小程序的类型, 默认正式版
     * @param title 标题
     * @param description 描述
     * @param imgUrl 网络图片地址
     */
    miniProgramShare(userName: string, path: string, miniprogramType: number, title: string, description: string,
      imgUrl: string) {
      this.isWXAPPCallback(async () => {
        const miniProgramObject = new wxOpenSdk.WXMiniProgramObject()
        miniProgramObject.userName = userName
        miniProgramObject.path = path
        miniProgramObject.miniprogramType = miniprogramType
        const mediaMessage = new wxOpenSdk.WXMediaMessage()
        mediaMessage.mediaObject = miniProgramObject
        mediaMessage.title = title
        mediaMessage.description = description
        loadImageUrl(imgUrl, async (buffer) => {
          mediaMessage.thumbData = new Uint8Array(buffer)
          const req = new wxOpenSdk.SendMessageToWXReq()
          req.scene = wxOpenSdk.SendMessageToWXReq.WXSceneSession
          req.message = mediaMessage
          this.WXApi.sendReq(getContext(this) as common.UIAbilityContext, req)
        })
      })
    }

参数说明

  • userName: 小程序的原始ID(gh_开头的ID,在小程序后台可以找到)
  • path: 小程序的页面路径(如:pages/index/index
  • miniprogramType: 小程序类型
    • 0: 正式版
    • 1: 开发版
    • 2: 体验版
  • 图片压缩逻辑与H5分享完全相同

使用场景:这个功能在电商、内容平台等场景非常实用,可以实现从鸿蒙App直接分享商品、文章到微信小程序。

四、完整工具类示例

将以上所有代码整合后,你就得到了一个功能完善的微信分享工具类。使用示例:

// 分享文本
WxShareViewModel.getInstance().textShare("这是一段分享的文本");

// 分享H5
WxShareViewModel.getInstance().h5Share(
  "https://example.com",
  "分享标题",
  "分享描述",
  "https://example.com/image.jpg"
);

// 分享小程序
WxShareViewModel.getInstance().miniProgramShare(
  "gh_xxxxxx",
  "pages/index/index",
  0,
  "小程序标题",
  "小程序描述",
  "https://example.com/image.jpg"
);

总结与思考

技术要点回顾

通过本文的实践,我们完整实现了鸿蒙应用中的微信分享功能,主要涉及以下核心技术点:

  1. 微信SDK集成:通过ohpm快速接入腾讯官方SDK
  2. 配置管理:在module.json5中配置必要的权限和跳转参数
  3. 图片处理:网络图片下载、格式转换、智能压缩(这是最容易踩坑的地方)
  4. 单例模式设计:封装可复用的工具类,提升代码质量
  5. 三种分享场景:文本、H5网页、小程序的完整实现

踩坑经验分享

在开发过程中,最大的难点是图片压缩问题。微信SDK要求缩略图必须小于64KB,否则会静默失败(没有任何错误提示)。我最初尝试了多种压缩方案,最后采用华为官方推荐的二分法+缩放相结合的算法,完美解决了这个问题。这个经历让我深刻体会到:阅读官方文档的重要性

另外,在调试过程中发现,必须在微信开放平台正确配置应用签名和包名,否则会出现"应用签名不一致"的错误。建议大家在开发阶段就做好这些配置工作。

我与HarmonyOS的故事

从Android开发转向鸿蒙开发,最初我是抱着"试试看"的心态。但随着深入学习,我发现HarmonyOS不仅仅是一个操作系统,更是一个面向未来的全场景生态。ArkTS的声明式UI、分布式能力、元服务等特性,让我看到了移动开发的新可能。

这次微信分享功能的实现,只是鸿蒙生态完善的一个缩影。从最初的SDK匮乏,到现在各大主流平台纷纷提供鸿蒙版SDK,这个速度令人惊叹。作为开发者,我们既是鸿蒙生态的建设者,也是受益者。

对鸿蒙生态的展望

随着HarmonyOS生态的不断壮大,我相信会有越来越多的第三方服务商加入进来。未来,我们可能会看到:

  • 更完善的开发工具链
  • 更丰富的第三方SDK
  • 更强大的跨设备协同能力
  • 更多的商业化机会

星光不负,码向未来。每一行代码,都是为鸿蒙生态添砖加瓦;每一次分享,都是为开发者社区贡献力量。希望这篇文章能帮助到正在探索鸿蒙开发的你,也期待与更多开发者一起,见证鸿蒙生态的繁荣。

关于我们

青蓝逐码组织致力于分享实战开发经验,帮助开发者解决实际项目中的技术难题。我们的官网正在筹备中,后续会持续输出更多高质量的技术文章和开源项目。

如果这篇文章对你有帮助,欢迎点赞、收藏、分享!你们的支持是我持续创作的动力。


作者:君莫笑
组织:青蓝逐码
标签:#HarmonyOS #鸿蒙开发 #微信分享 #实战教程 #星光不负码向未来

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
收藏
回复
举报
回复
    相关推荐