アプリをGCMからFCMに移行しています。
新しいユーザーが私のアプリをインストールすると、onTokenRefresh()
が自動的に呼び出されます。問題は、ユーザーがまだログインしていないことです(ユーザーIDなし)。
onTokenRefresh()
ユーザーのログイン後にをトリガーするにはどうすればよいですか?
アプリをGCMからFCMに移行しています。
新しいユーザーが私のアプリをインストールすると、onTokenRefresh()
が自動的に呼び出されます。問題は、ユーザーがまだログインしていないことです(ユーザーIDなし)。
onTokenRefresh()
ユーザーのログイン後にをトリガーするにはどうすればよいですか?
回答:
この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);
}
}
実装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の変更を処理するために必要です。
システムは、すべてのデバイスで更新イベントを抑制して、トークンの更新によるアプリケーションサーバーの過負荷を回避します。
以下の方法を試してください:
メインスレッド外の任意の場所(サービス、AsyncTaskなど)でFirebaseInstanceID.getToken()を呼び出し、返されたトークンをローカルに保存してサーバーに送信します。次に
onTokenRefresh()
、が呼び出されるたびに 、FirebaseInstanceID.getToken()を再度呼び出し 、新しいトークンを取得してサーバーに送信します(おそらく古いトークンも含まれているため、サーバーがトークンを削除して、新しいトークンに置き換えることができます)。 。
onTokenRefresh()
が呼び出されますか?
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");
}
みんなそれは非常に簡単な解決策を持っています
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);
これは、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
);
この回答はインスタンス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メソッドに移動することで、これをさらに強化できます。
[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();
}
}
}
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>
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