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.