# uni-push

uni-push是DCloud与合作伙伴个推共同推出的统一推送服务。用于从服务器端推送消息到客户端。

它包括在线推送、离线推送,聚合了Apple、华为、小米、OPPO、VIVO、魅族、荣耀(3.99+)、Google等多个手机厂商的推送通道。

若不使用服务器推送,仅想创建手机通知栏本地消息,也需要使用本模块的API。

它是一个云端一体的业务,涉及多份文档:

  1. 业务介绍:对于未使用过uni-push的新用户,本文必读:uni-push业务介绍
  2. 客户端API,即本文
  3. 服务器API,另见

# 注意事项

  • Android离线推送消息,需要开通厂商通道,在UniPush2.0中进行配置 文档
  • iOS平台配置证书时,请注意开通推送能力,否则云打包会报错,配置如下图:
  • iOS平台可以通过info.plist配置通知权限描述

# uni.getPushClientId(options)

获取客户端唯一的推送标识

# getPushClientId 兼容性

Web 微信小程序 Android iOS HarmonyOS
4.27 4.41 3.98 4.18 4.61

# 参数

名称 类型 必填 默认值 兼容性 描述
options GetPushClientIdOptions -
-
名称 类型 必备 默认值 兼容性 描述
success (result: GetPushClientIdSuccess) => void null
-
接口调用成功的回调函数
fail (result: UniError) => void null
-
接口调用失败的回调函数
complete (result: any) => void null
-
接口调用结束的回调函数(调用成功、失败都会执行)

# GetPushClientIdSuccess 的属性值

名称 类型 必备 默认值 兼容性 描述
cid string -
个推客户端推送id,对应uni-id-device表的push_clientid
errMsg string -
错误描述

# 参见

# uni.onPushMessage(callback)

启动监听推送消息事件

# onPushMessage 兼容性

Web 微信小程序 Android iOS HarmonyOS
4.27 4.41 3.98 4.18 4.61

# 参数

名称 类型 必填 默认值 兼容性 描述
callback (result: OnPushMessageCallbackResult) => void -
-

# OnPushMessageCallbackResult 的属性值

名称 类型 必备 默认值 兼容性 描述
type string -
事件类型
- click 从系统推送服务点击消息启动应用事件
- receive 应用从推送服务器接收到推送消息事件
合法值 兼容性 描述
click
-
-
receive
-
-
data UTSJSONObject -
消息内容

# 参见

# 注意事项

  • 如果多次监听onPushMessage,那么事件也会多次触发,所以当不需要监听的时候需要offPushMessage

# uni.offPushMessage(callback)

关闭推送消息监听事件,iOS端调用会关闭所有监听。

# offPushMessage 兼容性

Web 微信小程序 Android iOS HarmonyOS
4.27 4.41 3.98 4.18 4.61

# 参数

名称 类型 必填 默认值 兼容性 描述
callback (result: OnPushMessageCallbackResult) => void -
-

# OnPushMessageCallbackResult 的属性值

名称 类型 必备 默认值 兼容性 描述
type string -
事件类型
- click 从系统推送服务点击消息启动应用事件
- receive 应用从推送服务器接收到推送消息事件
合法值 兼容性 描述
click
-
-
receive
-
-
data UTSJSONObject -
消息内容

# 参见

# 注意事项

  • 由于各大厂商限制推送频次,当使用厂商离线推送的时,需要在不同品牌手机后台开通自分类权益,限制数量说明

    开通自分类权益后,需要客户端创建channel,因此客户端提供了setPushChannel来进行channel的创建,通过此Api来创建渠道进行推送。 客户端创建渠道成功后,即可通过云函数进行推送,uni-push2服务端文档

  • 由于Android通知渠道的机制问题,一旦通知渠道建立,便不能修改此渠道的配置,即使删除渠道后再次创建同channelId名称的渠道,也不会改变原先渠道的配置(除非删除应用),最明显的现象就是铃声动态修改失败,比如调用setPushChannel时,第一次的设置参数是{"channelId":"test","soundName":"pushsound"} , 这时你想切换铃音,你的channelId就不能再叫test了,而应该为{"channelId":"test2","soundName":"ring"} ,此时会新建一个渠道。

