Firebase FCM memaksa onTokenRefresh () dipanggil

111

Saya memigrasi aplikasi saya dari GCM ke FCM.

Saat pengguna baru menginstal aplikasi saya, onTokenRefresh()secara otomatis dipanggil. Masalahnya adalah bahwa pengguna belum masuk (Tidak ada id pengguna).

Bagaimana saya bisa memicu onTokenRefresh()setelah pengguna masuk?

TareK Khoury
sumber
1
Pertanyaan yang sangat mirip telah ditanyakan di tautan berikut. Periksa apakah jawabannya berguna bagi Anda: stackoverflow.com/questions/37517254/…
Diego Giorgini

Jawaban:

172

The onTokenRefresh()Metode ini akan dipanggil setiap kali token baru dihasilkan. Setelah pemasangan aplikasi, itu akan segera dibuat (seperti yang Anda temukan). Itu juga akan dipanggil ketika token telah berubah.

Menurut FirebaseCloudMessagingpanduannya:

Anda dapat menargetkan pemberitahuan ke satu perangkat tertentu. Pada permulaan awal aplikasi Anda, FCM SDK menghasilkan token pendaftaran untuk instance aplikasi klien.

Screenshot

Tautan Sumber: https://firebase.google.com/docs/notifications/android/console-device#access_the_registration_token

Artinya, pendaftaran token berlaku untuk setiap aplikasi. Sepertinya Anda ingin menggunakan token setelah pengguna masuk. Yang saya sarankan adalah Anda menyimpan token dalam onTokenRefresh()metode ke penyimpanan internal atau preferensi bersama. Kemudian, ambil token dari penyimpanan setelah pengguna masuk dan daftarkan token dengan server Anda sesuai kebutuhan.

Jika Anda ingin memaksa secara manual onTokenRefresh(), Anda dapat membuat IntentService dan menghapus instance token. Kemudian, saat Anda memanggil getToken, onTokenRefresh()metode ini akan dipanggil lagi.

Kode Contoh:

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

EDIT

FirebaseInstanceIdService

public class FirebaseInstanceIdService memperluas Layanan

Kelas ini tidak digunakan lagi. Untuk mengganti onNewToken di FirebaseMessagingService. Setelah diterapkan, layanan ini dapat dihapus dengan aman.

onTokenRefresh () tidak digunakan lagi . Gunakan onNewToken()dalamMyFirebaseMessagingService

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);
    }
} 
Pangeran
sumber
27
Bahkan jika onTokenRefresh () dipanggil sebelum pengguna masuk, alih-alih melakukan pekerjaan menyimpannya secara lokal, ketika pengguna masuk, Anda dapat mengambil token menggunakan FirebaseInstanceId.getInstance (). GetToken () dan mengirimkannya ke server untuk pendaftaran. (kecuali Anda ingin menyimpannya secara lokal untuk menghapus token lama dari server Anda)
geekoraul
10
FireBase pintar dan akan memanggil metode onTokenRefresh (), hanya jika belum ada token (ini dihapus atau dipanggil untuk pertama kali) atau sesuatu yang lain terjadi dan token telah diubah. Jika seseorang ingin memanggil onTokenRefresh dapat menghapus token tersebut dan kemudian memanggil FirebaseInstanceId.getInstance (). GetToken (). Operasi FirebaseInstanceId.getInstance (). DeleteInstanceId () harus ada di AsyncTask atau Thread baru, tidak bisa di MainThread !!!
Stoycho Andreev
3
Mengapa tidak memanggil FirebaseInstanceId.getToken saja?
esong
1
baik, bekerja dengan sempurna saat memanggil IntentService, & tidak perlu menyimpan token di prefs, kurasa. Karena nilai tidak berubah hingga FirebaseInstanceId.getInstance (). DeleteInstanceId (); disebut. Agak menyelamatkan hariku. :)
Detoxic-Soul
5
Mengapa menyimpan token ke preferensi bersama - jika Anda dapat memanggil FirebaseInstanceId.getInstance (). GetToken () kapan saja untuk mendapatkan nilainya?
Alexander Farber
18

Coba terapkan FirebaseInstanceIdServiceuntuk mendapatkan token penyegaran.

Akses token pendaftaran:

Anda dapat mengakses nilai token dengan memperluas FirebaseInstanceIdService . Pastikan Anda telah menambahkan layanan ke manifes Anda , lalu panggil getTokendalam konteks onTokenRefresh, dan catat nilainya seperti yang ditunjukkan:

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

Kode Lengkap:

   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.
    }
}

Lihat jawaban saya di sini .

EDIT:

Anda tidak boleh memulai FirebaseInstanceIdService sendiri.

Ini akan Dipanggil ketika sistem menentukan bahwa token perlu di-refresh. Aplikasi harus memanggil getToken () dan mengirim token ke semua server aplikasi.

Ini tidak akan sering dipanggil, ini diperlukan untuk rotasi kunci dan untuk menangani perubahan ID Instance karena:

  • Aplikasi menghapus ID Instance
  • Aplikasi dipulihkan pada Pengguna perangkat baru
  • uninstal / instal ulang aplikasi
  • Pengguna menghapus data aplikasi

Sistem akan membatasi acara penyegaran di semua perangkat untuk menghindari kelebihan beban server aplikasi dengan pembaruan token.

Coba cara di bawah ini :

Anda akan memanggil FirebaseInstanceID.getToken () di mana pun dari utas utama Anda (apakah itu layanan, AsyncTask, dll), simpan token yang dikembalikan secara lokal dan kirimkan ke server Anda. Kemudian setiap kali onTokenRefresh()dipanggil, Anda akan memanggil FirebaseInstanceID.getToken () lagi, mendapatkan token baru, dan mengirimkannya ke server (mungkin termasuk token lama juga sehingga server Anda dapat menghapusnya, menggantinya dengan yang baru) .

