You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Retrofit遇401跳转LoginActivity及非Activity拦截器启动页面问题

解决Retrofit Interceptor中401跳转LoginActivity的问题 & 消除重复代码

首先得先指出你原Interceptor代码里的几个关键问题:

  1. 线程问题:Interceptor是在OkHttp的后台线程执行的,直接调用startActivity会因为不在主线程而报错;
  2. 上下文标记缺失:用Application上下文启动Activity时,必须添加Intent.FLAG_ACTIVITY_NEW_TASK,否则会抛出android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag.异常;
  3. 无效代码:finish()是Activity类的方法,Interceptor不是Activity,这行代码完全没用;
  4. 重复请求:你最后又调用了一次chain.proceed(newRequest),这会导致同一个请求被执行两次,这是严重的错误!

接下来给你几个更优的解决方案,彻底消除重复代码:

方案一:用LiveData实现全局401事件通知(推荐,低耦合)

这种方式通过全局LiveData发送401事件,在所有Activity的基类中统一监听处理,完全解耦网络层和UI层。

步骤1:创建全局事件LiveData

public class UnauthorizedEvent extends MutableLiveData<Boolean> {
    private static UnauthorizedEvent instance;

    public static synchronized UnauthorizedEvent getInstance() {
        if (instance == null) {
            instance = new UnauthorizedEvent();
        }
        return instance;
    }
}

步骤2:修改Interceptor发送事件

OkHttpClient client = new OkHttpClient.Builder().addInterceptor(new Interceptor() {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request newRequest = chain.request().newBuilder()
                .addHeader("Authorization", "Bearer " + auth_token_string)
                .build();
        Response response = chain.proceed(newRequest);
        Log.d("MyApp", "Code : "+response.code());
        
        if (response.code() == 401){
            // 切换到主线程发送事件
            new Handler(Looper.getMainLooper()).post(() -> 
                UnauthorizedEvent.getInstance().setValue(true)
            );
        }
        return response; // 注意:只返回一次请求结果!
    }
}).build();

步骤3:在BaseActivity中监听事件

创建所有Activity的基类,统一处理401跳转:

public class BaseActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 监听401未授权事件
        UnauthorizedEvent.getInstance().observe(this, isUnauthorized -> {
            if (isUnauthorized) {
                Intent loginIntent = new Intent(this, LoginActivity.class);
                // 清除栈内所有Activity,避免返回后回到未授权页面
                loginIntent.addFlags(
                    Intent.FLAG_ACTIVITY_CLEAR_TOP 
                    | Intent.FLAG_ACTIVITY_NEW_TASK 
                    | Intent.FLAG_ACTIVITY_CLEAR_TASK
                );
                startActivity(loginIntent);
                finish();
                // 重置事件状态,防止重复触发
                UnauthorizedEvent.getInstance().setValue(false);
            }
        });
    }
}

之后所有业务Activity都继承这个BaseActivity即可,无需再在每个回调里写401判断。

方案二:自定义Retrofit CallAdapter统一处理响应

这种方式在Retrofit的CallAdapter层统一拦截所有响应,自动处理401情况,适合希望在网络层直接处理的场景。

步骤1:实现自定义CallAdapter

public class AuthCallAdapter<T> implements CallAdapter<T, Call<T>> {
    private final CallAdapter<T, Call<T>> delegate;
    private final Context appContext;

    public AuthCallAdapter(CallAdapter<T, Call<T>> delegate, Context appContext) {
        this.delegate = delegate;
        this.appContext = appContext;
    }

    @Override
    public Type responseType() {
        return delegate.responseType();
    }

    @Override
    public Call<T> adapt(Call<T> call) {
        return new AuthWrappedCall<>(call, appContext);
    }

    private static class AuthWrappedCall<T> implements Call<T> {
        private final Call<T> delegate;
        private final Context appContext;

        AuthWrappedCall(Call<T> delegate, Context appContext) {
            this.delegate = delegate;
            this.appContext = appContext;
        }

        @Override
        public void enqueue(Callback<T> callback) {
            delegate.enqueue(new Callback<T>() {
                @Override
                public void onResponse(Call<T> call, Response<T> response) {
                    if (response.code() == 401) {
                        // 处理401跳转
                        Intent loginIntent = new Intent(appContext, LoginActivity.class);
                        loginIntent.addFlags(
                            Intent.FLAG_ACTIVITY_CLEAR_TOP 
                            | Intent.FLAG_ACTIVITY_NEW_TASK 
                            | Intent.FLAG_ACTIVITY_CLEAR_TASK
                        );
                        appContext.startActivity(loginIntent);
                        // 如果当前上下文是Activity,直接finish
                        if (appContext instanceof Activity) {
                            ((Activity) appContext).finish();
                        }
                    } else {
                        // 正常回调给业务代码
                        callback.onResponse(call, response);
                    }
                }

                @Override
                public void onFailure(Call<T> call, Throwable t) {
                    callback.onFailure(call, t);
                }
            });
        }

        // 其他Call接口方法直接委托给原Call实现
        @Override
        public Response<T> execute() throws IOException {
            return delegate.execute();
        }

        @Override
        public boolean isExecuted() {
            return delegate.isExecuted();
        }

        @Override
        public void cancel() {
            delegate.cancel();
        }

        @Override
        public boolean isCanceled() {
            return delegate.isCanceled();
        }

        @Override
        public Call<T> clone() {
            return new AuthWrappedCall<>(delegate.clone(), appContext);
        }

        @Override
        public Request request() {
            return delegate.request();
        }
    }
}

步骤2:创建CallAdapter工厂

public class AuthCallAdapterFactory extends CallAdapter.Factory {
    private final Context appContext;

    public AuthCallAdapterFactory(Context context) {
        this.appContext = context.getApplicationContext();
    }

    @Override
    public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
        // 获取Retrofit默认的CallAdapter
        CallAdapter<?, ?> delegate = retrofit.nextCallAdapter(this, returnType, annotations);
        return new AuthCallAdapter<>((CallAdapter<Object, Call<Object>>) delegate, appContext);
    }
}

步骤3:构建Retrofit时添加工厂

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(BASE_URL)
        .client(client)
        .addConverterFactory(GsonConverterFactory.create())
        .addCallAdapterFactory(new AuthCallAdapterFactory(getApplicationContext()))
        .build();

这样所有Retrofit的请求都会自动处理401情况,业务代码里无需再做任何判断。

方案三:直接在Interceptor中启动Activity(不推荐)

如果一定要在Interceptor里处理,需要修复线程和标记问题,但这种方式耦合性高,栈管理不够灵活:

OkHttpClient client = new OkHttpClient.Builder().addInterceptor(new Interceptor() {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request newRequest = chain.request().newBuilder()
                .addHeader("Authorization", "Bearer " + auth_token_string)
                .build();
        Response response = chain.proceed(newRequest);
        Log.d("MyApp", "Code : "+response.code());
        
        if (response.code() == 401){
            new Handler(Looper.getMainLooper()).post(() -> {
                Intent intent = new Intent(SplashActivity.getContextOfApplication(), LoginActivity.class);
                // 必须添加NEW_TASK标记,否则会报错
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                SplashActivity.getContextOfApplication().startActivity(intent);
            });
        }
        return response;
    }
}).build();

这种方式无法直接关闭当前Activity,只能通过FLAG_ACTIVITY_CLEAR_TASK清空整个栈,使用场景有限。


内容的提问来源于stack exchange,提问作者Rick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 03:53:04