# uni.createPushMessage(options)

创建本地通知栏消息

# createPushMessage 兼容性

Web 微信小程序 Android iOS HarmonyOS
x - 3.98 4.18 4.61

# 参数

名称 类型 必填 默认值 兼容性 描述
options CreatePushMessageOptions -
名称 类型 必备 默认值 兼容性 描述
cover boolean false
是否覆盖上一次提示的消息
delay number 0
提示消息延迟显示的时间,单位为s
icon string null
推送消息的图标
sound string "system"
推送消息的提示音
- system: 使用系统通知提示音(默认值)
- none: 不使用提示音
title string App的名称
推送消息的标题
content string -
消息显示的内容,在系统通知中心中显示的文本内容。鸿蒙系统中,此字段为必填字段。
payload any null
消息承载的数据,可根据业务逻辑自定义数据格式,在点击通知消息时onPushMessage回调中会返回此字段的数据。
when number 当前时间
消息上显示的提示时间,需要传入时间戳。
channelId string "DcloudChannelID"
渠道id,Android特有字段,通知渠道介绍
创建通知渠道请使用getPushChannelManager获取PushChannelManager对象,调用setPushChannel方法配置渠道。
category string null
通知类别,Android特有字段,通知渠道介绍
标识通知的类别,应用场景为对于离线推送厂商配置的支持,比如华为消息分类
success (result: CreatePushMessageSuccess) => void null
接口调用成功的回调函数
fail (result: UniError) => void null
接口调用失败的回调函数
complete (result: any) => void null
接口调用结束的回调函数(调用成功、失败都会执行)

# 参见

# 通用类型

# GeneralCallbackResult

名称 类型 必备 默认值 兼容性 描述
errMsg string -
错误信息

# uni.getPushChannelManager()

获取通知渠道管理器,Android 8系统以上才可以设置通知渠道,Android 8系统以下返回null,通知渠道介绍,iOS不支持。

# getPushChannelManager 兼容性

Web 微信小程序 Android iOS HarmonyOS
x - 4.25 x -

# 返回值

类型
ChannelManager

# ChannelManager 的方法

# setPushChannel(options: SetPushChannelOptions): void;

setPushChannel 设置推送渠道

# setPushChannel 兼容性
Web 微信小程序 Android iOS HarmonyOS
x - 3.98 x -
# 参数
名称 类型 必填 默认值 兼容性 描述
options SetPushChannelOptions -
名称 类型 必备 默认值 兼容性 描述
soundName string null
添加的声音文件,注意raw目录下必须要有 ,不传此字段将使用默认铃音。
channelId string -
通知渠道id
channelDesc string -
通知渠道描述
enableLights boolean false
呼吸灯闪烁
enableVibration boolean false
震动
importance number 3
通知的重要性级别,可选范围IMPORTANCE_LOW:2、IMPORTANCE_DEFAULT:3、IMPORTANCE_HIGH:4 。
lockscreenVisibility number -1000
锁屏可见性,可选范围VISIBILITY_PRIVATE:0、VISIBILITY_PUBLIC:1、VISIBILITY_SECRET:-1、VISIBILITY_NO_OVERRIDE:-1000。

# getAllChannels(): Array<string>;

getAllChannels 获取当前应用注册的所有的通知渠道。

# getAllChannels 兼容性
Web 微信小程序 Android iOS HarmonyOS
x - 3.98 x -
# 返回值
类型
Array<string>

# 参见

# uni.setAppBadgeNumber(num, options?)

设置应用图标上显示的角标数字,注意:不同手机厂商的角标显示规则不同,有部分设备的rom版本不支持显示角标,另有部分rom需要在应用的通知管理里开启桌面角标配置,才可以设置角标成功。

# setAppBadgeNumber 兼容性

Web 微信小程序 Android iOS HarmonyOS
x - 4.25 4.25 4.61

# 参数

