使用Retrofit和SharedPreferences时登录Token请求报错排查与修复
常见NullPointerException原因及修正方案
一、SharedPreferences实例初始化问题
- 错误场景:存储/读取Token时,SharedPreferences实例为null,比如在未初始化的Context环境下调用
getSharedPreferences,或是在Application生命周期启动前尝试获取实例。 - 修正:
- 使用全局Application Context初始化SharedPreferences,避免依赖Activity Context引发的内存泄漏或实例空值问题:
public class MyApp extends Application { private static SharedPreferences tokenSp; @Override public void onCreate() { super.onCreate(); tokenSp = getSharedPreferences("token_storage", MODE_PRIVATE); } public static SharedPreferences getTokenSp() { return tokenSp; } } - 存储Token时调用全局实例:
MyApp.getTokenSp().edit().putString("user_token", serverReturnedToken).apply();
- 使用全局Application Context初始化SharedPreferences,避免依赖Activity Context引发的内存泄漏或实例空值问题:
二、Retrofit拦截器中Token处理空指针
- 错误场景:拦截器中获取Token时,要么SharedPreferences未初始化,要么键名拼写错误导致返回null,后续拼接
"Bearer " + token时触发NPE。 - 修正:
- 在拦截器中先判断Token是否存在,再添加请求头:
public class TokenInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request original = chain.request(); String token = MyApp.getTokenSp().getString("user_token", null); Request finalRequest; if (token != null && !token.isEmpty()) { finalRequest = original.newBuilder() .header("Authorization", "Bearer " + token) .build(); } else { finalRequest = original; } return chain.proceed(finalRequest); } } - 确保Retrofit初始化时正确绑定拦截器,且拦截器依赖的SharedPreferences已完成初始化:
Retrofit retrofit = new Retrofit.Builder() .baseUrl(BASE_API_URL) .addConverterFactory(GsonConverterFactory.create()) .client(new OkHttpClient.Builder() .addInterceptor(new TokenInterceptor()) .build()) .build();
- 在拦截器中先判断Token是否存在,再添加请求头:
三、Token存储时机错误
- 错误场景:登录成功后未及时存储Token,或是存储操作在异步线程中未完成就发起需要Token的请求,导致读取时Token为null。
- 修正:
- 在登录接口的成功回调中同步存储Token,确认存储完成后再发起后续请求:
loginApi.login(loginRequestBody).enqueue(new Callback<LoginResp>() { @Override public void onResponse(Call<LoginResp> call, Response<LoginResp> response) { if (response.isSuccessful() && response.body() != null) { LoginResp resp = response.body(); if (resp.getResultCode() == 200) { // 立即存储Token MyApp.getTokenSp().edit().putString("user_token", resp.getToken()).apply(); // 再执行需要Token的接口请求 loadUserInfo(); } } } @Override public void onFailure(Call<LoginResp> call, Throwable t) { // 处理请求失败逻辑 } });
- 在登录接口的成功回调中同步存储Token,确认存储完成后再发起后续请求:
四、非空判断缺失
- 错误场景:读取Token后未做非空校验就直接使用,比如直接执行
header("Authorization", "Bearer " + token),当token为null时触发NPE。 - 修正:
- 所有使用Token的场景都先添加非空判断,确保变量有效后再执行后续逻辑。
内容的提问来源于stack exchange,提问作者박종준
相关产品推荐
相关产品推荐

