내 앱을 GCM에서 FCM으로 마이그레이션하고 있습니다.
새로운 사용자가 내 앱을 설치하면 onTokenRefresh()
이 자동으로 호출됩니다. 문제는 사용자가 아직 로그인하지 않았다는 것입니다 (사용자 ID 없음).
onTokenRefresh()
사용자가 로그인 한 후 어떻게 트리거 할 수 있습니까?
내 앱을 GCM에서 FCM으로 마이그레이션하고 있습니다.
새로운 사용자가 내 앱을 설치하면 onTokenRefresh()
이 자동으로 호출됩니다. 문제는 사용자가 아직 로그인하지 않았다는 것입니다 (사용자 ID 없음).
onTokenRefresh()
사용자가 로그인 한 후 어떻게 트리거 할 수 있습니까?
답변:
이 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);
}
}
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 변경을 처리하는 데 필요합니다.
시스템은 토큰 업데이트로 애플리케이션 서버에 과부하가 걸리지 않도록 모든 장치에서 새로 고침 이벤트를 조절합니다.
아래 방법으로 시도하십시오 .
메인 스레드 (서비스, AsyncTask 등 )의 어느 곳에서든 FirebaseInstanceID.getToken ()을 호출 하고 반환 된 토큰을 로컬에 저장 한 다음 서버로 보냅니다. 그런 다음을
onTokenRefresh()
호출 할 때마다 FirebaseInstanceID.getToken ()을 다시 호출 하고 새 토큰을 가져 와서 서버로 보냅니다 (아마 서버에서 제거 할 수 있도록 이전 토큰도 포함하여 새 토큰으로 대체). .
onTokenRefresh()
가 호출됩니까?
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");
}
여러분들 아주 간단한 해결책이 있습니다
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);
한 사용자가 앱에서 로그 아웃하고 다른 사용자가 로그인하는 경우 (동일한 앱) 시나리오에서 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
);
이 답변은 인스턴스 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 메소드로 이동하여이 기능을 더욱 향상시킬 수 있습니다.
[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();
}
}
}
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>
내 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