名称 类型 必填 默认值 兼容性 描述
num number -
要显示的角标数字值,参数为0则表示清除角标数字。
options BadgeOptions -
小米手机显示角标需要在系统消息中心显示一条通知,此参数用于设置通知的标题(title)和内容(content)。
名称 类型 必备 默认值 兼容性 描述
title string 应用的名称
消息的标题
content string '您有x条未读消息',其中x为设置的角标数字值。
消息的内容

# 参见

# uni.getChannelManager()

获取通知渠道管理器,Android 8系统以上才可以设置通知渠道,Android 8系统以下返回null,通知渠道介绍 ,iOS不支持。 已废弃,仅为了向下兼容保留,建议使用getPushChannelManager

# getChannelManager 兼容性

Web 微信小程序 Android iOS HarmonyOS
x - 3.98 x -

# 返回值

类型
ChannelManager

# ChannelManager 的方法

# setPushChannel(options: SetPushChannelOptions): void;

setPushChannel 设置推送渠道

# setPushChannel 兼容性
Web 微信小程序 Android iOS HarmonyOS
x - 3.98 x -
# 参数
名称 类型 必填 默认值 兼容性 描述
options SetPushChannelOptions -
名称 类型 必备 默认值 兼容性 描述
soundName string null
添加的声音文件,注意raw目录下必须要有 ,不传此字段将使用默认铃音。
channelId string -
通知渠道id
channelDesc string -
通知渠道描述
enableLights boolean false
呼吸灯闪烁
enableVibration boolean false
震动
importance number 3
通知的重要性级别,可选范围IMPORTANCE_LOW:2、IMPORTANCE_DEFAULT:3、IMPORTANCE_HIGH:4 。
lockscreenVisibility number -1000
锁屏可见性,可选范围VISIBILITY_PRIVATE:0、VISIBILITY_PUBLIC:1、VISIBILITY_SECRET:-1、VISIBILITY_NO_OVERRIDE:-1000。

# getAllChannels(): Array<string>;

getAllChannels 获取当前应用注册的所有的通知渠道。

# getAllChannels 兼容性
Web 微信小程序 Android iOS HarmonyOS
x - 3.98 x -
# 返回值
类型
Array<string>

# 参见

# 注意事项

  • Android原生的系统其实是不支持设置角标的,在原生系统中应用有通知时,会在图标右上角出现圆点,所以原生系统并不适用 setAppBadgeNumber
  • 支持的手机品牌为:小米、华为、荣耀、OPPO、vivo、三星、索尼。
  • 在小米系统上设置角标有个默认的行为,即:app处于前台状态会清空角标数(可以理解为已读),所以小米平台建议在onHide中设置角标。

# 示例

示例为hello uni-app x alpha分支,与最新HBuilderX Alpha版同步。与最新正式版同步的master分支示例另见

扫码体验(手机浏览器跳转到App直达页)

示例

<template>
  <!-- #ifdef APP -->
  <scroll-view style="flex: 1">
  <!-- #endif -->
  <view>
    <!-- #ifdef APP-ANDROID -->
    <button class="normal-button" type="default" @click="handleCreateChannel(true)">
      创建通知渠道 | setPushChannel
    </button>
    <button class="normal-button" type="default" @click="handleGetAllChannels">
      获取所有通知渠道信息 | getAllChannels
    </button>
    <textarea style="width: 100%;" :disabled="true" :value="channelInfo"></textarea>
    <!-- #endif -->
    <!-- #ifdef APP -->
    <button class="normal-button" type="default" @click="handleCreateLocalNotification">
      创建本地通知消息 | createPushMessage
    </button>
    <text class="instructions">
      不同手机厂商的角标显示规则不同,有部分设备的rom版本不支持显示角标,另有部分rom需要在应用的通知管理里开启`桌面角标`配置,才可以设置角标成功。\n
      部分rom需要在设置中同时开启`通知开关`和`桌面角标`配置,才允许设置角标,例如鸿蒙4.2。 \n
      另外针对高版本小米设备,会借助创建通知栏消息来设置角标数,所以设置时需要注意是否有权限创建通知栏消息。
    </text>
    <button class="normal-button" type="default" @click="handleSetBadge">
      设置角标为5 | setAppBadgeNumber(5)
    </button>
    <button class="normal-button" type="default" @click="handleCleanBadge">
      清空角标 | setAppBadgeNumber(0)
    </button>
    <!-- #endif -->
    <button class="normal-button" type="default" @click="handleSendPushMessage">
      发送通知消息 | sendPushMessage
    </button>
    <button class="normal-button uni-common-mb" type="default" @click="handleGetClientId">
      获取cid | getPushClientId
    </button>
    <button class="normal-button" type="default" @click="handleOnPushMessage">
      注册回调 | onPushMessage
    </button>
    <button class="normal-button" type="default" @click="handleOffPushMessage">
      注销回调 | offPushMessage
    </button>
  </view>
  <!-- #ifdef APP -->
  </scroll-view>
  <!-- #endif -->
