# 关于 Pushwoosh Android SDK 的常见问题解答

* [Android 推送令牌和 HWID 是什么样子的？](#what-does-android-push-token-and-hwid-look-like)
* [如何获取我的 Android 设备推送令牌？](#how-can-i-obtain-my-android-device-push-token)
* [哪些权限是必需的，哪些是可选的？](#what-permissions-are-necessary-and-what-are-optional)
* [如何设置 UserID？](#how-do-i-set-a-userid)
* [Android 订阅者总数的准确性如何？](#how-accurate-is-the-total-number-of-android-subscribers)
* [我可以在发送到 Android 的推送中使用 HTML 标签吗？](#can-i-use-html-tags-in-pushes-sent-to-android)
* [如何在 Android Lollipop（及更高版本）中设置通知图标？](#how-to-set-a-notification-icon-in-android-lollipop-and-later-versions)
* [将 Pushwoosh SDK 与其他 FCM 服务一起使用](#using-pushwoosh-sdk-with-other-fcm-services)
* [将 Pushwoosh 与 LeakCanary 或 AppMetrica 库一起使用](#using-pushwoosh-with-leakcanary-or-appmetrica-libraries)
* [从设备删除收到的通知](#deleting-particular-received-notifications-from-a-device)

### Android 推送令牌和 HWID 是什么样子的？

Android 设备推送令牌的长度不同（小于 255 个字符），并以 **APA91b** 开头，例如：

`APA91bFoi3lMMre9G3XzR1LrF4ZT82_15MsMdEICogXSLB8-MrdkRuRQFwNI5u8Dh0cI90ABD3BOKnxkEla8cGdisbDHl5cVIkZah5QUhSAxzx4Roa7b4xy9tvx9iNSYw-eXBYYd8k1XKf8Q_Qq1X9-x-U-Y79vdPq`

令牌在 **APA91b** 之前通常包含一个以冒号分隔的前缀，例如：`eQnyCE6ULAQ:APA91bGrh4ya3b_owo9tshZNVAGhZdGMGb3sA5HbM...`

Pushwoosh 使用 UUID 作为 HWID，即随机生成的 32 个字母数字字符的字符串：`123e4567-e89b-12d3-a456-426655440000`

### 如何获取我的 Android 设备推送令牌？

您可以在控制台日志中获取您的 Android 设备推送令牌。请使用 [Android Studio](https://developer.android.com/studio) 中的 logcat 工具。

在 `%USERPROFILE%\AppData\Local\Android\sdk\tools\monitor.bat` 中打开 **monitor.bat**，将您的设备连接到 PC，并在 Android 设置中允许 USB 调试。在设备上运行您的应用程序。找到 `/registerDevice`，找到您设备的推送令牌，以便稍后在“测试设备”中使用。

<img src="/android-push-notifications-android-faq-1.webp" alt="截图显示 Android Studio 中显示的带有推送令牌注册信息的 logcat 输出"/>

### 哪些权限是必需的，哪些是可选的？

当安装在 Android 设备上时，应用程序将请求与 Pushwoosh SDK 相关的以下权限：

```java
<!-- FCM 连接到 Firebase 服务。 -->
 <uses-permission android:name="android.permission.INTERNET"/>

<!-- 在收到消息时防止处理器进入休眠状态。 -->
 <uses-permission android:name="android.permission.WAKE_LOCK"/>

 <!-- 此权限用于确定设备是否可以访问网络。 -->
 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

<!-- 此应用有权注册和接收数据消息。 -->
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
```

我们的 SDK 不会请求访问图片、设备联系人等权限。

### 如何设置 UserID？

您可以设置一个[用户标识符 (UserID)](/zh/developer/first-steps/collecting-user-ids/)，它可以是 Facebook ID、用户名、电子邮件或任何其他唯一的用户 ID。这使您能够匹配与同一用户关联的多个设备上的数据和事件。要设置 UserID，请调用 **setUserId** 方法。

**示例**

```
Pushwoosh.getInstance().setUserId("testUser");
```

### Android 订阅者总数的准确性如何？

Pushwoosh 在收到来自 FCM 的“NotRegistered”响应后，会从数据库中清除已取消订阅的 Android 设备，该响应可能在第二次尝试联系特定设备后返回。这意味着您必须向一个已取消订阅的设备发送 2 次推送，才能将其从数据库中移除。

以下是 [FCM 文档](https://firebase.google.com/docs/cloud-messaging/)中描述的最常见场景：

1. 您的订阅者卸载了应用。
2. Pushwoosh 向 FCM 服务器发送一条消息。
3. FCM 服务器将消息发送到您用户的设备。
4. 设备上的 FCM 客户端收到消息并检测到您的应用程序已被卸载；检测细节取决于应用运行的平台。
5. 设备上的 FCM 客户端通知 FCM 服务器应用已被卸载。
6. FCM 服务器将注册 ID 标记为待删除。
7. Pushwoosh 向 FCM 发送另一条消息。
8. FCM 返回一条 NotRegistered 消息。
9. Pushwoosh 从您的用户群中移除该推送令牌。

注册 ID 可能需要一段时间才能从 FCM 中完全移除。因此，即使消息不会传递到客户端应用，在上述步骤 7 中发送的消息也可能获得一个有效的消息 ID 作为响应。

### 我可以在发送到 Android 的推送中使用 HTML 标签吗？

是的，在 Android 中，您可以使用以下 HTML 标签来修改推送的外观：

```txt
<span style="color: green;"><b><i><span style="text-decoration: underline;">Hello world!
Hello hi hey</span></i></b></span>
```

将这些 HTML 标签放在**消息**输入字段中，并在 API 请求中使用它们。某些 Android 设备可能无法正确处理这些 HTML 标签，但大多数设备都能正确显示格式。

### 如何在 Android Lollipop（及更高版本）中设置通知图标？

在 Android Lollipop 中，图标被更改为仅为白色。因此，如果您在 **AndroidManifest.xml** 文件中选择 **targetSdkVersion >= 21**，Android 将只使用图标的 alpha 通道。\
有关此行为的更多信息，请参阅 [Android 文档](https://developer.android.com/docs)。

系统会忽略操作图标和主通知图标中的所有非 alpha 通道。假设这些图标将仅为 alpha 通道。系统以白色绘制通知图标，以深灰色绘制操作图标。_这超出了 Pushwoosh SDK 的控制范围_。

**1.** 根据 [Android 指南](https://material.io/design/platform-guidance/android-notifications.html#style)创建通知图标。根据[文档](https://developer.android.com/about/versions/lollipop/android-5.0-changes)，系统将忽略所有颜色。

**2.** 将图标命名为 `pw_notification.png` 并将其放入 **res/drawable** 文件夹中。Pushwoosh SDK 将使用此图标作为通知的默认图标。

**3.** 或者，您可以使用远程 API 并将 `"android_icon"` 参数值设置为图标图像（不带文件扩展名）。

### 将 Pushwoosh SDK 与其他 FCM 服务一起使用

您可以将 Pushwoosh 与其他使用 FCM 进行推送消息传递的 SDK 一起使用。为此，请创建一个路由器服务以在这些服务之间分发事件。首先，在主 Pushwoosh 模块旁边添加 **pushwoosh-firebase** 依赖项：

```groovy title="build.gradle"
implementation 'com.pushwoosh:pushwoosh-firebase:6.0.3'
```

创建路由类：

<Tabs>
<TabItem label="FirebaseMessagingRouterService.kt">
```kotlin
import com.pushwoosh.firebase.PushwooshFcmHelper;

class FirebaseMessagingRouterService : FirebaseMessagingService() {

    override fun onNewToken(token: String?) {
        super.onNewToken(token)
        PushwooshFcmHelper.onTokenRefresh(token)
        sendTokenToAnotherService(token)
    }

    override fun onMessageReceived(remoteMessage: RemoteMessage) {
        if (PushwooshFcmHelper.isPushwooshMessage(remoteMessage)) {
           // 这是 Pushwoosh 推送，SDK 将自动处理
            PushwooshFcmHelper.onMessageReceived(this, remoteMessage)
        } else {
            // 这不是 Pushwoosh 推送，您应自行处理
            dispatchNonPushwooshMessage(remoteMessage);
        }
    }

    private fun dispatchNonPushwooshMessage(remoteMessage: RemoteMessage) {
       // 在此处实现您的推送处理逻辑
   }
}
```
</TabItem>

<TabItem label="FirebaseMessagingRouterService.java">
```java
import com.pushwoosh.firebase.PushwooshFcmHelper;

public class FirebaseMessagingRouterService extends FirebaseMessagingService {

    @Override
    public void onNewToken(String token) {
        super.onNewToken(token);
        PushwooshFcmHelper.onTokenRefresh(token);
        sendTokenToAnotherService(token);
    }

    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        if (PushwooshFcmHelper.isPushwooshMessage(remoteMessage)) {
            // 这是 Pushwoosh 推送，SDK 将自动处理
            PushwooshFcmHelper.onMessageReceived(this, remoteMessage);
        } else {
            // 这不是 Pushwoosh 推送，您应自行处理
            dispatchNonPushwooshMessage(remoteMessage);
        }
    }

    private void dispatchNonPushwooshMessage(RemoteMessage remoteMessage) {
        // 在此处实现您的推送处理逻辑
    }
}
```
</TabItem>
</Tabs>

在您的 AndroidManifest.xml 中注册路由器：

```txt title="AndroidManifest.xml"
<service
    android:name=".FirebaseMessagingRouterService">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT"/>
    </intent-filter>
</service>
```

### 将 Pushwoosh 与 LeakCanary 或 AppMetrica 库一起使用

当您集成 LeakCanary、AppMetrica 或其他分析工具时，这些库会启动一个新进程，创建应用的新实例。由于您无法在另一个进程中监听推送通知，这会导致抛出 `java.lang.NullPointerException`。

如果您在 `Application.onCreate()` 中调用 `registerForPushNotifications`，您应该检查您是否在应用程序的主进程中。使用以下代码执行此检查：

```java
List<ActivityManager.RunningAppProcessInfo> runningAppProcesses = ((ActivityManager) getSystemService(Context.ACTIVITY_SERVICE)).getRunningAppProcesses();
if (runningAppProcesses != null && runningAppProcesses.size() != 0) {
    for (ActivityManager.RunningAppProcessInfo runningAppProcessInfo : runningAppProcesses) {
        boolean isCurrentProcess = runningAppProcessInfo.pid == android.os.Process.myPid();
        boolean isMainProcessName = getPackageName().equals(runningAppProcessInfo.processName);
        if (isCurrentProcess && isMainProcessName) {

            Pushwoosh.getInstance().registerForPushNotifications(...);
            break;
        }
    }
}
```

### 在设备本地删除特定的已接收通知

如果您想在应用程序内执行特定操作时移除不相关的推送，可以使用以下方法：

```java title="MainActivity.java"
public class MainActivity extends AppCompatActivity {

	List<NotificationCreatedEvent> savedPushes = new ArrayList<>();

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		Pushwoosh.getInstance().registerForPushNotifications();

		PushwooshNotificationSettings.setMultiNotificationMode(true);

		findViewById(R.id.clear_button).setOnClickListener( v -> {
			cancelPushes();
		});

		EventBus.subscribe(NotificationCreatedEvent.class, event -> {
			try {
				if (event.getMessage().getCustomData() == null)
					return;

				// 仅取消带有特定标志的推送
				if (new JSONObject(event.getMessage().getCustomData()).getBoolean("cancel")) {
					savedPushes.add(event);
				}
			} catch (JSONException e) {
			}
		});
	}
	private void cancelPushes() {
		NotificationManager manager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);

		if (manager == null) {
			return;
		}

		for (NotificationCreatedEvent event : savedPushes) {
			manager.cancel(event.getMessageTag(), event.getMessageId());
		}
	}
}
```

## 与我们分享您的反馈

您的反馈有助于我们创造更好的体验。如果您在 SDK 集成过程中遇到任何困难，请[通过此表单](https://docs.google.com/forms/d/e/1FAIpQLSd_0b8jwn-V_JmoPLIxIFYbHACCQhrzidOZV3ELywoQPXRSxw/viewform)与我们分享您的想法。