Android MVVM架构下如何通过Repository存储登录AccessToken到SharedPreference
我正在对接实时API,登录接口返回了AccessToken。采用MVVM架构,通过Repository处理数据,希望将该AccessToken存储到SharedPreference中,后续作为其他API的请求头使用,但不清楚如何通过Repository实现存储操作。以下是相关代码:
包结构

Repository类
class OpusRepoImpl private constructor() : OpusRepository { private val mLivedata: MutableLiveData<LoginPojo> = MutableLiveData() private var rInstance: RetrofitInstance = RetrofitInstance() override fun cLoginResponse( context: Context, username: String, password: String ): MutableLiveData<LoginPojo> { val apiServices = rInstance.getRetrofitInstance()?.create(ApiServices::class.java) val requestBody = LoginBody(username, password) val clientId = "myclientid" val clientSecret = "myClientSecrete" val grantType = "password" val call = apiServices?.getUser(clientId, clientSecret, grantType, requestBody) try { call?.enqueue(object : Callback<LoginPojo> { override fun onResponse( call: Call<LoginPojo>, response: Response<LoginPojo> ) { if (!response.isSuccessful) { Toast.makeText(context, "Login Failed", Toast.LENGTH_SHORT).show() return } val loginResponse = response.body() loginResponse?.access_token //Here i want to save the login access token into sharedpreference mLivedata.postValue(loginResponse!!) } override fun onFailure(call: Call<LoginPojo>, t: Throwable) { Toast.makeText(context, "Login Failed", Toast.LENGTH_SHORT).show() } }) } catch (e: Exception) { e.printStackTrace() Toast.makeText(context, "Login Failed", Toast.LENGTH_SHORT).show() } return mLivedata } companion object { private var instance: OpusRepoImpl? = null fun getInstance(): OpusRepoImpl { return instance ?: synchronized(this) { instance ?: OpusRepoImpl().also { instance = it } } } } }
Login Fragment
class LoginFragment : Fragment(), OnClickListener { private lateinit var binding: FragmentLoginBinding override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { binding = FragmentLoginBinding.inflate(inflater, container, false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) setOnclicklistener() } private fun setOnclicklistener() { binding.btnLogin.setOnClickListener(this) } companion object { @JvmStatic fun newInstance() = LoginFragment().apply { arguments = Bundle().apply { } } } override fun onClick(v: View) { when (v.id) { R.id.btnLogin -> { val email = binding.edEmail.text.toString() val pass = binding.edPassword.text.toString() if (isValid()) { loginCheck(email, pass) } } } } private fun loginCheck(email: String, pass: String) { //val sharedPreferences = requireContext().getSharedPreferences("MyPrefs", Context.MODE_PRIVATE) val loginViewModel = ViewModelProvider(this)[LoginViewModel::class.java] loginViewModel.loginResponse(requireContext(), email, pass).observe(viewLifecycleOwner, Observer { loginPojo -> val accessToken = loginPojo.access_token if (accessToken.isNotEmpty()){ Navigation.findNavController(requireView()).navigate(R.id.homepage) } }) } private fun isValid(): Boolean { binding.edEmail.error = null binding.edPassword.error = null var isValid = true val email = binding.edEmail.toString() val pass = binding.edPassword.toString() if (email.isEmpty()) { isValid = false binding.edEmail.error = "Enter Your Email" } else { val emailPattern = "[a-zA-Z0-9._-]+@[a-z]+\\.+[a-z]+" if (!email.matches(emailPattern.toRegex())) { binding.edEmail.error = "Enter a Valid Email" isValid = false } } if (pass.isEmpty()) { isValid = false binding.edPassword.error = "Enter Your Password" } else { if (pass.length < 8) { binding.edPassword.error = "Your pass was at-least 8 character" isValid = false } } return isValid }}
Login ViewModel
class LoginViewModel(val application: Application) : AndroidViewModel(application) { private val loginRepo = OpusRepoImpl.getInstance() fun loginResponse( context:Context, username: String, password: String ): MutableLiveData<LoginPojo> { return loginRepo.cLoginResponse(context, username, password) }}
解决方案
1. 封装SharedPreference工具类
先实现一个独立的SP工具类,统一处理Token的存储、读取和清除,避免Repository直接操作SP,降低耦合度:
object PreferenceManager { private const val PREF_NAME = "AuthPrefs" private const val KEY_ACCESS_TOKEN = "access_token" fun saveAccessToken(context: Context, token: String) { val sharedPref = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) with(sharedPref.edit()) { putString(KEY_ACCESS_TOKEN, token) apply() } } fun getAccessToken(context: Context): String? { val sharedPref = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) return sharedPref.getString(KEY_ACCESS_TOKEN, null) } fun clearAccessToken(context: Context) { val sharedPref = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) with(sharedPref.edit()) { remove(KEY_ACCESS_TOKEN) apply() } } }
2. 在Repository中保存Token
在登录接口的onResponse回调中,调用工具类的方法存储AccessToken:
override fun onResponse( call: Call<LoginPojo>, response: Response<LoginPojo> ) { if (!response.isSuccessful) { Toast.makeText(context, "登录失败", Toast.LENGTH_SHORT).show() return } val loginResponse = response.body() loginResponse?.access_token?.let { token -> // 保存Token到SharedPreference PreferenceManager.saveAccessToken(context, token) } mLivedata.postValue(loginResponse!!) }
3. 优化:避免Repository持有Context(推荐)
当前Repository通过方法参数获取Context,存在内存泄漏风险,建议初始化时传入Application Context:
- 修改Repository单例方法:
class OpusRepoImpl private constructor(private val appContext: Context) : OpusRepository { // 其他代码不变 companion object { private var instance: OpusRepoImpl? = null fun getInstance(appContext: Context): OpusRepoImpl { return instance ?: synchronized(this) { instance ?: OpusRepoImpl(appContext.applicationContext).also { instance = it } } } } }
- 修改ViewModel传递Application Context:
class LoginViewModel(val application: Application) : AndroidViewModel(application) { private val loginRepo = OpusRepoImpl.getInstance(application) fun loginResponse( username: String, password: String ): MutableLiveData<LoginPojo> { return loginRepo.cLoginResponse(username, password) } }
- 同时修改Repository的
cLoginResponse方法,移除Context参数,直接使用成员变量appContext
4. 后续API自动携带Token
通过Retrofit拦截器,自动将Token添加到请求头:
class AuthInterceptor(private val context: Context) : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val originalRequest = chain.request() val token = PreferenceManager.getAccessToken(context) val authenticatedRequest = token?.let { originalRequest.newBuilder() .header("Authorization", "Bearer $it") .build() } ?: originalRequest return chain.proceed(authenticatedRequest) } }
在Retrofit初始化时添加该拦截器:
fun getRetrofitInstance(): Retrofit? { val okHttpClient = OkHttpClient.Builder() .addInterceptor(AuthInterceptor(appContext)) .build() return Retrofit.Builder() .baseUrl("YOUR_BASE_URL") .client(okHttpClient) .addConverterFactory(GsonConverterFactory.create()) .build() }
内容的提问来源于stack exchange,提问作者Asikur Rahman
相关产品推荐
相关产品推荐

