推送通知 #

一、通知类型 #

1.1 本地通知 vs 远程通知 #

类型 触发方式 适用场景
本地通知 应用内触发 提醒、闹钟、定时任务
远程通知 服务器推送 即时消息、营销推送

二、本地通知 #

2.1 安装 #

bash
npm install @capacitor/local-notifications
npx cap sync

2.2 权限配置 #

iOS (Info.plist):

xml
<key>UIBackgroundModes</key>
<array>
    <string>remote-notification</string>
</array>

Android (AndroidManifest.xml):

xml
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

2.3 基本使用 #

typescript
import { LocalNotifications, ScheduleOptions, LocalNotificationSchema } from '@capacitor/local-notifications';

// 请求权限
async function requestPermission() {
    const result = await LocalNotifications.requestPermissions();
    return result.display === 'granted';
}

// 发送简单通知
async function sendNotification() {
    await LocalNotifications.schedule({
        notifications: [
            {
                title: '通知标题',
                body: '这是通知内容',
                id: 1,
                schedule: { at: new Date(Date.now() + 1000) },
                sound: undefined,
                attachments: undefined,
                actionTypeId: '',
                extra: null
            }
        ]
    });
}

2.4 延迟通知 #

typescript
// 延迟5秒发送
async function scheduleDelayedNotification() {
    await LocalNotifications.schedule({
        notifications: [
            {
                title: '延迟通知',
                body: '这是延迟5秒的通知',
                id: 2,
                schedule: {
                    at: new Date(Date.now() + 5000)
                }
            }
        ]
    });
}

// 定时通知
async function scheduleTimedNotification() {
    const tomorrow = new Date();
    tomorrow.setDate(tomorrow.getDate() + 1);
    tomorrow.setHours(9, 0, 0, 0);
    
    await LocalNotifications.schedule({
        notifications: [
            {
                title: '每日提醒',
                body: '早上好!新的一天开始了',
                id: 3,
                schedule: { at: tomorrow }
            }
        ]
    });
}

2.5 重复通知 #

typescript
// 每分钟重复
async function scheduleRepeatingNotification() {
    await LocalNotifications.schedule({
        notifications: [
            {
                title: '重复通知',
                body: '每分钟提醒一次',
                id: 4,
                schedule: {
                    every: 'minute',
                    count: 10  // 重复10次
                }
            }
        ]
    });
}

// 每天重复
async function scheduleDailyNotification() {
    await LocalNotifications.schedule({
        notifications: [
            {
                title: '每日提醒',
                body: '每天早上9点提醒',
                id: 5,
                schedule: {
                    every: 'day',
                    on: {
                        hour: 9,
                        minute: 0
                    }
                }
            }
        ]
    });
}

2.6 通知操作 #

typescript
// 取消通知
async function cancelNotification(id: number) {
    await LocalNotifications.cancel({
        notifications: [{ id }]
    });
}

// 取消所有通知
async function cancelAllNotifications() {
    const pending = await LocalNotifications.getPending();
    const ids = pending.notifications.map(n => ({ id: n.id }));
    
    if (ids.length > 0) {
        await LocalNotifications.cancel({ notifications: ids });
    }
}

// 获取待发送通知
async function getPendingNotifications() {
    const pending = await LocalNotifications.getPending();
    return pending.notifications;
}

2.7 通知监听 #

typescript
// 监听通知点击
await LocalNotifications.addListener('localNotificationReceived', (notification) => {
    console.log('Notification received:', notification);
});

// 监听通知点击动作
await LocalNotifications.addListener('localNotificationActionPerformed', (action) => {
    console.log('Action performed:', action);
    
    // 根据通知ID执行不同操作
    const notificationId = action.notification.id;
    const extra = action.notification.extra;
    
    // 跳转到特定页面
    handleNotificationAction(notificationId, extra);
});

2.8 带操作的通知 #

typescript
// 注册通知类型
async function registerActionTypes() {
    await LocalNotifications.registerActionTypes({
        types: [
            {
                id: 'CHAT_MESSAGE',
                actions: [
                    {
                        id: 'VIEW',
                        title: '查看'
                    },
                    {
                        id: 'REPLY',
                        title: '回复',
                        input: true
                    },
                    {
                        id: 'IGNORE',
                        title: '忽略',
                        destructive: true
                    }
                ]
            }
        ]
    });
}