pRaNaY
sumber
2
Saya memang mengimplementasikan FirebaseInstanceIdService, masalahnya adalah onTokenRefresh () dipanggil segera setelah pengguna menginstal aplikasi. saya memerlukannya untuk dipanggil setelah melakukan Login / Daftar
TareK Khoury
1
Jadi, menghapus FirebaseInstanceId akan menyegarkan token, terima kasih!
Louis CAD
setelah GCM ke FCM, FirebaseInstanceId.getInstance (). getToken (); selalu mengembalikan nol. Ada solusi?
Govinda Paliwal
@TareKhoury Anda dapat memanggil metode ini jika diperlukan untuk mendapatkan token. FirebaseInstanceId.getInstance (). GetToken ();
sssvrock
@pRaNaY jika pembaruan aplikasi klien akan onTokenRefresh()dipanggil?
Piyush Kukadiya
2

Saya mempertahankan satu bendera di pref bersama yang menunjukkan apakah token gcm dikirim ke server atau tidak. Di layar Splash setiap kali saya memanggil salah satu metode sendDevicetokenToServer. Metode ini memeriksa apakah id pengguna tidak kosong dan status pengiriman gcm kemudian mengirim token ke server.

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

}

}

Di kelas 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");
}
Prashanth Debbadwar
sumber
2

Guys punya solusi yang sangat sederhana

https://developers.google.com/instance-id/guides/android-implementation#generate_a_token

Catatan: Jika aplikasi Anda menggunakan token yang dihapus oleh deleteInstanceID, aplikasi Anda perlu membuat token pengganti.

Sebagai ganti menghapus ID instance, hapus hanya token:

String authorizedEntity = PROJECT_ID;
String scope = "GCM";
InstanceID.getInstance(context).deleteToken(authorizedEntity,scope);
Manav Patadia
sumber
2
Tidak berhasil untuk saya. Setelah memanggil deleteToken (), getToken () mengembalikan token yang sama seperti sebelumnya dan onTokenRefresh tidak dipanggil.
Lera
1

Ini ada di RxJava2 dalam skenario ketika satu pengguna keluar dari aplikasi Anda dan pengguna lain masuk (Aplikasi yang Sama) Untuk mendaftar dan memanggil masuk (Jika perangkat pengguna tidak memiliki koneksi internet lebih awal pada saat aktivitas dimulai dan kami perlu mengirim token masuk api masuk)

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

Gabung

@FormUrlEncoded
@POST(Site.LOGIN)
Single<ResponseSimple> login(@Field("username") String username,
                         @Field("password") String pass,
                         @Field("token") String token

);
Aklesh Singh
sumber
0

Jawaban ini tidak merusak id instance, melainkan bisa mendapatkan id saat ini. Itu juga menyimpan yang baru di preferensi Bersama.

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 (semua kelas di mana Anda ingin mengatur / mendapatkan preferensi)

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

}

Layanan Android 2.0 dan yang lebih baru onCreatetidak dipanggil saat dimulai secara otomatis ( sumber ). Sebaliknya onStartCommanddiganti dan digunakan. Namun dalam FirebaseInstanceIdService yang sebenarnya, ini dideklarasikan sebagai final dan tidak dapat diganti. Namun, ketika kita memulai layanan menggunakan startService (), jika layanan sudah berjalan, instance aslinya digunakan (yang bagus). OnCreate () (didefinisikan di atas) kita juga dipanggil !.

Gunakan ini di awal MainActivity atau di titik mana pun yang menurut Anda perlu id instance.

MyFirebaseInstanceIdService myFirebaseInstanceIdService = new MyFirebaseInstanceIdService();
Intent intent= new Intent(getApplicationContext(),myFirebaseInstanceIdService.getClass());
//Log.d(this.getClass().getSimpleName(),"Starting MyFirebaseInstanceIdService");
startService(intent); //invoke onCreate

Dan akhirnya,

Utility.getFirebaseInstanceId(getApplicationContext())

Catatan , Anda selanjutnya dapat menyempurnakan ini dengan mencoba memindahkan kode startservice () ke metode getFirebaseInstanceId.

Varun Garg
sumber
Jika Anda mengatur ulang aplikasi / menjalankan pertama kali, perlu beberapa saat untuk menyegarkan token. Jadi Anda akan mendapatkan string "default" selama satu atau dua menit.
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();
        }
    }
}
Sayed Azharuddin
sumber
0

FirebaseInstanceIdService

Kelas ini tidak digunakan lagi. Untuk mengganti onNewToken di FirebaseMessagingService. Setelah diterapkan, layanan ini dapat dihapus dengan aman.

Cara baru untuk melakukan ini adalah dengan mengganti onNewTokenmetode dariFirebaseMessagingService

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

Juga jangan lupa untuk menambahkan layanan di Manifest.xml

<service
    android:name=".MyFirebaseMessagingService"
    android:stopWithTask="false">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>
Ashwin Valento
sumber
0

Bagaimana saya memperbarui deviceToken saya

Pertama ketika saya masuk, saya mengirim token perangkat pertama di bawah koleksi pengguna dan pengguna yang masuk saat ini.

Setelah itu, saya hanya mengganti onNewToken(token:String)di my FirebaseMessagingService()dan hanya memperbarui nilai itu jika token baru dibuat untuk pengguna itu

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

Setiap kali aplikasi Anda membukanya, aplikasi akan memeriksa token baru, jika pengguna belum masuk, ia tidak akan memperbarui token, jika pengguna sudah masuk, Anda dapat memeriksa newToken

Gastón Saillén
sumber