Android WebView如何在Firebase onNewToken时刷新页面并注入JS传Token
实现方案
FirebaseMessagingService 属于后台运行的Android组件,没有权限直接操作主线程的WebView UI,因此无法在Service内直接调用loadUrl刷新页面、注入JS。你之前考虑用Intent做跨组件传值的思路是完全可行的,具体落地逻辑如下:
核心流程
- 在
MyFirebaseMessagingService收到新Token时,通过App内部的本地广播将Token值传递给正在运行的MainActivity - 在
MainActivity内注册广播接收器,收到Token值后,在主线程操作WebView执行JS注入、页面刷新操作 - 现有
MyWebViewClient的URL拦截、外链跳转逻辑不需要做任何调整,和本次Token传递的流程完全独立
代码修改示例
1. 调整MyFirebaseMessagingService
移除直接操作WebView的逻辑,收到新Token后发送本地广播:
package com.example.app; import android.content.Intent; import android.util.Log; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import com.google.firebase.messaging.FirebaseMessagingService; public class MyFirebaseMessagingService extends FirebaseMessagingService { private static final String TAG = "MyFirebaseMessagingService"; // 广播标识,需和接收端保持一致 public static final String ACTION_TOKEN_REFRESH = "com.example.app.ACTION_TOKEN_REFRESH"; public static final String EXTRA_NEW_TOKEN = "fcm_refreshed_token"; @Override public void onNewToken(String token) { Log.d(TAG, "Refreshed token: " + token); // 构建携带Token的广播意图 Intent refreshIntent = new Intent(ACTION_TOKEN_REFRESH); refreshIntent.putExtra(EXTRA_NEW_TOKEN, token); // 发送内部广播 LocalBroadcastManager.getInstance(this).sendBroadcast(refreshIntent); } }
说明:选用LocalBroadcastManager传递内部数据,相比全局广播更安全、传输效率更高,不会造成App外的应用接收到敏感的Token信息。
2. 调整MainActivity
- 注册广播接收器,监听Token刷新事件
- 收到Token后在主线程操作WebView,完成JS注入和页面刷新
package com.example.app; import android.annotation.SuppressLint; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Build; import android.os.Bundle; import android.webkit.WebSettings; import android.webkit.WebView; import androidx.localbroadcastmanager.content.LocalBroadcastManager; public class MainActivity extends Activity { public WebView mWebView; // 定义Token刷新的广播接收器 private BroadcastReceiver tokenRefreshReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // 从广播中取出新的FCM Token String newToken = intent.getStringExtra(MyFirebaseMessagingService.EXTRA_NEW_TOKEN); // 操作WebView注入JS,将Token传递给Web应用 String injectJs = String.format( "window.postMessage({type: 'fcmTokenRefresh', token: '%s'}, '*')", newToken ); // 适配不同安卓版本的JS执行方式 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { mWebView.evaluateJavascript(injectJs, null); } else { mWebView.loadUrl("javascript:" + injectJs); } // 如需刷新WebView当前页面,取消下面这行的注释即可 // mWebView.reload(); } }; @Override @SuppressLint("SetJavaScriptEnabled") protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mWebView = findViewById(R.id.activity_main_webview); WebSettings webSettings = mWebView.getSettings(); webSettings.setJavaScriptEnabled(true); webSettings.setDomStorageEnabled(true); webSettings.setDatabaseEnabled(true); mWebView.setWebViewClient(new MyWebViewClient()); // 加载远程Web资源 mWebView.loadUrl("http://192.168.0.11:3000/"); // 注册本地广播接收器 IntentFilter filter = new IntentFilter(MyFirebaseMessagingService.ACTION_TOKEN_REFRESH); LocalBroadcastManager.getInstance(this).registerReceiver(tokenRefreshReceiver, filter); } @Override protected void onDestroy() { // 页面销毁时注销广播,避免内存泄漏 LocalBroadcastManager.getInstance(this).unregisterReceiver(tokenRefreshReceiver); super.onDestroy(); } @Override public void onBackPressed() { if(mWebView.canGoBack()) { mWebView.goBack(); } else { super.onBackPressed(); } } }
落地注意事项
- 如果App被切到后台、MainActivity被系统回收,收到Token刷新事件时,可直接将最新Token存入本地SharedPreferences。待用户下次打开App进入MainActivity时,自动读取存储的最新Token注入WebView,避免Token丢失。
- 不要在Service中持有WebView的强引用,否则会导致Activity销毁时WebView无法正常释放,引发内存泄漏。
- JS注入的逻辑可根据Web应用的实际约定调整,比如直接调用前端暴露的全局接口给后端上报Token即可。
内容的提问来源于stack exchange,提问作者Rodrigo Schneider
相关产品推荐
相关产品推荐