// 发送带操作的通知
async function sendChatNotification() {
    await LocalNotifications.schedule({
        notifications: [
            {
                title: '新消息',
                body: 'John: 你好!',
                id: 10,
                actionTypeId: 'CHAT_MESSAGE',
                extra: {
                    chatId: 'chat_123',
                    userId: 'user_456'
                }
            }
        ]
    });
}

三、远程推送通知 #

3.1 安装 #

bash
npm install @capacitor/push-notifications
npx cap sync

3.2 iOS配置 #

创建APNs证书 #

  1. 登录Apple Developer
  2. 创建APNs证书或APNs Key (.p8)
  3. 配置App ID启用Push Notifications

配置Xcode #

swift
// ios/App/App/AppDelegate.swift
import UIKit
import Capacitor

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    
    func application(_ application: UIApplication, 
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        // 注册远程通知
        UNUserNotificationCenter.current().requestAuthorization(
            options: [.alert, .sound, .badge]
        ) { granted, error in
            if granted {
                DispatchQueue.main.async {
                    application.registerForRemoteNotifications()
                }
            }
        }
        
        return true
    }
    
    func application(_ application: UIApplication, 
                     didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        // 将deviceToken发送到服务器
    }
    
    func application(_ application: UIApplication, 
                     didFailToRegisterForRemoteNotificationsWithError error: Error) {
        print("Failed to register for remote notifications: \(error)")
    }
}

3.3 Android配置 #

Firebase配置 #

  1. 创建Firebase项目
  2. 添加Android应用
  3. 下载google-services.json
  4. 放到 android/app/ 目录

build.gradle配置 #

groovy
// android/build.gradle
buildscript {
    dependencies {
        classpath 'com.google.gms:google-services:4.3.15'
    }
}

// android/app/build.gradle
apply plugin: 'com.google.gms.google-services'

dependencies {
    implementation 'com.google.firebase:firebase-messaging:23.1.0'
}

3.4 基本使用 #

typescript
import { PushNotifications } from '@capacitor/push-notifications';

// 注册推送
async function registerPush() {
    // 请求权限
    const result = await PushNotifications.requestPermissions();
    
    if (result.receive === 'granted') {
        // 注册
        await PushNotifications.register();
    }
}

// 监听注册成功
await PushNotifications.addListener('registration', (token) => {
    console.log('Push registration success, token:', token.value);
    
    // 将token发送到服务器
    sendTokenToServer(token.value);
});

// 监听注册失败
await PushNotifications.addListener('registrationError', (error) => {
    console.error('Push registration error:', error);
});

// 监听推送接收
await PushNotifications.addListener('pushNotificationReceived', (notification) => {
    console.log('Push received:', notification);
});

// 监听推送点击
await PushNotifications.addListener('pushNotificationActionPerformed', (action) => {
    console.log('Push action performed:', action);
    
    // 处理点击事件
    handlePushAction(action.notification.data);
});

3.5 获取Token #

typescript
// 获取FCM/APNs Token
async function getPushToken() {
    const result = await PushNotifications.register();
    return result;
}

// 获取已注册的Token
async function checkToken() {
    const { value } = await PushNotifications.getToken();
    return value;
}

四、通知服务封装 #

4.1 通知服务类 #

typescript
// src/services/notification.service.ts
import { LocalNotifications, LocalNotificationSchema } from '@capacitor/local-notifications';
import { PushNotifications, PushNotificationToken } from '@capacitor/push-notifications';
import { Capacitor } from '@capacitor/core';

type NotificationCallback = (data: any) => void;

class NotificationService {
    private callbacks: Map<string, NotificationCallback[]> = new Map();
    private pushToken: string | null = null;
    
    async init(): Promise<void> {
        // 初始化本地通知
        await this.initLocalNotifications();
        
        // 初始化远程推送(仅原生平台)
        if (Capacitor.isNativePlatform()) {
            await this.initPushNotifications();
        }
    }
    
    private async initLocalNotifications(): Promise<void> {
        // 请求权限
        const result = await LocalNotifications.requestPermissions();
        
        if (result.display !== 'granted') {
            console.warn('Local notification permission not granted');
            return;
        }
        
        // 注册操作类型
        await LocalNotifications.registerActionTypes({
            types: [
                {
                    id: 'DEFAULT',
                    actions: [
                        { id: 'OPEN', title: '打开' },
                        { id: 'DISMISS', title: '关闭' }
                    ]
                }
            ]
        });
        
        // 监听事件
        await LocalNotifications.addListener(
            'localNotificationActionPerformed',
            this.handleLocalAction.bind(this)
        );
    }
    
