Firebase FCM은 onTokenRefresh ()를 강제로 호출합니다.


111

내 앱을 GCM에서 FCM으로 마이그레이션하고 있습니다.

새로운 사용자가 내 앱을 설치하면 onTokenRefresh()이 자동으로 호출됩니다. 문제는 사용자가 아직 로그인하지 않았다는 것입니다 (사용자 ID 없음).

onTokenRefresh()사용자가 로그인 한 후 어떻게 트리거 할 수 있습니까?


1
매우 유사한 질문이 이미 다음 링크에서 요청되었습니다. 답변이 유용한 지 확인하십시오 : stackoverflow.com/questions/37517254/…
Diego 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는 서비스를 확장합니다.

이 클래스는 더 이상 사용되지 않습니다. 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을 호출하지 않는 이유는 무엇입니까?
esong

1
글쎄, IntentService에서 호출 할 때 완벽하게 작동했으며 토큰을 prefs에 저장할 필요가 없습니다. 값은 FirebaseInstanceId.getInstance (). deleteInstanceId ();까지 변경되지 않으므로 호출됩니다. 제 하루를 구했습니다. :)
Detoxic-Soul

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

1
따라서 FirebaseInstanceId를 삭제하면 토큰이 새로 고쳐집니다. 감사합니다!
Louis CAD

GCM에서 FCM으로, FirebaseInstanceId.getInstance (). getToken (); 항상 null을 반환합니다. 어떤 해결책이 있습니까?
Govinda Paliwal

@TareKhoury 토큰을 얻기 위해 필요한 곳에서이 메서드를 호출 할 수 있습니다. FirebaseInstanceId.getInstance (). getToken ();
sssvrock

클라이언트 앱 업데이트의 경우 @pRaNaY onTokenRefresh()가 호출됩니까?
Piyush Kukadiya

2

gcm 토큰이 서버로 전송되었는지 여부를 나타내는 하나의 플래그를 공유 환경에 유지하고 있습니다. 스플래시 화면에서 하나의 메서드 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가 호출되지 않았습니다.
Lera

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서비스는 자동으로 시작될 때 호출되지 않습니다 ( source ). 대신 onStartCommand재정의되고 사용됩니다. 그러나 실제 FirebaseInstanceIdService에서는 최종적으로 선언되며 재정의 할 수 없습니다. 그러나 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 메소드로 이동하여이 기능을 더욱 향상시킬 수 있습니다.


앱 / 실행을 처음 재설정하는 경우 토큰을 새로 고치는 데 약간의 시간이 걸립니다. 따라서 1 ~ 2 분 동안 문자열 "default"를 얻게됩니다.
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.