【问题标题】:Android Firebase Service is not getting SharedPreferences valueAndroid Firebase 服务未获得 SharedPreferences 值
【发布时间】:2019-02-15 08:40:10
【问题描述】:

我在我的片段 (Kotlin) 上设置 sharedPreferences 值,然后我想在我的 FirebaseMessagingService (Java) 上使用这个值。当我设置值并销毁应用程序并再次打开时,我的片段没有任何问题。我可以看到设定值。所以我确信我的片段更新了 sharedPreferences 值。但是当我尝试在 FirebaseMessagingService 上使用该值时,我总是得到默认值。

这是我在 kotlin 类上的设置方式:

sharedPref = activity?.getSharedPreferences("com.xxx.xxx.xxx",Context.MODE_PRIVATE)!!

private fun sharedPrefWrite(boolean: Boolean){
    with (sharedPref?.edit()) {
        this!!.putBoolean("notf", boolean)
        apply()
    }
}

这很好用。

这是我在 FirebaseMessagingService 上获取这些数据的方式:

@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
    preferences = getApplicationContext().getSharedPreferences("com.xxx.xxx.xxx",Context.MODE_PRIVATE);
    if(preferences.getBoolean("notf",true))
    sendNotification(remoteMessage.getNotification().getBody());
}

并且始终提供服务发送通知。

而且我没有在我的活动中启动此服务,它只是在 Manifest.xml 上的 Application 标签下;

     <service android:name="com.xxx.xxx.xxx.newsFirebaseMessagingService">
        <intent-filter>
            <action android:name="com.google.firebase.MESSAGING_EVENT" />
        </intent-filter>
    </service>

关于为什么我不能得到这个值的任何建议?谢谢

编辑:

我刚刚进行了几次调试,当应用程序恢复(在前台)时,firebase 服务正确获取值。但如果应用程序在后台(onPause)或应用程序被破坏,服务无法获取更正来自 SharedPreferences 的数据。

编辑 2

我从 FirebaseMessagingService 中删除了 onMessageReceived 函数,然后将该应用重新安装到我的设备上,当应用销毁时,即使没有“onMessageReceived”,我也会收到通知...

最后编辑

下面的解决方案

【问题讨论】:

  • 尝试使用 commit() 而不是 apply()。
  • @rmanalo 还是一样
  • 保存和检索几乎同时发生吗?根据我的经验,如果您保存一个偏好然后立即想要使用它,您可能无法获得您期望的价值。我有一个解决方法,但它不适用于您的情况,因为它们在不同的课程中。
  • @rmanalo 几秒钟后没有服务调用
  • 您确定该首选项存在吗?当首选项不存在时,getBoolean 方法返回默认值事件。尝试使用 contains(String key) 来了解偏好是否存在

标签: android firebase service kotlin sharedpreferences


【解决方案1】:

我遇到了类似的问题,并非所有来自 Android api 的东西在我的 FirebaseMessagingService 中都能正常工作。

我认为这可能与从 Oreo 及更高版本运行后台服务的限制有关,并且 FCM 消息对这些服务例外 https://developer.android.com/about/versions/oreo/background#services

我猜你只在 Android 上遇到过这个问题>=8,对吧?

所以我现在所做的是两段代码,分别用于 pre-Oreo 和 Oreo-and-newer,从我的 onMessageReceived 运行

@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
        val body = remoteMessage.getNotification().getBody()
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
            scheduleJobService(body);
        } else {
            runIntentService(body);
        }
}

然后在 runIntentService 中,我只是启动了一个 Intent 服务(我猜你知道怎么做),但由于上述限制,这只能在奥利奥之前工作

在 Android 8 及更高版本上,您需要安排 JobService 或实现您自己的自定义 BroadcastReceiver...

我选择 JobService,因为它对我来说更容易,而且我不介意有时等待 Android 来安排我的 Job,我所做的是:

@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private fun scheduleNotificationJobService(body: String) {
    val serviceName = ComponentName(getPackageName(),
            NotificationJobService::class.java.name)

    val scheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler

    val notificationInfoBundle = PersistableBundle()
    notificationInfoBundle.putString(Constants.EXTRA_NOTIF_BODY, body)

    val builder = JobInfo.Builder(JOB_ID, serviceName)
            .setOverrideDeadline(0)
            .setExtras(notificationInfoBundle)
    val notificationInfo = builder.build()
    scheduler.schedule(notificationInfo)
}

