爱码者说

鸿蒙应用集成Push Kit 指南

2025/09/18
12
0

相关资料

先给出Push Kit(推送服务)官方指南链接:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/push-kit-guide,把开发准备里面需要做的都做下

如果对 Push Kit 比较陌生,官方也提供了学习的视频和配套的资料,链接:https://developer.huawei.com/consumer/cn/training/course/slightMooc/C101718765607651496

接入指南

看完了前面的资料,相信你已经对Push Kit有了基本的了解了,下面就进入实战环节。

App端接入

App 端接入需要做的工作很少,主要就两个步骤:

  1. 获取Push Token,代码如下
import { pushService } from '@kit.PushKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { UIAbility, AbilityConstant, Want } from '@kit.AbilityKit';

export default class EntryAbility extends UIAbility {
  // 入参 want 与 launchParam 并未使用,为初始化项目时自带参数
  async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> {
    // 获取Push Token
    try {
      const pushToken: string = await pushService.getToken();
      hilog.info(0x0000, 'testTag', 'Succeeded in getting push token');
    } catch (err) {
      let e: BusinessError = err as BusinessError;
      hilog.error(0x0000, 'testTag', 'Failed to get push token: %{public}d %{public}s', e.code, e.message);
    }
    // 上报Push Token并上报到您的服务端
  }
}
  1. 获取通知权限,代码如下
import { BusinessError } from '@kit.BasicServicesKit';
import { UIAbility } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { notificationManager } from '@kit.NotificationKit';

class MyAbility extends UIAbility {
  onWindowStageCreate(windowStage: window.WindowStage) {
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
    windowStage.loadContent('pages/Index', (err, data) => {
      if (err.code) {
        hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
        return;
      }
      hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
      notificationManager.requestEnableNotification(this.context).then(() => {
        hilog.info(0x0000, 'testTag', `[ANS] requestEnableNotification success`);
      }).catch((err: BusinessError) => {
        hilog.error(0x0000, 'testTag', `[ANS] requestEnableNotification failed, code is ${err.code}, message is ${err.message}`);
      });
    });
  }
}

做完这两步就可以收到推送的通知了。这时候点击收到的推送,打开的是 app 的首页,如果想打开其他的界面,可以参考官方文档,链接:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/push-send-alert#section697519219136

服务端接入

服务端接入也不难,重点就是生成鉴权令牌,鉴权令牌生成的方法可以参考这个链接:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/push-jwt-token#section4780149123414

以 Java 代码示例,项目中先引入需要的maven依赖

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.16.2</version>
</dependency>
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt-api</artifactId>
  <version>0.11.5</version>
</dependency>
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt-impl</artifactId>
  <version>0.11.5</version>
  <scope>runtime</scope>
</dependency>
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt-jackson</artifactId>
  <version>0.11.5</version>
  <scope>runtime</scope>
</dependency>
<dependency>
  <groupId>org.bouncycastle</groupId>
  <artifactId>bcprov-jdk18on</artifactId>
  <version>1.78.1</version>
  <scope>runtime</scope>
</dependency>

生成鉴权令牌的代码如下

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import io.jsonwebtoken.*;
import io.jsonwebtoken.lang.Maps;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.Map;

public class JsonWebTokenFactory {

    // 实际开发时请将公网地址存储在配置文件或数据库
    private static final String AUD = "https://oauth-login.cloud.huawei.com/oauth2/v3/token";

    public static String createJwt() throws NoSuchAlgorithmException, InvalidKeySpecException, IOException, NullPointerException {
        // 读取配置文件
        ObjectMapper mapper = new ObjectMapper();
        // 上述private.json文件放置于工程的src/main/resources路径下
        URL url = JsonWebTokenFactory.class.getClassLoader().getResource("private.json");
        if (url == null) {
            throw new NullPointerException("File not exist");
        }
        JsonNode rootNode = mapper.readTree(new File(url.getPath()));

        RSAPrivateKey privateKey = (RSAPrivateKey) generatePrivateKey(rootNode.get("private_key").asText()
                                                                      .replace("-----BEGIN PRIVATE KEY-----", "")
                                                                      .replace("-----END PRIVATE KEY-----", "")
                                                                      .replaceAll("\\s", ""));
        long iat = System.currentTimeMillis() / 1000;
        long exp = iat + 3600;

        Map<String, Object> header = Maps.<String, Object>of(JwsHeader.KEY_ID, rootNode.get("key_id").asText())
        .and(JwsHeader.TYPE, JwsHeader.JWT_TYPE)
        .and(JwsHeader.ALGORITHM, SignatureAlgorithm.PS256.getValue())
        .build();

        Map<String, Object> payload = Maps.<String, Object>of(Claims.ISSUER, rootNode.get("sub_account").asText())
        .and(Claims.ISSUED_AT, iat)
        .and(Claims.EXPIRATION, exp)
        .and(Claims.AUDIENCE, AUD)
        .build();

        return Jwts.builder()
        .setHeader(header)
        .setPayload(new ObjectMapper().writeValueAsString(payload))
        .signWith(privateKey, SignatureAlgorithm.PS256)
        .compact();
    }