    private async initPushNotifications(): Promise<void> {
        const result = await PushNotifications.requestPermissions();
        
        if (result.receive !== 'granted') {
            console.warn('Push notification permission not granted');
            return;
        }
        
        await PushNotifications.register();
        
        await PushNotifications.addListener('registration', (token) => {
            this.pushToken = token.value;
            console.log('Push token:', token.value);
        });
        
        await PushNotifications.addListener('registrationError', (error) => {
            console.error('Push registration error:', error);
        });
        
        await PushNotifications.addListener(
            'pushNotificationActionPerformed',
            this.handlePushAction.bind(this)
        );
    }
    
    // 发送本地通知
    async schedule(notification: Omit<LocalNotificationSchema, 'id'> & { id?: number }) {
        await LocalNotifications.schedule({
            notifications: [
                {
                    id: notification.id || Date.now(),
                    title: notification.title,
                    body: notification.body,
                    schedule: notification.schedule,
                    extra: notification.extra,
                    actionTypeId: notification.actionTypeId || 'DEFAULT'
                }
            ]
        });
    }
    
    // 立即发送
    async show(title: string, body: string, extra?: any) {
        await this.schedule({
            title,
            body,
            extra,
            schedule: { at: new Date(Date.now() + 100) }
        });
    }
    
    // 取消通知
    async cancel(id: number) {
        await LocalNotifications.cancel({
            notifications: [{ id }]
        });
    }
    
    // 订阅通知事件
    on(event: string, callback: NotificationCallback): () => void {
        if (!this.callbacks.has(event)) {
            this.callbacks.set(event, []);
        }
        
        this.callbacks.get(event)!.push(callback);
        
        return () => {
            const callbacks = this.callbacks.get(event);
            if (callbacks) {
                const index = callbacks.indexOf(callback);
                if (index > -1) {
                    callbacks.splice(index, 1);
                }
            }
        };
    }
    
    private handleLocalAction(action: any) {
        const { notificationId, actionId, inputValue } = action;
        const extra = action.notification.extra;
        
        this.emit('localAction', {
            notificationId,
            actionId,
            inputValue,
            extra
        });
    }
    
    private handlePushAction(action: any) {
        const data = action.notification.data;
        this.emit('pushAction', data);
    }
    
    private emit(event: string, data: any) {
        const callbacks = this.callbacks.get(event);
        if (callbacks) {
            callbacks.forEach(cb => cb(data));
        }
    }
    
    getPushToken(): string | null {
        return this.pushToken;
    }
}

export const notificationService = new NotificationService();

4.2 使用示例 #

typescript
// 初始化
await notificationService.init();

// 订阅事件
notificationService.on('localAction', (data) => {
    console.log('Local action:', data);
});

notificationService.on('pushAction', (data) => {
    console.log('Push action:', data);
});

// 发送通知
await notificationService.show('新消息', '您有一条新消息', {
    type: 'chat',
    chatId: '123'
});

// 获取推送Token
const token = notificationService.getPushToken();

五、服务器端推送 #

5.1 Firebase Cloud Messaging #

javascript
// Node.js服务器示例
const admin = require('firebase-admin');

// 初始化
admin.initializeApp({
    credential: admin.credential.cert(serviceAccount)
});

// 发送推送
async function sendPush(token, title, body, data) {
    const message = {
        token,
        notification: {
            title,
            body
        },
        data
    };
    
    const response = await admin.messaging().send(message);
    console.log('Successfully sent message:', response);
}

// 发送到主题
async function sendToTopic(topic, title, body) {
    const message = {
        topic,
        notification: {
            title,
            body
        }
    };
    
    await admin.messaging().send(message);
}

5.2 APNs推送 #

javascript
// Node.js APNs示例
const apn = require('apn');

const options = {
    token: {
        key: 'path/to/AuthKey.p8',
        keyId: 'KEY_ID',
        teamId: 'TEAM_ID'
    },
    production: false
};

const apnProvider = new apn.Provider(options);

async function sendAPNs(deviceToken, title, body, data) {
    const notification = new apn.Notification();
    
    notification.alert = {
        title,
        body
    };
    notification.topic = 'com.example.app';
    notification.payload = data;
    
    const result = await apnProvider.send(notification, deviceToken);
    console.log(result);
}

六、总结 #

6.1 核心功能 #

功能 插件
本地通知 @capacitor/local-notifications
远程推送 @capacitor/push-notifications

6.2 下一步 #

了解推送通知后,让我们学习 设备信息

最后更新:2026-03-28