那么你的 JobService 会像这样:

@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
class NotificationJobService : JobService() {

    override fun onStopJob(params: JobParameters?): Boolean {}

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    override fun onStartJob(params: JobParameters?): Boolean {
        params?.extras?.let {
            val body = it.getString(Constants.EXTRA_NOTIF_BODY)
            val preferences = getApplicationContext().getSharedPreferences("com.xxx.xxx.xxx",Context.MODE_PRIVATE)
            if(preferences.getBoolean("notf",true)){
                sendNotification(body);
            }
        }
        return false
    }
}

记得在 AndroidManifest.xml 中声明它

    <service android:name=".push.NotificationJobService"
        android:permission="android.permission.BIND_JOB_SERVICE">
    </service>

如果这也适合你,请告诉我:) 干杯

【讨论】:

  • 感谢您的回答,我会尝试并告诉您结果
  • 它没有用.. 实际上,我从 FirebaseMessagingService 中删除了 onMessageReceived 函数,当然我完全删除了旧应用程序,然后我安装了该应用程序(没有 onMessageReceived 函数),当应用程序打开时销毁,即使没有'onMessageReceived'我也收到通知......
  • 我怀疑在活动中?.getSharedPreference()。尝试替换活动? getSharedPreference() 和 applicationContext.getSharedPreferences()??
【解决方案2】:

我终于解决了这个问题..

FCM(Firebase Cloud Messaging)中有两种类型的消息:

显示消息:这些消息仅在您的应用处于前台时触发 onMessageReceived() 回调

数据消息:这些消息会触发 onMessageReceived() 回调,即使您的应用处于前台/后台/已终止状态

所以我必须为我的应用发布数据消息。通常它是这样做的;

POST https://fcm.googleapis.com/fcm/send

标题

Key: Content-Type, Value: application/json
Key: Authorization, Value: key=<your-server-key>

正文使用主题

{
   "to": "/topics/my_topic",
   "data": {
    "my_custom_key": "my_custom_value",
    "my_custom_key2": true
    }
}

但是这不再有效(错误 401)。由于 FCM 现在使用 OAUTH 2,因此这种情况会出现身份验证问题

所以我阅读了firebase documentation 并根据文档发布数据消息的新方法是;

POST: https://fcm.googleapis.com/v1/projects/YOUR_FIREBASEDB_ID/messages:send

标题

Key: Content-Type, Value: application/json

授权

Bearer YOUR_TOKEN 

身体

{
   "message":{
    "topic" : "xxx",
    "data" : {
         "body" : "This is a Firebase Cloud Messaging Topic Message!",
         "title" : "FCM Message"
          }
      }
 }

在 URL 中有数据库 ID,您可以在 Firebase 控制台上找到它。 (Go 项目设置)

现在让我们拿走我们的令牌(它只会在 1 小时内有效):

首先在 Firebase 控制台中,打开 设置 > 服务帐号。 点击Generate New Private Key,安全存储包含密钥的JSON文件。我需要这个 JSON 文件来手动授权服务器请求。我下载了。

然后我创建了一个node.js项目并使用这个函数来获取我的token;

var PROJECT_ID = 'YOUR_PROJECT_ID';
var HOST = 'fcm.googleapis.com';
var PATH = '/v1/projects/' + PROJECT_ID + '/messages:send';
var MESSAGING_SCOPE = 'https://www.googleapis.com/auth/firebase.messaging';
var SCOPES = [MESSAGING_SCOPE];

  router.get('/', function(req, res, next) {
      res.render('index', { title: 'Express' });
      getAccessToken().then(function(accessToken) {
        console.log("TOKEN: "+accessToken)
      })

    });

function getAccessToken() {
return new Promise(function(resolve, reject) {
    var key = require('./YOUR_DOWNLOADED_JSON_FILE.json');
    var jwtClient = new google.auth.JWT(
        key.client_email,
        null,
        key.private_key,
        SCOPES,
        null
    );
    jwtClient.authorize(function(err, tokens) {
        if (err) {
            reject(err);
            return;
        }
        resolve(tokens.access_token);
    });
});
}

现在我在我的发布请求中使用这个令牌。然后我发布我的数据消息,它现在由我的应用程序 onMessageReceived 函数处理。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多