我正在将我的应用从GCM迁移到FCM。
当新用户安装我的应用时,会自动调用onTokenRefresh()
。问题是用户尚未登录(无用户ID)。
如何在用户登录后触发onTokenRefresh()
?
答案 0 :(得分:162)
每当生成新令牌时,都会调用onTokenRefresh()
方法。安装应用程序后,它将立即生成(正如您所发现的那样)。当令牌发生变化时也会调用它。
根据FirebaseCloudMessaging
指南:
您可以将通知定位到单个特定设备。在最初 启动您的应用程序后,FCM SDK会为其生成注册令牌 客户端应用实例。
来源链接: https://firebase.google.com/docs/notifications/android/console-device#access_the_registration_token
这意味着令牌注册是针对每个应用。听起来您希望在用户登录后使用令牌。我建议您将onTokenRefresh()
方法中的令牌保存到内部存储或共享首选项。然后,在用户登录后从存储中检索令牌,并根据需要向您的服务器注册令牌。
如果您想手动强制onTokenRefresh()
,可以创建一个IntentService并删除令牌实例。然后,当您调用getToken时,将再次调用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);
}
}
答案 1 :(得分:16)
尝试实施FirebaseInstanceIdService
以获取刷新令牌。
访问注册令牌:
您可以通过扩展FirebaseInstanceIdService来访问令牌的值。确保您已将服务添加到manifest,然后在getToken
的上下文中调用onTokenRefresh
,并按如下所示记录值:
@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.
}
}
请参阅我的回答here。
<强>编辑:强>
您不应该自己创建 FirebaseInstanceIdService 。
当系统确定令牌需要时,它将被调用 刷新。应用程序应调用getToken()并发送令牌 到所有应用程序服务器。
这不会经常调用,需要进行密钥轮换并处理由于以下原因引起的实例ID更改:
系统将限制所有设备上的刷新事件,以避免使用令牌更新使应用程序服务器过载。
尝试以下方式:
您可以在主电台的任何地方拨打 FirebaseInstanceID.getToken() 线程(无论是服务,AsyncTask等),存储返回的 本地令牌并将其发送到您的服务器。然后
onTokenRefresh()
被叫,你打电话 再次 FirebaseInstanceID.getToken(),获取一个新令牌,并将其发送到服务器(可能还包括旧令牌) 您的服务器可以将其删除,将其替换为新服务器。)
答案 2 :(得分:2)
他们有非常简单的解决方案
https://developers.google.com/instance-id/guides/android-implementation#generate_a_token
注意:如果您的应用使用了由deleteInstanceID删除的令牌,则您的应用需要生成替换令牌。
不删除实例ID,而是仅删除令牌:
String authorizedEntity = PROJECT_ID;
String scope = "GCM";
InstanceID.getInstance(context).deleteToken(authorizedEntity,scope);
答案 3 :(得分:2)
对于那些正在寻找一种方法来强制刷新您的令牌并以这种方式调用 onNewToken 因为生成了新令牌的人,您只需要在需要时调用它:
FirebaseMessaging.getInstance().deleteToken().addOnSuccessListener {
FirebaseMessaging.getInstance().token
}
为了简单起见,我将它写为 MyFirebaseMessagingService 中的静态函数,如下所示:
class MyFirebaseMessagingService : FirebaseMessagingService() {
override fun onMessageReceived(remoteMessage: RemoteMessage) {
if (remoteMessage.data.isNotEmpty()) {
Log.d(TAG, "Message data payload: ${remoteMessage.data}")
}
// do your stuff.
}
override fun onNewToken(token: String) {
Log.d(TAG, "FCM token changed: $token")
// send it to your backend.
}
companion object {
private const val TAG = "MyFirebaseMessagingService"
fun refreshFcmToken() {
FirebaseMessaging.getInstance().deleteToken().addOnSuccessListener {
FirebaseMessaging.getInstance().token
}
}
}
}
仅调用 deleteToken() 是不够的,因为只有在请求时才会生成新令牌;当然,每次打开应用程序时都会请求它,因此如果您只调用 deleteToken() 新令牌将在用户下次打开应用程序时生成,但是如果您需要在或期间立即发送通知,这可能会导致问题在他第一次使用该应用之后。
在 deleteToken() 之后立即调用 token() 会导致并发问题,因为它们都是异步操作,并且 token() 将始终在 deleteToken() 之前结束执行(因为它看到令牌已经存在,因为它不是尚未删除,因此甚至不会尝试生成新令牌,而 deleteToken() 正在请求 Firebase 服务器删除当前令牌)。
这就是为什么需要在 deleteToken() 线程成功完成后调用 token() 的原因。
答案 4 :(得分:1)
我在共享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");
}
答案 5 :(得分:1)
在RxJava2中,当一个用户从您的应用程序注销而其他用户登录(同一应用程序)以重新分类并调用登录名时(如果用户的设备在活动开始时较早时没有互联网连接,我们需要在登录api中发送令牌)
Single.fromCallable(() -> FirebaseInstanceId.getInstance().getToken())
.flatMap( token -> Retrofit.login(userName,password,token))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(simple -> {
if(simple.isSuccess){
loginedSuccessfully();
}
}, throwable -> Utils.longToast(context, throwable.getLocalizedMessage()));
登录
@FormUrlEncoded
@POST(Site.LOGIN)
Single<ResponseSimple> login(@Field("username") String username,
@Field("password") String pass,
@Field("token") String token
);
答案 6 :(得分:1)
FirebaseMessaging.getInstance().getToken().addOnSuccessListener(new
OnSuccessListener<String>() {
@Override
public void onSuccess(String newToken) {
....
}
});
答案 7 :(得分:0)
这个答案不会破坏实例ID,而是能够获得当前的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);
....
}
自动启动(source)时,不会调用Android 2.0及以上onCreate
服务。相反,onStartCommand
被覆盖并使用。但在实际的FirebaseInstanceIdService中,它被声明为final,无法覆盖。
但是,当我们使用startService()启动服务时,如果服务已在运行,则其original instance is used(这是好的)。我们的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方法来进一步增强此功能。
答案 8 :(得分:0)
[Service]
[IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
class MyFirebaseIIDService: FirebaseInstanceIdService
{
const string TAG = "MyFirebaseIIDService";
NotificationHub hub;
public override void OnTokenRefresh()
{
var refreshedToken = FirebaseInstanceId.Instance.Token;
Log.Debug(TAG, "FCM token: " + refreshedToken);
SendRegistrationToServer(refreshedToken);
}
void SendRegistrationToServer(string token)
{
// Register with Notification Hubs
hub = new NotificationHub(Constants.NotificationHubName,
Constants.ListenConnectionString, this);
Employee employee = JsonConvert.DeserializeObject<Employee>(Settings.CurrentUser);
//if user is not logged in
if (employee != null)
{
var tags = new List<string>() { employee.Email};
var regID = hub.Register(token, tags.ToArray()).RegistrationId;
Log.Debug(TAG, $"Successful registration of ID {regID}");
}
else
{
FirebaseInstanceId.GetInstance(Firebase.FirebaseApp.Instance).DeleteInstanceId();
hub.Unregister();
}
}
}
答案 9 :(得分:0)
FirebaseInstanceIdService
不推荐使用此类。 支持在FirebaseMessagingService中重写onNewToken。一旦实施,就可以安全地删除此服务。
执行此操作的新方法是覆盖onNewToken
中的FirebaseMessagingService
方法
public class MyFirebaseMessagingService extends FirebaseMessagingService {
@Override
public void onNewToken(String s) {
super.onNewToken(s);
Log.e("NEW_TOKEN",s);
}
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
}
}
也不要忘记在Manifest.xml中添加服务
<service
android:name=".MyFirebaseMessagingService"
android:stopWithTask="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
答案 10 :(得分:0)
我如何更新我的deviceToken
首先,当我登录时,我将在用户集合下发送第一个设备令牌,并发送当前登录的用户。
此后,我只是覆盖了onNewToken(token:String)
中的FirebaseMessagingService()
,并且如果为该用户生成了新令牌,也只是更新该值
class MyFirebaseMessagingService: FirebaseMessagingService() {
override fun onMessageReceived(p0: RemoteMessage) {
super.onMessageReceived(p0)
}
override fun onNewToken(token: String) {
super.onNewToken(token)
val currentUser= FirebaseAuth.getInstance().currentUser?.uid
if(currentUser != null){
FirebaseFirestore.getInstance().collection("user").document(currentUser).update("deviceToken",token)
}
}
}
每次您的应用打开时,它都会检查一个新令牌,如果用户尚未登录,它将不会更新该令牌,如果用户已经登录,则可以检查newToken
>