Firebase FCM buộc onTokenRefresh () được gọi


111

Tôi đang di chuyển ứng dụng của mình từ GCM sang FCM.

Khi người dùng mới cài đặt ứng dụng của tôi, ứng dụng onTokenRefresh()sẽ tự động được gọi. Vấn đề là người dùng chưa đăng nhập (Không có id người dùng).

Làm cách nào để kích hoạt onTokenRefresh()sau khi người dùng đã đăng nhập?


1
Một câu hỏi tương tự đã được hỏi trong liên kết sau. Kiểm tra xem câu trả lời có hữu ích cho bạn không: stackoverflow.com/questions/37517254/…
Diego Giorgini,

Câu trả lời:


172

Các onTokenRefresh()phương pháp sẽ được gọi bất cứ khi nào một thẻ mới được tạo ra. Sau khi cài đặt ứng dụng, nó sẽ được tạo ngay lập tức (như bạn đã từng thấy). Nó cũng sẽ được gọi khi mã thông báo đã thay đổi.

Theo FirebaseCloudMessaginghướng dẫn:

Bạn có thể nhắm mục tiêu thông báo đến một thiết bị cụ thể. Khi khởi động lần đầu ứng dụng của bạn, FCM SDK tạo mã thông báo đăng ký cho phiên bản ứng dụng khách.

Ảnh chụp màn hình

Liên kết nguồn: https://firebase.google.com/docs/notifications/android/console-device#access_the_registration_token

Điều này có nghĩa là đăng ký mã thông báo là cho mỗi ứng dụng. Có vẻ như bạn muốn sử dụng mã thông báo sau khi người dùng đã đăng nhập. Điều tôi khuyên bạn nên lưu mã thông báo trong onTokenRefresh()phương thức vào bộ nhớ trong hoặc tùy chọn chia sẻ. Sau đó, lấy mã thông báo từ bộ nhớ sau khi người dùng đăng nhập và đăng ký mã thông báo với máy chủ của bạn nếu cần.

Nếu bạn muốn buộc thủ công onTokenRefresh(), bạn có thể tạo IntentService và xóa phiên bản mã thông báo. Sau đó, khi bạn gọi getToken, onTokenRefresh()phương thức sẽ được gọi lại.

Mã ví dụ:

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

BIÊN TẬP

FirebaseInstanceIdService

lớp công khai FirebaseInstanceIdService mở rộng Dịch vụ

Lớp này không được dùng nữa. Có lợi cho việc ghi đè onNewToken trong FirebaseMessagingService. Khi điều đó đã được triển khai, dịch vụ này có thể được xóa một cách an toàn.

onTokenRefresh () không được dùng nữa . Sử dụng onNewToken()trongMyFirebaseMessagingService

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
Ngay cả khi onTokenRefresh () được gọi trước khi người dùng đăng nhập, thay vì thực hiện công việc lưu trữ cục bộ, khi người dùng đăng nhập, bạn có thể truy xuất mã thông báo bằng cách sử dụng FirebaseInstanceId.getInstance (). GetToken () và gửi nó đến máy chủ để đăng ký. (trừ khi bạn muốn lưu nó tại địa phương để xóa một thẻ cũ từ máy chủ của bạn)
geekoraul

10
FireBase rất thông minh và nó sẽ gọi phương thức onTokenRefresh (), chỉ khi chưa có mã thông báo (nó bị xóa hoặc nó được gọi lần đầu tiên) hoặc có điều gì khác xảy ra và mã thông báo đã được thay đổi. Nếu ai đó muốn gọi onTokenRefresh có thể xóa mã thông báo và sau đó gọi FirebaseInstanceId.getInstance (). GetToken (). Hoạt động FirebaseInstanceId.getInstance (). DeleteInstanceId () cần phải ở trong AsyncTask hoặc Thread mới, nó không thể có trên MainThread !!!
Stoycho Andreev

3
Tại sao không chỉ gọi FirebaseInstanceId.getToken?
esong

