# 极光推送集成说明

## 目录结构

    capability-push/src/
    └── main/
        ├── AndroidManifest.xml          # Android 清单文件（权限声明）
        └── java/
            └── com/narutohuo/xindazhou/push/
                ├── api/                  # API 接口层
                │   └── PushService.kt    # 推送服务接口定义
                ├── factory/              # 工厂类层
                │   └── PushServiceFactory.kt  # 服务工厂（统一入口，单例获取）
                ├── impl/                 # 实现层
                │   └── PushServiceImpl.kt     # 推送服务具体实现（封装极光推送SDK）
                ├── receiver/             # 接收器层
                │   └── JPushReceiver.kt       # 推送消息接收器（完全封装在模块内部）
                ├── mapping/              # 页面映射层
                │   └── ActivityMapping.kt     # 静态页面映射配置（支持应用未启动时跳转）
                └── model/                # 数据模型层
                    ├── PushConfig.kt          # 推送服务配置模型
                    └── PushResponse.kt         # 推送相关的数据模型
                        ├── PushMessage        # 推送消息模型
                        └── PushResponse       # 推送响应模型

## 功能说明

### 1. api/ 层 - 接口定义

- 职责：定义推送服务的公共接口，业务层只依赖接口
- 内容：`PushService` 接口，包含初始化、别名/标签设置、消息监听、注册/注销等方法

### 2. factory/ 层 - 工厂类

- 职责：提供统一的服务实例获取和初始化方式
- 内容：`PushServiceFactory` 单例工厂
- 作用：隐藏实现细节，统一入口，便于替换实现

### 3. impl/ 层 - 具体实现

- 职责：实现 `PushService` 接口的具体逻辑
- 内容：`PushServiceImpl` 单例实现类
- 功能：封装极光推送 SDK 调用，处理推送消息接收，错误处理和日志记录

### 3.1 receiver/ 层 - 消息接收器

- 职责：封装推送消息接收逻辑
- 内容：`JPushReceiver` BroadcastReceiver
- 功能：
  - 自动接收极光推送的消息（自定义消息、通知消息、通知点击）
  - 自动解析消息内容
  - 完全封装：在模块的 `AndroidManifest.xml` 中自动注册，业务层无需处理
  - 自动跳转：优先使用静态配置（`ActivityMapping`）自动处理通知点击跳转，支持应用未启动场景

### 3.2 mapping/ 层 - 页面映射配置

- 职责：提供静态页面映射配置，支持应用未启动时的跳转
- 内容：`ActivityMapping` 静态配置类
- 功能：
  - 静态配置：页面映射关系在编译时确定，不依赖运行时初始化
  - 应用未启动支持：即使应用从未启动过，点击通知也能正常跳转
  - 向后兼容：支持运行时动态配置（通过 `pageMappings` 参数）
  - 优先级：静态配置优先，运行时配置作为补充

### 4. model/ 层 - 数据模型

- 职责：定义推送相关的数据模型
- 内容：
  - `PushConfig` - 推送服务配置模型
  - `PushMessage` - 推送消息模型（标题、内容、扩展数据等）
  - `PushResponse` - 推送响应模型（成功/失败、错误信息等）

## 架构设计

- 接口隔离：业务层只依赖接口，不依赖实现
- ARouter 依赖注入：通过 ARouter 实现模块间解耦，base-core 定义接口，能力层实现接口
- 工厂模式：统一入口，隐藏创建逻辑（内部使用 ARouter 获取服务）
- 单例模式：全局共享一个服务实例
- 分层架构：base-core (接口) → capability-push (实现) → ARouter (注册) → Factory (获取)
- 完全封装：跳转逻辑封装在模块内部，外部只需配置页面映射规则
- 静态配置优先：使用静态配置（`ActivityMapping`）确保应用未启动时也能正常跳转

### ARouter 架构说明

- base-core 模块定义 `IPushService` 接口（位于 `com.narutohuo.xindazhou.core.push`）
- capability-push 模块实现 `IPushService` 接口，并通过 `@Route(path = "/push/service")` 注册到 ARouter
- Factory 内部使用 `ARouter.getInstance().navigation(IPushService::class.java)` 获取服务实例
- 业务层 通过 Factory 获取服务，无需直接依赖实现模块

## 已完成的工作

✅ SDK 依赖已添加

