Firebase FCM บังคับให้ onTokenRefresh () ถูกเรียก


111

ฉันกำลังย้ายแอปของฉันจาก GCM ไปยัง FCM

เมื่อผู้ใช้ใหม่ติดตั้งแอปของฉันonTokenRefresh()ระบบจะเรียกแอปโดยอัตโนมัติ ปัญหาคือผู้ใช้ยังไม่ได้เข้าสู่ระบบ (ไม่มีรหัสผู้ใช้)

ฉันจะทริกเกอร์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 ขยายบริการ

คลาสนี้เลิกใช้แล้ว ในการลบล้าง onNewToken ใน FirebaseMessagingService เมื่อดำเนินการแล้วบริการนี้สามารถลบออกได้อย่างปลอดภัย

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 () Operation FirebaseInstanceId.getInstance (). deleteInstanceId () ต้องอยู่ใน AsyncTask หรือ Thread ใหม่ไม่สามารถอยู่บน MainThread ได้ !!!
Stoycho Andreev

3
ทำไมไม่โทรหา FirebaseInstanceId.getToken
esong

1
ทำงานได้ดีเมื่อโทรใน IntentService และไม่จำเป็นต้องบันทึกโทเค็นในคำนำหน้าฉันเดา เนื่องจากค่าไม่เปลี่ยนแปลงจนกว่า FirebaseInstanceId.getInstance (). deleteInstanceId (); ถูกเรียก. Kinda บันทึกวันของฉัน :)
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 () และส่งโทเค็นไปยังเซิร์ฟเวอร์แอปพลิเคชันทั้งหมด

สิ่งนี้จะไม่ถูกเรียกบ่อยนัก แต่จำเป็นสำหรับการหมุนคีย์และเพื่อจัดการกับการเปลี่ยนแปลงรหัสอินสแตนซ์เนื่องจาก:

  • แอปลบรหัสอินสแตนซ์
  • แอปได้รับการกู้คืนในผู้ใช้อุปกรณ์ใหม่
  • ถอนการติดตั้ง / ติดตั้งแอพใหม่
  • ผู้ใช้ล้างข้อมูลแอพ

ระบบจะเค้นเหตุการณ์การรีเฟรชในอุปกรณ์ทั้งหมดเพื่อหลีกเลี่ยงการโหลดแอปพลิเคชันเซิร์ฟเวอร์มากเกินไปด้วยการอัปเดตโทเค็น

ลองวิธีด้านล่าง :

คุณจะเรียกFirebaseInstanceID.getToken ()ที่ใดก็ได้นอกเธรดหลักของคุณ (ไม่ว่าจะเป็นบริการ AsyncTask ฯลฯ ) จัดเก็บโทเค็นที่ส่งคืนไว้ในเครื่องและส่งไปยังเซิร์ฟเวอร์ของคุณ จากนั้นเมื่อใดก็ตามที่ onTokenRefresh()ถูกเรียกคุณจะเรียก FirebaseInstanceID.getToken ()อีกครั้งรับโทเค็นใหม่และส่งไปยังเซิร์ฟเวอร์ (อาจรวมถึงโทเค็นเก่าด้วยเพื่อให้เซิร์ฟเวอร์ของคุณสามารถลบออกแทนที่ด้วยโทเค็นใหม่) .


2
ฉันใช้ FirebaseInstanceIdService ปัญหาคือ onTokenRefresh () ถูกเรียกเกือบจะทันทีหลังจากที่ผู้ใช้ติดตั้งแอป ฉันต้องการให้เรียกหลังจากดำเนินการเข้าสู่ระบบ / สมัคร
TareK Khoury

1
ดังนั้นการลบ FirebaseInstanceId จะรีเฟรชโทเค็นขอบคุณ!
Louis CAD

หลังจาก GCM ถึง FCM, FirebaseInstanceId.getInstance (). getToken (); คืนค่าว่างเสมอ วิธีแก้ปัญหาใด ๆ
Govinda Paliwal

@TareKhoury คุณสามารถเรียกเมธอดนี้ได้ทุกที่ที่ต้องใช้เพื่อรับโทเค็น FirebaseInstanceId.getInstance (). getToken ();
sssvrock

@pRaNaY เผื่อลูกค้าจะอัพเดทแอพจะonTokenRefresh()โดนเรียก?
Piyush Kukadiya

2

ฉันกำลังรักษาแฟล็กเดียวในการแชร์ pref ซึ่งระบุว่าโทเค็น gcm ถูกส่งไปยังเซิร์ฟเวอร์หรือไม่ ในหน้าจอ Splash ทุกครั้งที่ฉันโทรหาวิธีการหนึ่ง 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 แอปของคุณจะต้องสร้างโทเค็นทดแทน

แทนการลบรหัสอินสแตนซ์ให้ลบโทเค็นเท่านั้น:

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

คำตอบนี้ไม่ได้ทำลายรหัสอินสแตนซ์ แต่สามารถรับรหัสปัจจุบันได้ นอกจากนี้ยังจัดเก็บรายการที่รีเฟรชไว้ในการตั้งค่าการแชร์

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

}

ระบบonCreateไม่เรียกใช้Android 2.0 ขึ้นไปเมื่อเริ่มต้นโดยอัตโนมัติ (ที่มา ) แต่onStartCommandถูกแทนที่และใช้ แต่ใน FirebaseInstanceIdService จริงจะประกาศเป็นขั้นสุดท้ายและไม่สามารถแทนที่ได้ อย่างไรก็ตามเมื่อเราเริ่มให้บริการโดยใช้ startService () หากบริการกำลังทำงานอยู่ระบบจะใช้อินสแตนซ์เดิม (ซึ่งเป็นสิ่งที่ดี) onCreate () (กำหนดไว้ด้านบน) ของเราก็ถูกเรียกด้วยเช่นกัน!.

ใช้สิ่งนี้ในการเริ่มต้น MainActivity หรือจุดใดก็ตามที่คุณคิดว่าคุณต้องการรหัสอินสแตนซ์

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

โปรดทราบว่าคุณสามารถปรับปรุงสิ่งนี้ได้มากขึ้นโดยพยายามย้ายรหัส startervice () ไปยังเมธอด getFirebaseInstanceId


หากคุณรีเซ็ตแอป / เรียกใช้ครั้งแรกอาจต้องใช้เวลาสักพักในการรีเฟรชโทเค็น ดังนั้นคุณจะได้รับสตริง "ค่าเริ่มต้น" สำหรับนาทีหรือสองนาที
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

คลาสนี้เลิกใช้แล้ว ในการลบล้าง onNewToken ใน FirebaseMessagingService เมื่อดำเนินการแล้วบริการนี้สามารถลบออกได้อย่างปลอดภัย

วิธีใหม่ในการทำเช่นนี้คือการลบล้าง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

ฉันจะอัปเดตอุปกรณ์ของฉันได้อย่างไร Token

อันดับแรกเมื่อฉันเข้าสู่ระบบฉันจะส่งโทเค็นอุปกรณ์แรกภายใต้คอลเล็กชันผู้ใช้และผู้ใช้ที่ล็อกอินปัจจุบัน

หลังจากนั้นฉันเพียงแค่แทนที่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.