1
tốt, hoạt động hoàn hảo khi gọi trong IntentService và không cần lưu mã thông báo trong prefs, tôi đoán vậy. Vì giá trị không thay đổi cho đến khi FirebaseInstanceId.getInstance (). DeleteInstanceId (); được gọi là. Kinda đã cứu một ngày của tôi. :)
Detoxic-Soul

5
Tại sao phải lưu mã thông báo vào các tùy chọn được chia sẻ - nếu bạn có thể gọi FirebaseInstanceId.getInstance (). GetToken () bất kỳ lúc nào để nhận giá trị của nó?
Alexander Farber,

18

Cố gắng triển khai FirebaseInstanceIdServiceđể nhận mã thông báo làm mới.

Truy cập mã thông báo đăng ký:

Bạn có thể truy cập giá trị của mã thông báo bằng cách mở rộng FirebaseInstanceIdService . Đảm bảo rằng bạn đã thêm dịch vụ vào tệp kê khai của mình , sau đó gọi getTokentrong ngữ cảnh onTokenRefreshvà ghi lại giá trị như được hiển thị:

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

Mã đầy đủ:

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

Xem câu trả lời của tôi tại đây .

CHỈNH SỬA:

Bạn không nên tự mình bắt đầu một FirebaseInstanceIdService .

Nó sẽ được gọi khi hệ thống xác định rằng các mã thông báo cần được làm mới. Ứng dụng sẽ gọi getToken () và gửi mã thông báo đến tất cả các máy chủ ứng dụng.

Điều này sẽ không được gọi thường xuyên, nó cần thiết để xoay khóa và xử lý các thay đổi ID phiên bản do:

  • Ứng dụng xóa ID phiên bản
  • Ứng dụng được khôi phục trên thiết bị mới Người dùng
  • gỡ cài đặt / cài đặt lại ứng dụng
  • Người dùng xóa dữ liệu ứng dụng

Hệ thống sẽ điều chỉnh sự kiện làm mới trên tất cả các thiết bị để tránh làm quá tải các máy chủ ứng dụng với các bản cập nhật mã thông báo.

Hãy thử cách dưới đây :

bạn sẽ gọi FirebaseInstanceID.getToken () ở bất kỳ đâu ngoài chuỗi chính của bạn (cho dù đó là dịch vụ, AsyncTask, v.v.), lưu trữ cục bộ mã thông báo trả về và gửi nó đến máy chủ của bạn. Sau đó, bất cứ khi nào onTokenRefresh()được gọi, bạn sẽ gọi lại FirebaseInstanceID.getToken () , lấy mã thông báo mới và gửi mã đó đến máy chủ (có thể bao gồm cả mã thông báo cũ để máy chủ của bạn có thể xóa nó, thay thế bằng mã mới) .


2
Tôi đã triển khai FirebaseInstanceIdService, vấn đề là onTokenRefresh () đang được gọi gần như ngay lập tức sau khi người dùng cài đặt ứng dụng. tôi cần nó được gọi sau khi thực hiện Đăng nhập / Đăng ký
TareK Khoury

1
Vì vậy, việc xóa FirebaseInstanceId sẽ làm mới mã thông báo, cảm ơn!
Louis CAD

sau GCM thành FCM, FirebaseInstanceId.getInstance (). getToken (); luôn trả về null. Bất kì giải pháp nào?
Govinda Paliwal

@TareKhoury Bạn có thể gọi phương thức này khi cần thiết để nhận mã thông báo. FirebaseInstanceId.getInstance (). GetToken ();
sssvrock

@pRaNaY trong trường hợp cập nhật ứng dụng khách sẽ onTokenRefresh()được gọi?
Piyush Kukadiya

2

Tôi đang duy trì một cờ trong pref được chia sẻ cho biết liệu mã thông báo gcm có được gửi đến máy chủ hay không. Trong màn hình Splash mỗi khi tôi gọi một phương thức sendDevicetokenToServer. Phương pháp này kiểm tra nếu id người dùng không trống và trạng thái gửi gcm sau đó gửi mã thông báo đến máy chủ.

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

}

}

