【问题标题】:Accessing sharedPreferences inside Retrofit InterceptorAndroid/Kotlin:在 Retrofit Interceptor 中访问 sharedPreferences
【发布时间】: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)
    }

}

【问题讨论】:

  • 也许您可能想在ServiceInterceptors 构造函数中传递实例

标签: android kotlin retrofit retrofit2


【解决方案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()
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-09-10
          • 2012-05-31
          • 2021-12-22
          • 1970-01-01
          • 2015-03-17
          • 1970-01-01
          • 1970-01-01
          • 2019-09-25
          相关资源
          最近更新 更多