</template>

<script setup>
  // 自动化测试
  type TypeJestResult = {
    clientId : string,
    sendPushMessageRes : number,
    onPushMessageType:string,
    onPushMessageCallbackInfo:string
  }
  type TypeIsRegister = {
    state:boolean
  }
  const jestResult = reactive({
    clientId:"",
    sendPushMessageRes:-1,
    onPushMessageType:"",
    onPushMessageCallbackInfo:""
  } as TypeJestResult)
  // 自动化测试
  const autoTest = ref(false);
  const updateAutoTest = (value : boolean) => {
    autoTest.value = value
  }

  const channelInfo = ref("")
  const onPushMessageCallback = (res : OnPushMessageCallbackResult) => {
    // 自动化测试
    jestResult.onPushMessageType = res.type
    jestResult.onPushMessageCallbackInfo = JSON.stringify(res.data)
    if (!autoTest.value) {
      uni.showModal({
        title: "onPushMessage回调信息",
        content: `type:${res.type} \n data:${JSON.stringify(res.data)}`
      })
    }
  }

  // 为兼容Android测试例中能获取到,此处用reactive定义
  const isRegister = reactive({
    state:false
  } as TypeIsRegister);

  const handleOnPushMessage = () => {
    if (isRegister.state) {
      uni.showToast({
        icon: "error",
        title: "无需重复注册"
      })
      return
    }
    uni.onPushMessage(onPushMessageCallback)
    isRegister.state = true
    uni.showToast({
      title: "成功注册"
    })
  }

  const handleOffPushMessage = () => {
    if (!isRegister.state) {
      uni.showToast({
        icon: "error",
        title: "未注册, 无需注销"
      })
      return
    }
    uni.offPushMessage(onPushMessageCallback)
    isRegister.state = false
    uni.showToast({
      title: "成功注销"
    })
  }

  const handleCreateChannel = (showToast : boolean) => {
    // #ifdef APP-ANDROID
    const manager = uni.getPushChannelManager()
    manager.setPushChannel({
      channelId: "msg-pass",
      channelDesc: "留言审核通过",
      soundName: "#填写配置的声音文件名#",
      enableLights: true,
      enableVibration: true,
      importance: 4,
      lockscreenVisibility: 1
    } as SetPushChannelOptions)
    if (showToast) {
      uni.showToast({
        title: "设置渠道成功"
      })
    }
    // #endif
  }
  const handleGetAllChannels = () => {
    // #ifdef APP-ANDROID
    const manager = uni.getPushChannelManager()
    console.log("channels : " + manager.getAllChannels());
    channelInfo.value = `渠道信息为: \n ${manager.getAllChannels()}`
    // #endif
  }
  const handleCreateLocalNotification = () => {
    if (uni.getAppAuthorizeSetting().notificationAuthorized == "authorized") {
      handleCreateChannel(false)
      const date = new Date();
      const hour = date.getHours()
      const minute = date.getMinutes()
      const second = date.getSeconds()
      const formateTime = (target : number) : string => {
        return target < 10 ? `0${target}` : `${target}`
      }
      uni.createPushMessage({
        title: "主标题(title)",
        content: `内容(content),创建时间: ${formateTime(hour)}:${formateTime(minute)}:${formateTime(second)}`,
        cover: false,
        channelId: "msg-pass",
        when: Date.now() + 10000,
        icon: "/static/uni.png",
        sound: "system",
        delay: 1,
        payload: {
          pkey: "pvalue1"
        },
        // #ifdef APP-HARMONY
        category: "SOCIAL_COMMUNICATION",
        // #endif
        // #ifndef APP-HARMONY
        category: "IM",
        // #endif
        success(res) {
          console.log("res: " + res);
          uni.hideToast()
          uni.showToast({
            title: "创建本地通知消息成功"
          })
        },
        fail(e) {
          console.log("fail :" + e);
          uni.hideToast()
          uni.showToast({
            title: "创建本地通知消息失败",
            icon: "error"
          })
        }
      })
    } else {
      uni.showToast({
        title: "请在设置中开启通知权限",
        icon: "error"
      })
    }
  }

  async function getPushClientId(): Promise<string>{
    let pushClientId = '';
    let res:void = await new Promise<void>(resolve => {
      uni.getPushClientId({
        success: (res: GetPushClientIdSuccess) => {
          console.log(res.cid)
          pushClientId = res.cid
          resolve()
        },
        fail: (err: GetPushClientIdFail) => {
          resolve()
          console.error(err);
          if (err.message.includes('uniPush is not enabled')) {
            uni.showModal({
              title: '获取cid失败',
              content: '当前项目未启用uni-push,检查manifest.json中的uni-push配置',
              showCancel: false
            });
          } else if (err.message.includes('getPushClientId:fail register fail: {\"errorCode\":1,\"errorMsg\":\"\"}')) {
            uni.showModal({
              title: '获取cid失败',
              content: '当前项目未开通uni-push,开通文档:https://uniapp.dcloud.net.cn/unipush-v2.html#%E7%AC%AC%E4%B8%80%E6%AD%A5-%E5%BC%80%E9%80%9A',
              showCancel: false
            });
          } else {
            uni.showToast({
              title: `获取cid失败`,
              icon: "error"
            })
          }
        }
      })
    })
    return pushClientId
  }

  const handleGetClientId = async():Promise<void> =>{
    uni.showLoading({
      title: "正在获取cid",
    })
    const cid = await getPushClientId()
    if (cid != '') {
      // 自动化测试
      jestResult.clientId = cid
      if (!autoTest.value) {
        uni.showModal({
          title: "获取cid",
          content: "获取cid成功" + cid,
          showCancel: false
        })
      }
    }
    uni.hideLoading()
  }
  const handleSendPushMessage = async():Promise<void>=> {
    const pushClientId = await getPushClientId()
    if (pushClientId == ''){
      return
    }
    const uniPushCo = uniCloud.importObject("uni-push-co")
    try {
      const res = await uniPushCo.sendPushMessage(pushClientId)
      // 自动化测试
      jestResult.sendPushMessageRes = res.errCode as number
      if (!autoTest.value) {
        uni.showToast({
          title: "发送通知消息成功"
        })
      }
    } catch (err:any) {
      const error = err as UniCloudError
      console.error(error)
      if (!autoTest.value) {
        uni.showToast({
          title: "发送通知消息失败",
          icon: "error"
        })
      }
    }
  }
  const handleSetBadge = () => {
    if (uni.getDeviceInfo().deviceBrand?.toLowerCase() == "xiaomi") {
      if (uni.getAppAuthorizeSetting().notificationAuthorized == "authorized") {
        uni.setAppBadgeNumber(5, {
          title: "AppName",
          content: "您有5条未读消息"
        } as BadgeOptions)
        uni.showToast({
          title: "设置应用角标数为5"
        })
      } else {
        uni.showToast({
          title: "请在设置中开启通知权限",
          icon: "error"
        })
      }

    } else {
      uni.setAppBadgeNumber(5)
      uni.showToast({
        title: "设置应用角标数为5"
      })
    }
  }
  const handleCleanBadge = () => {
    if (uni.getDeviceInfo().deviceBrand?.toLowerCase() == "xiaomi") {
      if (uni.getAppAuthorizeSetting().notificationAuthorized == "authorized") {
        uni.setAppBadgeNumber(0, {} as BadgeOptions)
        uni.showToast({
          title: "清空应用角标数"
        })
      } else {
        uni.showToast({
          title: "请在设置中开启通知权限",
          icon: "error"
        })
      }
    } else {
      uni.setAppBadgeNumber(0)
      uni.showToast({
        title: "清空应用角标数"
      })
    }
  }

  // 自动化测试
  defineExpose({
    jestResult,
    autoTest,
    updateAutoTest,
    isRegister,
    handleSendPushMessage,
    handleGetClientId,
    handleOnPushMessage,
    handleOffPushMessage
  })