- `jpush-5.9.0.aar` - 极光推送SDK（本地 AAR）
- `jcore-5.2.0.aar` - 极光推送核心库（本地 AAR）
- `cn.jiguang.sdk.plugin:xiaomi:5.9.0` - 小米通道
- `cn.jiguang.sdk.plugin:huawei:5.9.0` - 华为通道
- `cn.jiguang.sdk.plugin:oppo:5.9.0` - OPPO通道
- `cn.jiguang.sdk.plugin:vivo:5.9.0` - vivo通道
- `cn.jiguang.sdk.plugin:meizu:5.9.0` - 魅族通道

✅ 代码实现已完成

- `PushServiceImpl` 已实现极光推送 SDK 调用，并实现 `IPushService` 接口
- `PushServiceImpl` 已通过 ARouter 注册（`@Route(path = "/push/service")`）
- `PushServiceFactory` 已创建，内部使用 ARouter 获取服务
- `IPushService` 接口已在 `base-core` 模块定义
- `PushConfig` 配置模型已创建
- `JPushReceiver` 消息接收器已封装
- `ActivityMapping` 静态页面映射已实现：支持应用未启动时的跳转
- 跳转逻辑已封装：自动跳转功能已实现，优先使用静态配置
- ARouter 集成完成：模块间解耦，通过 ARouter 进行依赖注入

✅ 配置已完成

- 所有配置都在 `capability-push` 模块中管理
- `AndroidManifest.xml` 已在 `capability-push/src/main/AndroidManifest.xml` 中配置
- `strings.xml` 已在 `capability-push/src/main/res/values/strings.xml` 中配置

## 需要您配置的内容

### 1. 在极光推送官网注册应用

1. 访问 极光推送官网
2. 注册账号并创建应用
3. 获取 AppKey 和 AppSecret

### 2. 配置厂商通道参数（可选，但推荐）

在各厂商开放平台申请参数，并在 `capability-push/src/main/res/values/strings.xml` 中配置。

详细步骤请参考：厂商通道接入说明.md

### 3. 配置资源文件

所有配置都在 `capability-push/src/main/res/values/strings.xml` 中管理

编辑 `capability-push/src/main/res/values/strings.xml`：

    <resources>
        <!-- 极光推送基础配置（必填） -->
        <string name="jpush_app_key">您的极光推送 AppKey</string>
        <string name="push_jpush_channel">developer-default</string>

        <!-- 厂商通道配置（可选，但推荐配置以提升推送到达率） -->
        <!-- 详细配置请参考 厂商通道接入说明.md -->
    </resources>

### 3.5 配置静态页面映射（推荐，支持应用未启动场景）

方式1：使用静态配置（推荐）

在 `capability-push/src/main/java/com/narutohuo/xindazhou/push/mapping/ActivityMapping.kt` 中配置：

    package com.narutohuo.xindazhou.push.mapping

    import com.narutohuo.xindazhou.core.log.ILog

    /**
     * 静态页面映射配置
     *
     * 优势：
     * - 支持应用未启动时的跳转
     * - 不依赖 Application.onCreate() 的执行顺序
     * - 更可靠，避免初始化时序问题
     */
    object ActivityMapping {
        /**
         * 静态页面映射配置
         * 页面名称 -> Activity 完整类名
         */
        private val pageToActivityMap = mapOf(
            "detail" to "com.narutohuo.xindazhou.ui.DetailActivity",
            "home" to "com.narutohuo.xindazhou.ui.MainActivity",
            "order" to "com.narutohuo.xindazhou.ui.OrderActivity",
            // 添加更多页面映射...
        )

        /**
         * 根据页面名称获取 Activity Class
         *
         * @param page 页面名称（从推送的 extras["page"] 获取）
         * @return Activity Class，如果未找到返回 null
         */
        fun getActivityClass(page: String): Class<*>? {
            val className = pageToActivityMap[page] ?: return null
            return try {
                Class.forName(className)
            } catch (e: ClassNotFoundException) {
                ILog.e("ActivityMapping", "找不到 Activity: $className", e)
                null
            }
        }
    }

方式2：使用运行时配置（向后兼容）

在 `Application.onCreate()` 中通过 `pageMappings` 参数配置（见下方"4. 在 Application 中初始化"）。

推荐使用方式1（静态配置），因为：

- ✅ 支持应用未启动时的跳转
- ✅ 不依赖初始化顺序
- ✅ 更可靠

### 4. 在 Application 中初始化（统一入口）

