改造:如果响应代码为401,则重定向到LoginActivity

Ric*_*ick 21 java android retrofit okhttp retrofit2

如何LoginActivity从拦截器(非活动类)开始?我已经尝试过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){
                    Intent intent = new Intent(SplashActivity.getContextOfApplication(), LoginActivity.class);
                    startActivity(intent);
                    finish();  //Not working
                    return response;
                }

                return chain.proceed(newRequest);
            }
        }).build();
Run Code Online (Sandbox Code Playgroud)

这是我正在使用的当前解决方案,有没有比这更好的解决方案?该解决方案必须在每次api呼叫时保持重复.

主要活动

call.enqueue(new Callback<Token>() {
            @Override
            public void onResponse(Call<Token> call, Response<Token> response) {
                if(response.isSuccessful())
                {
                    //success
                }
                else
                {
                    Intent intent = new Intent(MainActivity.this.getApplicationContext(), LoginActivity.class);
                    startActivity(intent);
                    finish();
                }
            }
            @Override
            public void onFailure(Call<Token> call, Throwable t) {

            }
        });
Run Code Online (Sandbox Code Playgroud)

azi*_*ian 9

考虑引入retrofit2.Callback接口的自定义实现,例如BaseCallback:

public abstract class BaseCallback<T> implements Callback<T> {

    private final Context context;

    public BaseCallback(Context context) {
        this.context = context;
    }

    @Override
    public void onResponse(Call<T> call, Response<T> response) {
        if (response.code() == 401) {
            // launch login activity using `this.context`
        } else {
            onSuccess(response.body());
        }
    }

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

    }

    abstract void onSuccess(T response);

}
Run Code Online (Sandbox Code Playgroud)

现在,从主叫方的网站,你应该改变new Callback<Token>new BaseCallback<Token>:

call.enqueue(new BaseCallback<Token>(context) {
    @Override
    void onSuccess(Token response) {
        // do something with token
    }
});
Run Code Online (Sandbox Code Playgroud)

虽然,这种方法不符合您的以下声明:

所以我不必为每个api调用再次重复相同的代码

尽管如此,我无法想出更好的方法.


hlu*_*kyi 5

就个人而言,我建议在这里使用事件总线模式。您可以使用greenrobot实现或任何您想要的方法,因为它更多是关于体系结构方法而不是具体实现。

  1. 创建事件模型

    public class UnauthorizedEvent {
    
        private static final UnauthorizedEvent INSTANCE = new UnauthorizedEvent();
    
        public static UnauthorizedEvent instance() {
            return INSTANCE;
        }
    
        private UnauthorizedEvent() {
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 实施自定义Interceptor,以处理有关未经授权的需求的事件

    class UnauthorizedInterceptor implements Interceptor {
    
        @Override
        public Response intercept(@NonNull Chain chain) throws IOException {
            Response response = chain.proceed(chain.request());
            if (response.code() == 401) {
                EventBus.getDefault().post(UnauthorizedEvent.instance());
            }
            return response;
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 创建BaseActivity处理的类UnauthorizedEvent

    public class BaseActivity extends Activity {
    
        @Override
        public void onStart() {
            super.onStart();
            EventBus.getDefault().register(this);
        }
    
        @Override
        public void onStop() {
            super.onStop();
            EventBus.getDefault().unregister(this);
        }
    
        @Subscribe
        public final void onUnauthorizedEvent(UnauthorizedEvent e) {
            handleUnauthorizedEvent();
        }
    
        protected void handleUnauthorizedEvent() {
            Intent intent = new Intent(this, LoginActivity.class);
            startActivity(intent);
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  4. 阻止LoginActivityLoginActivity

    public class LoginActivty extends BaseActivity {
    
        @Override
        protected void handleUnauthorizedEvent() {
            //Don't handle unauthorized event
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    另一种方法是不在BaseActivity此处扩展。

  5. 注册您的拦截器

    OkHttpClient client = new OkHttpClient.Builder()
            .addInterceptor(new UnauthorizedInterceptor())
            .build();
    
    Run Code Online (Sandbox Code Playgroud)

优点:

  • 组件之间的松耦合
  • 通过覆盖轻松扩展逻辑 handleUnauthorizedEvent
  • 无需重写代码即可使用新型回调
  • 减少犯错误的人为因素(使用Callback代替BaseCallback

缺点:

  • EventBus模式使调试更加复杂
  • 一种新的依赖项或自己的实现,为项目带来新的代码

另外,请注意,此示例未涵盖多线程问题。它解决了您处理未经授权的请求的问题。因此,如果两个请求收到401,则有可能LoginActivity启动2个实例。


Him*_*der 5

这就是拦截器在全球范围内处理 401 的方式

public class ResponseHeaderInterceptor implements Interceptor {
private final Context context;

public ResponseHeaderInterceptor(Context context) {
    this.context = context;
}

@NotNull
@Override
public Response intercept(@NotNull Chain chain) throws IOException {
    Response response = chain.proceed(chain.request());
    if(response.code() == 401){
        SharedPreferences pref = context.getSharedPreferences(Constants.PREFERENCES, 0);
        String userName = pref.getString("key_user_email", "");
        //clear shared preferences
        pref.edit().clear().apply();
        Bundle params = new Bundle();
        params.putString("user", userName);
        FirebaseAnalytics.getInstance(context).logEvent(Constants.USER_UNAUTHORIZED_EVENT, params);
        Intent intent = new Intent(this.context, IntroActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        this.context.startActivity(intent);
    }
    return response;
}
Run Code Online (Sandbox Code Playgroud)

}

添加retrofit的okhttp客户端

var okHttpClient: OkHttpClient = OkHttpClient()
            .newBuilder()
            .addInterceptor(ResponseHeaderInterceptor(MyApplication.getMyApplicationContext()))//Header interceptor for logging network responses
            .build()
    private var retrofit: Retrofit? = null
    val client: Retrofit?
        get() {
            if (retrofit == null) {
                retrofit = Retrofit.Builder()
                        .client(okHttpClient)
                        .baseUrl(BuildConfig.SERVER)
                        .addConverterFactory(GsonConverterFactory.create())
                        .build()
            }
            return retrofit
        }
Run Code Online (Sandbox Code Playgroud)