</script>

<style>
  .normal-button {
    width: 100%;
  }

  .instructions {
    margin-top: 10px;
    margin-left: 10px;
    margin-right: 10px;
    background-color: #eee;
  }
</style>

# 通用类型

# GeneralCallbackResult

名称 类型 必备 默认值 兼容性 描述
errMsg string -
错误信息

# app-android平台高级场景用途

nativeResources/android目录可以配置图片和声音的二进制文件资源。

# 通知栏显示图标自定义

关于图标的配置,需要创建nativeResources目录, 放置对应分辨率的图片资源即可(打包后生效),目录配置如下

├── nativeResources
│   └── android
│       └── res
│           ├── drawable-ldpi
│           │   ├── push.png            // 分辨率要求48x48
│           │   └── push_small.png      // 分辨率要求18x18
│           ├── drawable-mdpi
│           │   ├── push.png            // 分辨率要求64x64
│           │   └── push_small.png      // 分辨率要求24x24
│           ├── drawable-hdpi
│           │   ├── push.png            // 分辨率要求96x96
│           │   └── push_small.png      // 分辨率要求36x36
│           ├── drawable-xhdpi
│           │   ├── push.png            // 分辨率要求128x128
│           │   └── push_small.png      // 分辨率要求48x48
│           ├── drawable-xxhdpi
│           │   ├── push.png            // 分辨率要求192x192
│           │   └── push_small.png      // 分辨率要求72x72
│           ├── drawable-xxxhdpi
│           │   └── push_small.png      // 分辨率要求96x96
│           └── raw
│               └── pushsound.mp3       // 声音文件, 自定义推送铃音时使用