在您的 `XinDaZhouApplication` 类中初始化推送服务：

    // app/src/main/java/com/narutohuo/xindazhou/XinDaZhouApplication.kt
    package com.narutohuo.xindazhou

    import android.app.Application
    import com.narutohuo.xindazhou.push.factory.PushServiceFactory

    class XinDaZhouApplication : Application() {

        override fun onCreate() {
            super.onCreate()

            // ========== 初始化推送服务（从资源文件读取配置）==========
            // 方式1：只使用静态配置（推荐，已在 ActivityMapping 中配置）
            PushServiceFactory.init(
                context = this
            )

            // 方式2：同时配置运行时映射（可选，作为静态配置的补充）
            PushServiceFactory.init(
                context = this,
                // 页面映射规则（用于自动跳转，作为静态配置的补充）
                // 注意：如果已在 ActivityMapping 中配置，这里可以不传
                pageMappings = mapOf(
                    "detail" to DetailActivity::class.java,  // 详情页
                    "home" to MainActivity::class.java,       // 首页
                )
            )
        }
    }

配置说明：

- ✅ 静态配置优先：`ActivityMapping` 中的配置优先级最高，即使应用未启动也能使用
- ✅ 运行时配置补充：`pageMappings` 参数作为补充，用于动态配置或覆盖静态配置
- ✅ 向后兼容：如果只使用 `pageMappings`，也能正常工作（但应用未启动时可能失败）

优势：

- ✅ 最简单：只需一行代码，配置都在资源文件和静态配置类中
- ✅ 统一入口：通过 `PushServiceFactory.init()` 统一初始化
- ✅ 跳转逻辑封装：跳转逻辑完全封装在模块内部，外部只需要配置页面映射规则
- ✅ 应用未启动支持：静态配置确保即使应用从未启动过，点击通知也能正常跳转
- ✅ 容错处理：初始化失败不会影响应用启动，只记录日志
- ✅ 自动识别设备：极光推送 SDK 自动识别设备类型并选择对应厂商通道
- ✅ 业务层不直接依赖极光推送API
- ✅ 如果更换SDK，只需修改实现层，业务层无需改动

## 核心功能说明

### 1. 通知展示（系统通知栏）

✅ 不需要我们处理，系统自动显示

- 极光推送 SDK 会自动在系统通知栏显示通知
- 系统会自动处理通知的展示、图标、声音等

### 2. 通知点击跳转（已封装）

✅ 跳转逻辑已完全封装在模块内部！

工作原理：

1. 用户点击通知栏通知
2. 模块内部自动解析 `extras["page"]`
3. 优先使用静态配置：从 `ActivityMapping` 获取 Activity Class（支持应用未启动场景）
4. 备选方案：如果静态配置未找到，使用运行时配置的 `pageMappings`
5. 根据映射关系自动跳转到对应 Activity
6. 所有 `extras` 参数自动传递给目标 Activity

优势：

- ✅ 应用未启动支持：即使应用从未启动过，点击通知也能正常跳转
- ✅ 不依赖初始化顺序：静态配置在类加载时即可用，不依赖 `Application.onCreate()`
- ✅ 更可靠：避免因初始化时序问题导致的跳转失败
- ✅ 向后兼容：仍支持运行时动态配置 `pageMappings`

服务器推送格式：

    {
      "notification": {
        "title": "新订单",
        "alert": "您有一个新订单待处理"
      },
      "extras": {
        "page": "detail",  // 必须匹配 ActivityMapping 或 pageMappings 中的 key
        "id": "12345"      // 会自动传递给目标 Activity
      }
    }

### 3. 厂商通道（自动识别）

✅ 极光推送 SDK 会自动识别设备类型，无需手动处理！

- ✅ 自动识别：SDK 会自动检测设备品牌（小米、华为、OPPO、vivo、魅族等）
- ✅ 自动选择通道：根据设备类型自动选择对应的厂商通道
- ✅ 自动降级：如果设备不是厂商手机，自动降级到极光通道
- ✅ 无需手动处理：不需要判断手机品牌，不需要手动选择通道

## 使用示例

### 初始化（在 Application 中）

    // 方式1：只使用静态配置（推荐，支持应用未启动场景）
    // 页面映射已在 ActivityMapping 中配置
    PushServiceFactory.init(
        context = this
    )

    // 方式2：同时配置运行时映射（可选，作为静态配置的补充）
    PushServiceFactory.init(
        context = this,
        messageListener = { message ->
            // 处理自定义消息（透传消息）
            Timber.d("收到推送: ${message.title} - ${message.content}")
        },
        notificationClickListener = { message ->
            // 处理通知点击事件（可选，用于统计、埋点等）
            Timber.d("用户点击通知，已自动跳转")
        },
        pageMappings = mapOf(
            "detail" to DetailActivity::class.java,
            // 注意：如果已在 ActivityMapping 中配置，这里可以不传
        )
    )

