Android/Kotlin:在 Retrofit Interceptor 中访问 sharedPreferences
Posted
技术标签:
【中文标题】Android/Kotlin:在 Retrofit Interceptor 中访问 sharedPreferences【英文标题】:Accessing sharedPreferences inside Retrofit Interceptor 【发布时间】:2020-01-20 06:52:02 【问题描述】:这是一个 Retrofit 拦截器,用于在请求中自动注入令牌。我正在尝试从 sharedPreferences
获取此令牌,但 getSharedPreferences
在那里不可用。
如何从 Interceptor 中的 sharedpreferences 中检索我的令牌?
import android.preference.PreferenceManager
import okhttp3.Interceptor
import okhttp3.Response
class ServiceInterceptor: Interceptor
var token : String = "";
override fun intercept(chain: Interceptor.Chain): Response
var request = chain.request()
if(request.header("No-Authentication") == null)
if (request.url.toString().contains("/user/signin") === false)
// Add Authorization header only if it's not the user signin request.
// Get token from shared preferences
val sharedPreference = PreferenceManager.getSharedPreferences()
token = sharedPreference.getString("token")
if (!token.isNullOrEmpty())
val finalToken = "Bearer " + token
request = request.newBuilder()
.addHeader("Authorization", finalToken)
.build()
return chain.proceed(request)
【问题讨论】:
也许您可能想在ServiceInterceptor
s 构造函数中传递实例
【参考方案1】:
在 Kotlin 中有一个简单的解决方案——只需复制并粘贴代码到一个新的 AppPreferences.kt
文件中并按照 4 个 TODO 步骤操作 strong> 在代码中概述:
import android.content.Context
import android.content.Context.MODE_PRIVATE
import android.content.SharedPreferences
import androidx.core.content.edit
object AppPreferences
private var sharedPreferences: SharedPreferences? = null
// TODO step 1: call `AppPreferences.setup(applicationContext)` in your MainActivity's `onCreate` method
fun setup(context: Context)
// TODO step 2: set your app name here
sharedPreferences = context.getSharedPreferences("<YOUR_APP_NAME>.sharedprefs", MODE_PRIVATE)
// TODO step 4: replace these example attributes with your stored values
var heightInCentimeters: Int?
get() = Key.HEIGHT.getInt()
set(value) = Key.HEIGHT.setInt(value)
var birthdayInMilliseconds: Long?
get() = Key.BIRTHDAY.getLong()
set(value) = Key.BIRTHDAY.setLong(value)
private enum class Key
HEIGHT, BIRTHDAY; // TODO step 3: replace these cases with your stored values keys
fun getBoolean(): Boolean? = if (sharedPreferences!!.contains(name)) sharedPreferences!!.getBoolean(name, false) else null
fun getFloat(): Float? = if (sharedPreferences!!.contains(name)) sharedPreferences!!.getFloat(name, 0f) else null
fun getInt(): Int? = if (sharedPreferences!!.contains(name)) sharedPreferences!!.getInt(name, 0) else null
fun getLong(): Long? = if (sharedPreferences!!.contains(name)) sharedPreferences!!.getLong(name, 0) else null
fun getString(): String? = if (sharedPreferences!!.contains(name)) sharedPreferences!!.getString(name, "") else null
fun setBoolean(value: Boolean?) = value?.let sharedPreferences!!.edit putBoolean(name, value) ?: remove()
fun setFloat(value: Float?) = value?.let sharedPreferences!!.edit putFloat(name, value) ?: remove()
fun setInt(value: Int?) = value?.let sharedPreferences!!.edit putInt(name, value) ?: remove()
fun setLong(value: Long?) = value?.let sharedPreferences!!.edit putLong(name, value) ?: remove()
fun setString(value: String?) = value?.let sharedPreferences!!.edit putString(name, value) ?: remove()
fun exists(): Boolean = sharedPreferences!!.contains(name)
fun remove() = sharedPreferences!!.edit remove(name)
现在,您可以从应用中的任何位置获取值,如下所示:
val heightInCentimeters: Int? = AppPreferences.heightInCentimeters
val heightOrDefault: Int = AppPreferences.heightInCentimeters ?: 170
为SharedPreferences
设置值同样简单:
AppPreferences.heightInCentimeters = 160 // sets a new value
以上内容摘自我的FitnessTracker project。有关完整示例,请参阅 this file。
【讨论】:
【参考方案2】:正如 coroutineDispatcher 所说,您应该将共享首选项传递给拦截器的构造函数并持有对它们的引用。
试试这个:
class ServiceInterceptor(private val prefs: SharedPreferences): Interceptor
val token: String get() = prefs.getString("token")
override fun intercept(chain: Interceptor.Chain): Response
var request = chain.request()
if(request.header("No-Authentication") == null)
if (request.url.toString().contains("/user/signin") === false)
// Add Authorization header only if it's not the user signin request.
request = token
.takeUnless it.isNullOrEmpty
?.let
request.newBuilder()
.addHeader("Authorization", "Bearer $it")
.build()
?: request
return chain.proceed(request)
拦截器现在接受对共享首选项的引用,因此依赖关系已被反转,它可以通过存根传递的 SharedPreferences 来轻松进行测试。
它可以像这样被实例化:
ServiceInterceptor(PreferenceManager.getSharedPreferences())
【讨论】:
【参考方案3】:您可以为SharedPreference
创建一个singleton class
,然后您可以从任何您想要的class
访问它。
示例
class SessionManager private constructor(context:Context)
private val prefs:SharedPreferences
private val editor:SharedPreferences.Editor
var token:String
get()
return prefs.getString("token", "")
set(token)
editor.putString("token", token)
editor.apply()
init
prefs = context.getSharedPreferences("Your_Preference_name", Context.MODE_PRIVATE)
editor = prefs.edit()
companion object
private val jInstance:SessionManager
@Synchronized fun getInstance(context:Context):SessionManager
if (jInstance != null)
return jInstance
else
jInstance = SessionManager(context)
return jInstance
现在你必须通过constructor
中的ServiceInterceptor
中的context
,你可以访问SharedPreference
,如下所示。
val token = SessionManager.getInstance(context).token;
【讨论】:
【参考方案4】:试试这个
val token = PreferenceManager.getSharedPreferences().getToken("","")
builder.addInterceptor chain ->
val original = chain.request()
val requestBuilder = original.newBuilder()
.addHeader("Authorization", "Bearer $token")
val request = requestBuilder.build()
chain.proceed(request)
return builder.build()
【讨论】:
以上是关于Android/Kotlin:在 Retrofit Interceptor 中访问 sharedPreferences的主要内容,如果未能解决你的问题,请参考以下文章
Android Kotlin Retrofit + SimpleXMLConverter ElementList 解析不正确
Android Kotlin Retrofit Post Request 输入的数据未发送
Android Kotlin Retrofit 与Flow。两个Flow用LiveData来进行分解
Android@Kotlin 在Android studio 中配置Kotlin