    private static PrivateKey generatePrivateKey(String base64Key) throws NoSuchAlgorithmException, InvalidKeySpecException {
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(base64Key.getBytes(StandardCharsets.UTF_8)));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePrivate(keySpec);
    }

    public static void main(String[] args) {
        try {
            // 获取鉴权令牌
            String jwt = createJwt();
        } catch (NoSuchAlgorithmException e) {
            // 异常处理流程1
        } catch (InvalidKeySpecException e) {
            // 异常处理流程2
        } catch (IOException e) {
            // 异常处理流程3
        } catch (NullPointerException e) {
            // 异常处理流程4
        }
    }
}

需要注意⚠️的是要先下载推送服务API的服务账号密钥文件,具体的下载方法可以查看官方文档,链接为:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/push-jwt-token#section16173145352

鉴权令牌生成后就可以进行消息的推送了,下面是我写的示例代码

 /**
     * 发送测试推送消息
     */
    public boolean pushTestMessage(String token, String title, String body) {
        return pushMessage(List.of(token), title, body, null, true);
    }

/**
     * 发送推送消息的核心方法
     *
     * @param tokens     设备token列表
     * @param title      通知标题
     * @param body       通知内容
     * @param customData 自定义数据
     * @param isTest     是否为测试消息
     * @return 是否发送成功
     */
    public boolean pushMessage(List<String> tokens, String title, String body, Map<String, String> customData, boolean isTest) {
        try {
//            String accessToken = getAccessToken();
            String accessToken = JsonWebTokenFactory.createJwt();
            return sendPushMessage(accessToken, tokens, title, body, customData, isTest);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

/**
     * 发送推送消息的核心实现
     */
    private boolean sendPushMessage(String accessToken, List<String> tokens, String title, String body, Map<String, String> customData, boolean isTest) {
        try {
            // 构建请求头
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(new MediaType(MediaType.APPLICATION_JSON, StandardCharsets.UTF_8));
            headers.setBearerAuth(accessToken);
            headers.set("push-type", "0");  // 华为推送要求的请求头

            // 构建请求体
            Map<String, Object> requestBody = buildRequestBody(tokens, title, body, customData, isTest);

            HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestBody, headers);

            // 发送请求
            String url = PUSH_URL.replace("{projectId}", projectId);
            ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);

            if (response.getStatusCode() == HttpStatus.OK) {
                System.out.println("推送成功: " + response.getBody());
                return true;
            } else {
                System.err.println("推送失败: " + response.getBody());
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

/**
     * 构建华为推送请求体
     */
    private Map<String, Object> buildRequestBody(List<String> tokens, String title, String body, Map<String, String> customData, boolean isTest) {
        Map<String, Object> requestBody = new HashMap<>();

        // 构建 payload
        Map<String, Object> payload = new HashMap<>();

        // 构建 notification
        Map<String, Object> notification = new HashMap<>();
        notification.put("category", "MARKETING");
        notification.put("title", title);
        notification.put("body", body);
        notification.put("notifyId", 12345);

        // 构建 clickAction
        Map<String, Object> clickAction = new HashMap<>();
        clickAction.put("actionType", 0);
        notification.put("clickAction", clickAction);

        // 设置前台是否显示通知,默认true表示前后台都展示
        notification.put("foregroundShow", true);

        payload.put("notification", notification);

        // 如果有自定义数据,添加到payload中
        if (customData != null && !customData.isEmpty()) {
            payload.put("data", customData);
        }

        requestBody.put("payload", payload);

        // 构建 target
        Map<String, Object> target = new HashMap<>();
        target.put("token", tokens);
        requestBody.put("target", target);

        // 构建 pushOptions
        Map<String, Object> pushOptions = new HashMap<>();
        pushOptions.put("testMessage", isTest);
//        pushOptions.put("ttl", 86400);  // 消息存活时间,默认24小时
        requestBody.put("pushOptions", pushOptions);

        return requestBody;
    }

通过下面的方式调用就可以实现服务端推送消息到客户端了

 pushTestMessage(user.getPushToken(), "记得记账哦!", "晚上好,今日是否还有收支未记录?");

结语

这篇文章只是推送普通消息的指南,目标是让你跑通基本的业务,如果你的项目中有其他的一些特殊要求,可以查看官方文档。