Trong lớp 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

Guys nó có giải pháp rất đơn giản

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

Lưu ý: Nếu ứng dụng của bạn sử dụng mã thông báo đã bị xóa bởi deleteInstanceID, ứng dụng của bạn sẽ cần tạo mã thông báo thay thế.

Thay vì xóa Id phiên bản, chỉ xóa mã thông báo:

String authorizedEntity = PROJECT_ID;
String scope = "GCM";
InstanceID.getInstance(context).deleteToken(authorizedEntity,scope);

2
Không làm việc cho tôi. Sau khi gọi deleteToken (), getToken () trả về cùng một mã thông báo như trước và onTokenRefresh không được gọi.
Lera

1

Điều này xảy ra trong RxJava2 trong trường hợp khi một người dùng đăng xuất khỏi ứng dụng của bạn và những người dùng khác đăng nhập (Cùng một ứng dụng) Để tạo lại và đăng nhập cuộc gọi (Nếu thiết bị của người dùng không có kết nối internet trước đó tại thời điểm hoạt động bắt đầu và chúng tôi cần gửi mã thông báo vào đăng nhập 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()));

Đăng nhập

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

);

0

Câu trả lời này không phá hủy id cá thể, thay vào đó nó có thể lấy id hiện tại. Nó cũng lưu trữ một cái được làm mới trong Tùy chọn được chia sẻ.

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 (bất kỳ lớp nào bạn muốn đặt / nhận tùy chọn)

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 (mở rộng 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);
     ....

}

Dịch vụ Android 2.0 trở lên onCreatekhông được gọi khi khởi động tự động ( nguồn ). Thay vào đó onStartCommandlà ghi đè và sử dụng. Nhưng trong FirebaseInstanceIdService thực tế, nó được khai báo là cuối cùng và không thể bị ghi đè. Tuy nhiên, khi chúng tôi bắt đầu dịch vụ bằng startService (), nếu dịch vụ đã chạy, thì phiên bản gốc của nó sẽ được sử dụng (tốt). OnCreate () (được định nghĩa ở trên) của chúng ta cũng đã được gọi !.

Sử dụng điều này khi bắt đầu MainActivity hoặc bất cứ lúc nào bạn nghĩ rằng bạn cần id cá thể.

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

Và cuối cùng,

Utility.getFirebaseInstanceId(getApplicationContext())

Lưu ý , bạn có thể nâng cao điều này hơn nữa bằng cách cố gắng di chuyển mã startervice () sang phương thức getFirebaseInstanceId.


Nếu bạn đặt lại ứng dụng / chạy lần đầu tiên, phải mất một thời gian để ứng dụng làm mới mã thông báo. Vì vậy, bạn sẽ nhận được chuỗi "mặc định" trong một hoặc hai phút.
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

Lớp này không được dùng nữa. Có lợi cho việc ghi đè onNewToken trong FirebaseMessagingService. Khi điều đó đã được triển khai, dịch vụ này có thể được xóa một cách an toàn.

Cách mới để thực hiện điều này là ghi đè onNewTokenphương thức từ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);
    }
} 

Cũng đừng quên thêm dịch vụ vào Manifest.xml

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

0

Cách tôi cập nhật thiết bị của mình

Đầu tiên khi tôi đăng nhập, tôi gửi mã thông báo thiết bị đầu tiên trong bộ sưu tập người dùng và người dùng đã đăng nhập hiện tại.

Sau đó, tôi chỉ ghi đè lên giá trị onNewToken(token:String)của mình FirebaseMessagingService()và chỉ cập nhật giá trị đó nếu mã thông báo mới được tạo cho người dùng đó

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

Mỗi khi ứng dụng của bạn mở, nó sẽ kiểm tra mã thông báo mới, nếu người dùng chưa đăng nhập, nó sẽ không cập nhật mã thông báo, nếu người dùng đã đăng nhập, bạn có thể kiểm tra newToken

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.