小图标要求

# 通知声音自定义

有些场景,如到账提醒,需要自定义通知声音。

setPushChannel设置新建渠道时,soundName字段的值为目录nativeResources->android->res->raw中存放的音频文件名称,(打包后生效) 注意不要带文件的后缀,比如pushsound.mp3文件,例:

const channelManager = getChannelManager()
channelManager.setPushChannel({
	channelId: "test1",
	channelDesc: "test1 desc",
	soundName: "pushsound"
})

# 在通知栏显示App下载进度

很多Android应用升级下载apk时会在通知栏显示下载进度。

该功能已经内置到uni升级中心中,此开源模板可直接使用。

# 在通知栏显示音乐播放条

需要使用uts插件,见插件市场

# 注意事项

  • 关于隐私安全问题,由于在调用getPushClientId或者onPushMessage时,才会初始化个推SDK,所以开发者要确保弹出隐私框之前不调用此两项API
  • 获取手机端app是否拥有push权限,请使用API uni.getAppAuthorizeSetting
  • uni-app x 的push模块仅支持uni-push2,不再支持uni-push1。但不要误会这不是强绑uniCloud的付费行为。而是DCloud的所有云服务都将统一纳入到uniCloud体系管理,开发者在开通uni-push2后,也可以拿到mastersecret,然后在自己的服务器去直接连接个推服务器。另外uniCloud的免费版也足够很多开发者使用。
  • uni-push是一个独立的模块,HBuilderX4.25以前版本标准基座中并不包含,HBuilder4.25及以上版本标准基座中包含,可直接在标准基座体验创建本地通知消息相关业务。由于push消息推送功能需要关联包名及签名信息,完整消息推送功能需打包自定义基座。
  • 创建本地通知栏,理论上可以和个推的服务无关。但目前也都包含在push模块里了。如果您不需要服务器推送,只需要本地创建通知栏,也需要打包push模块才行。
  • 部分手机创建本地通知时,App如果在后台状态,点击通知消息并不会拉起App,原因是厂商增加了后台弹窗权限,需要用户手动打开此权限。