### 配置静态页面映射（在 ActivityMapping.kt 中）

    // capability-push/src/main/java/com/narutohuo/xindazhou/push/mapping/ActivityMapping.kt
    package com.narutohuo.xindazhou.push.mapping

    import com.narutohuo.xindazhou.core.log.ILog

    object ActivityMapping {
        private val pageToActivityMap = mapOf(
            "detail" to "com.narutohuo.xindazhou.ui.DetailActivity",
            "home" to "com.narutohuo.xindazhou.ui.MainActivity",
            "order" to "com.narutohuo.xindazhou.ui.OrderActivity",
            // 添加更多页面映射...
        )

        fun getActivityClass(page: String): Class<*>? {
            val className = pageToActivityMap[page] ?: return null
            return try {
                Class.forName(className)
            } catch (e: ClassNotFoundException) {
                ILog.e("ActivityMapping", "找不到 Activity: $className", e)
                null
            }
        }
    }

### 后续使用（可选，按需调用）

    val pushService = PushServiceFactory.getInstance()
    pushService.setAlias("user123")  // 设置别名（可选）
    pushService.setTags(listOf("vip", "premium"))  // 设置标签（可选）
    val registrationId = pushService.getRegistrationId()  // 获取注册ID（可选）
    val isEnabled = pushService.isPushEnabled()  // 检查推送状态（可选）

## 推送消息类型说明

1. 自定义消息（透传消息）：`messageListener`

   - 不会显示在系统通知栏
   - 需要在应用内自己处理并显示
   - 适用于需要应用内自定义显示的推送

2. 通知消息（系统通知栏）：

   - 展示：由极光推送 SDK 和系统自动处理，自动显示在通知栏
   - 点击：优先使用静态配置（`ActivityMapping`）自动跳转，支持应用未启动场景（已封装）
   - 适用于需要系统通知栏显示的推送

## 配置位置说明

⚠️ 重要：所有配置都在 `capability-push` 模块中管理，`app` 模块不需要任何配置！

- ✅ 配置位置：`capability-push/src/main/res/values/strings.xml`
- ✅ 清单文件：`capability-push/src/main/AndroidManifest.xml`
- ✅ app 模块：只需要在 `Application` 中调用 `PushServiceFactory.init()` 即可

❌ 禁止在 app 模块中配置：

- ❌ 禁止在 `app/src/main/AndroidManifest.xml` 中配置极光推送的 `meta-data`
- ❌ 禁止在 `app/src/main/res/values/strings.xml` 中配置极光推送参数
- ❌ 禁止在 `app/build.gradle` 中配置极光推送相关参数
- ✅ 所有配置都在 `capability-push` 模块中！

## 常见问题

### Q1: 应用从未启动过，点击通知能正常跳转吗？

A: 可以！ 使用静态配置（`ActivityMapping`）后，即使应用从未启动过，点击通知也能正常跳转。

原理：

- `BroadcastReceiver` 是独立组件，系统会在应用未启动时启动应用进程
- 静态配置在类加载时即可用，不依赖 `Application.onCreate()` 的执行
- 跳转逻辑优先使用静态配置，确保可靠性

### Q2: 静态配置和运行时配置（pageMappings）有什么区别？

A:

- 静态配置（`ActivityMapping`）：

  - ✅ 支持应用未启动时的跳转
  - ✅ 不依赖初始化顺序
  - ✅ 更可靠
  - ❌ 需要修改代码并重新编译

- 运行时配置（`pageMappings`）：

  - ✅ 可以动态配置
  - ✅ 不需要重新编译
  - ❌ 依赖 `Application.onCreate()` 的执行
  - ❌ 应用未启动时可能失败

推荐：优先使用静态配置，运行时配置作为补充。

### Q3: 如何添加新的页面映射？

A: 在 `ActivityMapping.kt` 的 `pageToActivityMap` 中添加新的映射关系：

    private val pageToActivityMap = mapOf(
        "detail" to "com.narutohuo.xindazhou.ui.DetailActivity",
        "newPage" to "com.narutohuo.xindazhou.ui.NewPageActivity",  // 新增
    )

注意：Activity 类名必须是完整的包名路径。

### Q4: 如果静态配置和运行时配置都配置了同一个页面，会使用哪个？

A: 静态配置（`ActivityMapping`）优先级更高。如果静态配置中找到了对应的 Activity，会优先使用静态配置；如果静态配置中未找到，才会使用运行时配置（`pageMappings`）。

## 参考文档

- 极光推送官方文档
- 厂商通道 SDK 集成指南
- 厂商通道接入说明.md - 本项目厂商通道配置说明