Firebase FCM强制onTokenRefresh()被调用
我正在将我的应用程序从GCM迁移到FCM。
当新用户安装我的应用程序时, onTokenRefresh()
会自动被调用。 问题是用户还没有login(没有用户ID)。
如何在用户login后触发onTokenRefresh()
?
onTokenRefresh()
方法将在每当生成新的标记时被调用。 应用程序安装后,它会立即生成(如你发现的情况)。 当令牌改变时,它也会被调用。
根据FirebaseCloudMessaging
指南:
您可以将通知定位到单个特定设备。 在您的应用程序初次启动时,FCM SDK为客户端应用程序实例生成注册令牌。
来源链接: https : //firebase.google.com/docs/notifications/android/console-device#access_the_registration_token
这意味着令牌注册是每个应用程序。 这听起来像你想在用户login后使用令牌。我会build议您将onTokenRefresh()
方法中的令牌保存到内部存储或共享首选项。 然后,在用户login后从存储中检索令牌,并根据需要在服务器上注册令牌。
如果您想手动强制执行onTokenRefresh()
,则可以创build一个IntentService并删除该令牌实例。 然后,当您调用onTokenRefresh()
时, onTokenRefresh()
方法将再次被调用。
示例代码:
public class DeleteTokenService extends IntentService { public static final String TAG = DeleteTokenService.class.getSimpleName(); public DeleteTokenService() { super(TAG); } @Override protected void onHandleIntent(Intent intent) { try { // Check for current token String originalToken = getTokenFromPrefs(); Log.d(TAG, "Token before deletion: " + originalToken); // Resets Instance ID and revokes all tokens. FirebaseInstanceId.getInstance().deleteInstanceId(); // Clear current saved token saveTokenToPrefs(""); // Check for success of empty token String tokenCheck = getTokenFromPrefs(); Log.d(TAG, "Token deleted. Proof: " + tokenCheck); // Now manually call onTokenRefresh() Log.d(TAG, "Getting new token"); FirebaseInstanceId.getInstance().getToken(); } catch (IOException e) { e.printStackTrace(); } } private void saveTokenToPrefs(String _token) { // Access Shared Preferences SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences.Editor editor = preferences.edit(); // Save to SharedPreferences editor.putString("registration_id", _token); editor.apply(); } private String getTokenFromPrefs() { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); return preferences.getString("registration_id", null); } }
尝试实施FirebaseInstanceIdService
以获取刷新令牌。
访问注册令牌:
您可以通过扩展FirebaseInstanceIdService来访问令牌的值。 确保已经将该服务添加到清单中 ,然后在onTokenRefresh
的上下文中onTokenRefresh
,并按如下所示logging值:
@Override public void onTokenRefresh() { // Get updated InstanceID token. String refreshedToken = FirebaseInstanceId.getInstance().getToken(); Log.d(TAG, "Refreshed token: " + refreshedToken); // TODO: Implement this method to send any registration to your app's servers. sendRegistrationToServer(refreshedToken); }
完整代码:
import android.util.Log; import com.google.firebase.iid.FirebaseInstanceId; import com.google.firebase.iid.FirebaseInstanceIdService; public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService { private static final String TAG = "MyFirebaseIIDService"; /** * Called if InstanceID token is updated. This may occur if the security of * the previous token had been compromised. Note that this is called when the InstanceID token * is initially generated so this is where you would retrieve the token. */ // [START refresh_token] @Override public void onTokenRefresh() { // Get updated InstanceID token. String refreshedToken = FirebaseInstanceId.getInstance().getToken(); Log.d(TAG, "Refreshed token: " + refreshedToken); // TODO: Implement this method to send any registration to your app's servers. sendRegistrationToServer(refreshedToken); } // [END refresh_token] /** * Persist token to third-party servers. * * Modify this method to associate the user's FCM InstanceID token with any server-side account * maintained by your application. * * @param token The new token. */ private void sendRegistrationToServer(String token) { // Add custom implementation, as needed. } }
在这里看到我的答案。
EDITS:
您不应该自己启动FirebaseInstanceIdService 。
它会在系统确定令牌需要刷新时调用。 应用程序应调用getToken()并将令牌发送到所有应用程序服务器。
这不会被频繁地调用,它是需要的密钥轮换和处理实例ID更改由于:
- 应用程序删除实例ID
- 应用程序在新设备用户上恢复
- 卸载/重新安装应用程序
- 用户清除应用程序数据
系统将限制所有设备上的刷新事件,以避免使用令牌更新过载应用程序服务器。
尝试下面的方法 :
你可以在主线程(无论是服务,AsyncTask等)的任何地方调用FirebaseInstanceID.getToken() ,将返回的令牌本地存储并发送到服务器。 然后,无论何时
onTokenRefresh()
被调用,你会再次调用FirebaseInstanceID.getToken() ,获得一个新的令牌,并将其发送到服务器(可能还包括旧的令牌,以便您的服务器可以删除它,用新的一个)。
这个答案不会破坏实例ID,而是能够获得当前的一个。 它也存储在共享首选项中刷新。
strings.xml中
<string name="pref_firebase_instance_id_key">pref_firebase_instance_id</string> <string name="pref_firebase_instance_id_default_key">default</string>
Utility.java(你想设置/获取首选项的任何类)
public static void setFirebaseInstanceId(Context context, String InstanceId) { SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor editor; editor = sharedPreferences.edit(); editor.putString(context.getString(R.string.pref_firebase_instance_id_key),InstanceId); editor.apply(); } public static String getFirebaseInstanceId(Context context) { SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); String key = context.getString(R.string.pref_firebase_instance_id_key); String default_value = context.getString(R.string.pref_firebase_instance_id_default_key); return sharedPreferences.getString(key, default_value); }
MyFirebaseInstanceIdService.java(扩展了FirebaseInstanceIdService)
@Override public void onCreate() { String CurrentToken = FirebaseInstanceId.getInstance().getToken(); //Log.d(this.getClass().getSimpleName(),"Inside Instance on onCreate"); String savedToken = Utility.getFirebaseInstanceId(getApplicationContext()); String defaultToken = getApplication().getString(R.string.pref_firebase_instance_id_default_key); if(CurrentToken != null && !savedToken.equalsIgnoreCase(defaultToken)) //currentToken is null when app is first installed and token is not available //also skip if token is already saved in preferences... { Utility.setFirebaseInstanceId(getApplicationContext(),CurrentToken); } super.onCreate(); } @Override public void onTokenRefresh() { .... prev code Utility.setFirebaseInstanceId(getApplicationContext(),refreshedToken); ....
}
自动启动( 源 )时,不会调用Android 2.0及更高版本的服务创build。 相反, onStartCommand
被覆盖并使用。 但在实际的FirebaseInstanceIdService中,它被声明为final,不能被覆盖。 但是,当我们使用startService()启动服务时,如果服务已经在运行, 则使用它的原始实例 (这是很好的)。 我们的onCreate()(定义如上)也被调用!
在开始使用MainActivity的时候,或者你认为你需要实例id的地方。
MyFirebaseInstanceIdService myFirebaseInstanceIdService = new MyFirebaseInstanceIdService(); Intent intent= new Intent(getApplicationContext(),myFirebaseInstanceIdService.getClass()); //Log.d(this.getClass().getSimpleName(),"Starting MyFirebaseInstanceIdService"); startService(intent); //invoke onCreate
最后,
Utility.getFirebaseInstanceId(getApplicationContext())
请注意 ,您可以通过尝试将startservice()代码移至getFirebaseInstanceId方法来进一步增强此function。
我在共享pref中维护一个标志,它指示gcm令牌是否发送到服务器。 每当我调用一个方法sendDevicetokenToServer时,在Splash屏幕中。 此方法检查用户标识是否不为空,gcm发送状态然后将令牌发送到服务器。
public static void sendRegistrationToServer(final Context context) { if(Common.getBooleanPerf(context,Constants.isTokenSentToServer,false) || Common.getStringPref(context,Constants.userId,"").isEmpty()){ return; } String token = FirebaseInstanceId.getInstance().getToken(); String userId = Common.getUserId(context); if(!userId.isEmpty()) { HashMap<String, Object> reqJson = new HashMap<>(); reqJson.put("deviceToken", token); ApiInterface apiService = ApiClient.getClient().create(ApiInterface.class); Call<JsonElement> call = apiService.updateDeviceToken(reqJson,Common.getUserId(context),Common.getAccessToken(context)); call.enqueue(new Callback<JsonElement>() { @Override public void onResponse(Call<JsonElement> call, Response<JsonElement> serverResponse) { try { JsonElement jsonElement = serverResponse.body(); JSONObject response = new JSONObject(jsonElement.toString()); if(context == null ){ return; } if(response.getString(Constants.statusCode).equalsIgnoreCase(Constants.responseStatusSuccess)) { Common.saveBooleanPref(context,Constants.isTokenSentToServer,true); } }catch (Exception e){ e.printStackTrace(); } } @Override public void onFailure(Call<JsonElement> call, Throwable throwable) { Log.d("", "RetroFit2.0 :getAppVersion: " + "eroorrrrrrrrrrrr"); Log.e("eroooooooorr", throwable.toString()); } }); }
}
在MyFirebaseInstanceIDService类中
@Override public void onTokenRefresh() { // Get updated InstanceID token. String refreshedToken = FirebaseInstanceId.getInstance().getToken(); Log.d(TAG, "Refreshed token: " + refreshedToken); // If you want to send messages to this application instance or // manage this apps subscriptions on the server side, send the // Instance ID token to your app server. Common.saveBooleanPref(this,Constants.isTokenSentToServer,false); Common.sendRegistrationToServer(this); FirebaseMessaging.getInstance().subscribeToTopic("bloodRequest"); }
他们有非常简单的解决scheme
https://developers.google.com/instance-id/guides/android-implementation#generate_a_token
注意:如果您的应用使用了被deleteInstanceID删除的令牌,则您的应用将需要生成replace令牌。
而不是删除实例ID,只删除令牌:
String authorizedEntity = PROJECT_ID; String scope = "GCM"; InstanceID.getInstance(context).deleteToken(authorizedEntity,scope);