Firebase FCMはonTokenRefresh()を強制的に呼び出します


111

アプリをGCMからFCMに移行しています。

新しいユーザーが私のアプリをインストールすると、onTokenRefresh()が自動的に呼び出されます。問題は、ユーザーがまだログインしていないことです(ユーザーIDなし)。

onTokenRefresh()ユーザーのログイン後にをトリガーするにはどうすればよいですか?


1
非常によく似た質問が次のリンクですでに尋ねられています。答えはあなたの役に立つかどうかを確認します。stackoverflow.com/questions/37517254/...
サンディエゴGiorgini

回答:


172

この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);
    }
}

編集

FirebaseInstanceIdService

パブリッククラスFirebaseInstanceIdServiceはServiceを拡張します

このクラスは非推奨です。FirebaseMessagingServiceのonNewTokenのオーバーライドを支持します。それが実装されたら、このサービスは安全に削除できます。

onTokenRefresh()は非推奨です。使用onNewToken()MyFirebaseMessagingService

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);
    }
} 

27
ユーザーがログインする前にonTokenRefresh()が呼び出されても、ローカルに保存する代わりに、ユーザーがログインすると、FirebaseInstanceId.getInstance()。getToken()を使用してトークンを取得し、サーバーに送信できます登録のため。(サーバーから古いトークンを削除するためにローカルに保存する場合を除きます)
geekoraul

10
FireBaseは巧妙で、トークンがない(削除された、または初めて呼び出された)か、何か他のことが起こり、トークンが変更された場合にのみ、onTokenRefresh()メソッドを呼び出します。誰かがonTokenRefreshを呼び出したい場合は、トークンを削除してから、FirebaseInstanceId.getInstance()。getToken()を呼び出すことができます。操作FirebaseInstanceId.getInstance()。deleteInstanceId()は、AsyncTaskまたは新しいスレッドに存在する必要があります。MainThreadには存在できません!!!
Stoycho Andreev

3
なぜFirebaseInstanceId.getTokenを呼び出さないのですか?
2016年

1
まあ、IntentServiceで呼び出すときに完全に機能し、トークンを設定に保存する必要はないと思います。値はFirebaseInstanceId.getInstance()。deleteInstanceId();まで変更されないため。と呼ばれます。ちょっと私を救った。:)
Detoxic-Soul 2017

5
トークンを共有設定に保存する理由- いつでもFirebaseInstanceId.getInstance()。getToken()を呼び出してその値を取得できる場合は?
Alexander Farber

18

実装FirebaseInstanceIdServiceしてリフレッシュトークンを取得してください。

登録トークンにアクセスします。

FirebaseInstanceIdServiceを拡張することで、トークンの値にアクセスできます。サービスがマニフェストに追加されていることを確認してから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.
    }
}

ここで私の答えを参照してください

編集:

自分でFirebaseInstanceIdServiceを開始しないでください。

トークンを更新する必要があるとシステムが判断したときに呼び出されます。アプリケーションはgetToken()を呼び出して、すべてのアプリケーションサーバーにトークンを送信する必要があります。

これはあまり頻繁には呼び出されません。キーのローテーションとインスタンスIDの変更を処理するために必要です。

  • アプリがインスタンスIDを削除する
  • アプリは新しいデバイスユーザーに復元されます
  • アプリをアンインストール/再インストールします
  • ユーザーがアプリのデータを消去する

システムは、すべてのデバイスで更新イベントを抑制して、トークンの更新によるアプリケーションサーバーの過負荷を回避します。

以下の方法を試してください

メインスレッド外の任意の場所(サービス、AsyncTaskなど)でFirebaseInstanceID.getToken()を呼び出し、返されたトークンをローカルに保存してサーバーに送信します。次にonTokenRefresh()、が呼び出されるたびに 、FirebaseInstanceID.getToken()を再度呼び出し 、新しいトークンを取得してサーバーに送信します(おそらく古いトークンも含まれているため、サーバーがトークンを削除して、新しいトークンに置き換えることができます)。 。


2
FirebaseInstanceIdServiceを実装しました。問題は、ユーザーがアプリをインストールした直後にonTokenRefresh()が呼び出されることです。ログイン/サインアップの実行後に呼び出す必要があります
TareK Khoury '29

1
したがって、FirebaseInstanceIdを削除すると、トークンが更新されます。
Louis CAD

GCMからFCMへ、FirebaseInstanceId.getInstance()。getToken(); 常にnullを返します。解決策はありますか?
Govinda Paliwal 2017年

@TareKhouryこのメソッドは、トークンを取得する必要がある場合に呼び出すことができます。FirebaseInstanceId.getInstance()。getToken();
sssvrock 2017

クライアントアプリの更新の場合は@pRaNaY onTokenRefresh()が呼び出されますか?
Piyush Kukadiya

2

gcmトークンがサーバーに送信されたかどうかを示す1つのフラグを共有設定に保持しています。スプラッシュ画面で、1つのメソッドsendDevicetokenToServerを呼び出すたびに。このメソッドは、ユーザーIDが空でないかどうかをチェックし、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");
}

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);

2
私のために働いていませんでした。deleteToken()を呼び出した後、getToken()は以前と同じトークンを返し、onTokenRefreshは呼び出されませんでした。
レラ

1

これは、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

);

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);
     ....

}

Android 2.0以降onCreateのサービスは、自動的に開始されたときに起動されません(ソース)。代わり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メソッドに移動することで、これをさらに強化できます。


app / runを初めてリセットした場合、トークンが更新されるまでに時間がかかります。したがって、1〜2分で「デフォルト」の文字列が取得されます。
Varun Garg

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();
        }
    }
}

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>

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.