如何使用JavaScript获取OneSignal的User ID实现定向推送
问题根因
subscriptionChange事件不触发、getUserId返回NULL是两个原因导致的:
- 你写的第二段代码在调用
init后立刻执行getUserId,此时SDK还没完成和OneSignal服务端的通信、也没走完用户订阅流程,自然拿不到有效值 - WebView默认会禁用通知权限,部分场景下还会阻断Service Worker注册,而OneSignal必须拿到通知权限、完成Service Worker注册后才会生成有效的userId
分步解决方法
第一步:修正前端SDK初始化逻辑
不要在init后立刻调用getUserId,要等SDK完全就绪、用户完成订阅授权后再取值,可直接使用下面改好的代码:
<script src="https://cdn.onesignal.com/sdks/OneSignalSDK.js" async=""></script> <script> window.OneSignal = window.OneSignal || []; OneSignal.push(function() { OneSignal.init({ appId: "替换成你自己的appId", // WebView/本地调试必须加,允许非HTTPS环境下初始化 allowLocalhostAsSecureOrigin: true, // 自动触发通知授权请求 autoRegister: true, // 对应你放在站点根目录的Service Worker文件 serviceWorkerPath: 'OneSignalSDKWorker.js' }); // 监听SDK初始化完成事件,比subscriptionChange触发时机更稳定 OneSignal.on('init', function() { OneSignal.isPushNotificationsEnabled(function(isEnabled) { if (isEnabled) { // 已授权直接取userId OneSignal.getUserId(function(userId) { console.log("OneSignal UserID:", userId); document.querySelector(".some").innerHTML = userId; // 此处把userId上报到你的后端,和当前登录用户做绑定 }); } else { // 未授权时主动触发授权弹窗 OneSignal.registerForPushNotifications(); } }); }); // 订阅状态变化时同步更新userId OneSignal.on('subscriptionChange', function (isSubscribed) { if (isSubscribed) { OneSignal.getUserId(function(userId) { console.log("订阅状态更新,UserID:", userId); document.querySelector(".some").innerHTML = userId; // 此处把最新的userId上报到后端更新绑定关系 }); } }); }); </script>
第二步:给WebView开对应权限
如果是Android端WebView,必须在原生代码里加以下配置,否则通知权限会被系统直接拦截:
// 开启DOM存储 webView.getSettings().setDomStorageEnabled(true); // 开启JS执行权限 webView.getSettings().setJavaScriptEnabled(true); // 允许本地文件域跨域访问 webView.getSettings().setAllowFileAccessFromFileURLs(true); webView.getSettings().setAllowUniversalAccessFromFileURLs(true); // 处理网页端的权限请求,自动放行通知权限 webView.setWebChromeClient(new WebChromeClient() { @Override public void onPermissionRequest(final PermissionRequest request) { request.grant(request.getResources()); } });
如果是iOS端WKWebView,需要在配置中打开javaScriptEnabled开关,同时确保App本身已经拿到系统通知权限。
第三步:修改后端推送代码实现定向推送
拿到userId后,把原来全量推送的included_segments字段替换为include_player_ids,传入目标用户的userId即可,改好的PHP代码片段如下:
notify("Alert","Testing Notification"); $content = array("en" => "Testing Notification "); // $targetUserId 替换为你前端上报的、对应要触达用户的OneSignal userId $targetUserId = "替换为目标用户的userId"; $fields = array( 'app_id' => "替换为你的appId", // 移除全量推送配置,改为指定用户ID推送 'include_player_ids' => array($targetUserId), 'contents' => $content, 'data' => array("en" => "bar"), 'large_icon' =>"ic_launcher_round.png", ); $fields = json_encode($fields); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "https://onesignal.com/api/v1/notifications"); curl_setopt($ch, CURLOPT_HTTPHEADER, array( 'Content-Type: application/json; charset=utf-8', 'Authorization: Basic 替换为你的REST API Key' )); curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); curl_setopt($ch, CURLOPT_HEADER, FALSE); curl_setopt($ch, CURLOPT_POST, TRUE); curl_setopt($ch, CURLOPT_POSTFIELDS, $fields); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); $response = curl_exec($ch); curl_close($ch);
避坑提醒
- 必须把OneSignal官方提供的
OneSignalSDKWorker.js和OneSignalSDKUpdaterWorker.js两个文件放到站点根目录,否则Service Worker注册失败,永远拿不到userId - 单次调用
include_player_ids最多支持传2000个userId,批量触达更多用户时需要分批次请求 - WebView开启无痕模式时Service Worker和本地存储会被禁用,该场景下无法获取有效userId
- 不要在
init调用后立刻执行getUserId,必须等init回调或者subscriptionChange事件触发后再调用
内容的提问来源于stack exchange,提问作者Harry
相关产品推荐
相关产品推荐

