如何从拦截器(非活动类)启动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();
这是我正在使用的当前解决方案,有没有比这更好的解决方案?这个解决方案必须在每次api呼叫时保持重复。
MainActivity
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) {
}
});
答案 0 :(得分:7)
考虑引入SELECT *
FROM v$open_cursor oc, v$session s
WHERE oc.sid = s.sid
order by 3,2;
接口的自定义实现,例如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);
}
new Callback<Token>
:
new BaseCallback<Token>
虽然这种方法不能满足您的以下声明:
然而,我无法想出更好的方法。所以我不必为每次api调用再次重复相同的代码
答案 1 :(得分:3)
就个人而言,我想建议在这里使用事件总线模式。您可以使用greenrobot implementation或任何您想要的内容,因为它更多地是关于体系结构方法而不是具体实现。
创建活动模型
public class UnauthorizedEvent {
private static final UnauthorizedEvent INSTANCE = new UnauthorizedEvent();
public static UnauthorizedEvent instance() {
return INSTANCE;
}
private UnauthorizedEvent() {
}
}
实施自定义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;
}
}
创建处理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);
}
}
阻止从LoginActivity
LoginActivity
public class LoginActivty extends BaseActivity {
@Override
protected void handleUnauthorizedEvent() {
//Don't handle unauthorized event
}
}
另一种方法是不在此处扩展BaseActivity
。
注册你的拦截器
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new UnauthorizedInterceptor())
.build();
优点:
handleUnauthorizedEvent
Callback
代替BaseCallback
)缺点:
此外,请注意,此示例并未涵盖多线程问题。它解决了处理未经授权的请求的问题。因此,如果两个请求收到401,则可能会启动2个LoginActivity
实例。
答案 2 :(得分:2)
广义解决方案: 您可以通过概括错误处理来解决它。您可以将自定义CallAdapterFactory用于“Retrofit”构建器。请参考以下课程:
RxErrorHandlingCallAdapterFactory:
public class RxErrorHandlingCallAdapterFactory extends CallAdapter.Factory {
private static Context mContext = null;
private final RxJava2CallAdapterFactory original;
private RxErrorHandlingCallAdapterFactory() {
original = RxJava2CallAdapterFactory.create();
}
public static CallAdapter.Factory create(Context context) {
mContext = context;
return new RxErrorHandlingCallAdapterFactory();
}
@Override
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
return new RxCallAdapterWrapper(retrofit, original.get(returnType, annotations, retrofit));
}
private static class RxCallAdapterWrapper<R> implements CallAdapter<R, Object> {
private final Retrofit retrofit;
private final CallAdapter<R,
Object> wrapped;
public RxCallAdapterWrapper(Retrofit retrofit, CallAdapter<R, Object> wrapped) {
this.retrofit = retrofit;
this.wrapped = wrapped;
}
@Override
public Type responseType() {
return wrapped.responseType();
}
@Override
public Object adapt(Call<R> call) {
Object result = wrapped.adapt(call);
if (result instanceof Single) {
return ((Single) result).onErrorResumeNext(new Function<Throwable, SingleSource>() {
@Override
public SingleSource apply(@NonNull Throwable throwable) throws Exception {
return Single.error(asRetrofitException(throwable));
}
});
}
if (result instanceof Observable) {
return ((Observable) result).onErrorResumeNext(new Function<Throwable, ObservableSource>() {
@Override
public ObservableSource apply(@NonNull Throwable throwable) throws Exception {
return Observable.error(asRetrofitException(throwable));
}
});
}
if (result instanceof Completable) {
return ((Completable) result).onErrorResumeNext(new Function<Throwable, CompletableSource>() {
@Override
public CompletableSource apply(@NonNull Throwable throwable) throws Exception {
return Completable.error(asRetrofitException(throwable));
}
});
}
return result;
}
private RetrofitException asRetrofitException(Throwable throwable) {
// We had non-200 http error
Log.v("log", "eror");
throwable.printStackTrace();
if (throwable instanceof HttpException) {
HttpException httpException = (HttpException) throwable;
final Response response = httpException.response();
//if ((mContext instanceof Activity)) {
String s = "Something went wrong."; //mContext.getString(R.string.something_went_wrong);
try {
s = new JSONObject(response.errorBody().string()).getString("message");
if (response.code() == 401) { // 401 Unauthorized
Intent intent = new Intent(mContext, LoginActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
mContext.startActivity(intent);
}
} catch (JSONException | IOException e) {
e.printStackTrace();
}
return RetrofitException.unexpectedError(s, response, retrofit);
//showErrorDialog(mContext, response);
//}
// return RetrofitException.httpError(response.errorBody().toString(), response, retrofit);
}
// A network error happened
if (throwable instanceof IOException) {
return RetrofitException.networkError((IOException) throwable);
}
// We don't know what happened. We need to simply convert to an unknown error
return RetrofitException.unexpectedError(throwable);
}
}
}
RetrofitException:
public class RetrofitException extends RuntimeException {
private final String url;
private final Response response;
private final Kind kind;
private final Retrofit retrofit;
RetrofitException(String message, String url, Response response, Kind kind, Throwable exception, Retrofit retrofit) {
super(message, exception);
this.url = url;
this.response = response;
this.kind = kind;
this.retrofit = retrofit;
}
public static RetrofitException httpError(String url, Response response, Retrofit retrofit) {
String message = response.code() + " " + response.message();
return new RetrofitException(message, url, response, Kind.HTTP, null, retrofit);
}
public static RetrofitException networkError(IOException exception) {
return new RetrofitException(exception.getMessage(), null, null, Kind.NETWORK, exception, null);
}
public static RetrofitException unexpectedError(Throwable exception) {
return new RetrofitException(exception.getMessage(), null, null, Kind.UNEXPECTED, exception, null);
}
public static RetrofitException unexpectedError(String s, Response response, Retrofit retrofit) {
return new RetrofitException(s, null, null, Kind.UNEXPECTED, null, null);
}
/**
* The request URL which produced the error.
*/
public String getUrl() {
return url;
}
/**
* Response object containing status code, headers, body, etc.
*/
public Response getResponse() {
return response;
}
/**
* The event kind which triggered this error.
*/
public Kind getKind() {
return kind;
}
/**
* The Retrofit this request was executed on
*/
public Retrofit getRetrofit() {
return retrofit;
}
/**
* HTTP response body converted to specified {@code type}. {@code null} if there is no
* response.
*
* @throws IOException if unable to convert the body to the specified {@code type}.
*/
public <T> T getErrorBodyAs(Class<T> type) throws IOException {
if (response == null || response.errorBody() == null) {
return null;
}
Converter<ResponseBody, T> converter = retrofit.responseBodyConverter(type, new Annotation[0]);
return converter.convert(response.errorBody());
}
/**
* Identifies the event kind which triggered a {@link RetrofitException}.
*/
public enum Kind {
/**
* An {@link IOException} occurred while communicating to the server.
*/
NETWORK,
/**
* A non-200 HTTP status code was received from the server.
*/
HTTP,
/**
* An internal error occurred while attempting to execute a request. It is best practice to
* re-throw this exception so your application crashes.
*/
UNEXPECTED
}
}
改造生成器:
Retrofit retrofit = new Retrofit.Builder()
.addCallAdapterFactory(RxErrorHandlingCallAdapterFactory.create(context))
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(API_URL)
.client(client)
.build();
您可以通过401
处理RxErrorHandlingCallAdapterFactory
中的Throwable
和其他错误。
答案 3 :(得分:1)
最简单的方法是在Interceptor实例中注入活动上下文。 如果您正在使用一些DI工具,如Dagger2或Toothpick,它将非常简单。我建议使用牙签)
https://github.com/stephanenicolas/toothpick
附近的大部分代码都在kotlin中,因为它是我的样板代码。那些人认为,你需要解决你的问题,我将用Java编写。
解决方案将是这样的:
@Qualifier
annotation class BackendUrl
class ActivityModule(activity: BaseActivity): Module() {
init {
bind(Activity::class.java).toInstance(activity)
}
}
class NetworkModule: Module() {
init {
bind(String::class.java).withName(BackendUrl::class.java).toInstance(Constants.URL)
bind(Gson::class.java).toInstance(GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss").create())
bind(CacheHolder::class.java).toProvider(CacheProvider::class.java).singletonInScope()
bind(OkHttpClient::class.java).toProvider(OkHttpProvider::class.java).instancesInScope()
bind(BackendApi::class.java).toProvider(BackendApiProvider::class.java).instancesInScope()
bind(RedirectInterceptor::class.java).to(RedirectInterceptor::class.java)
}
}
您需要为注入依赖
创建提供商class BackendApiProvider @Inject constructor(
private val okHttpClient: OkHttpClient,
private val gson: Gson,
@BackendUrl private val serverPath: String
) : Provider<BackendApi> {
override fun get() =
Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(okHttpClient)
.baseUrl(serverPath)
.build()
.create(BackendApi::class.java)
}
你的重定向拦截器:
public class RedirectInterceptor implements Interceptor {
private final Context context;
@Inject
public RedirectInterceptor(Activity context) {
this.context = context;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request newRequest = chain.request().newBuilder()
.build();
Response response = chain.proceed(newRequest);
Log.d("MyApp", "Code : "+response.code());
if (response.code() == 401){
Intent intent = new Intent(context, LoginActivity.class);
context.startActivity(intent);
((Activity) context).finish();
return response;
}
return chain.proceed(newRequest);
}
}
哦,是的。对于Authorization标头,最好创建另一个拦截器的新实例
class HeaderInterceptor(private val token: String?) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val newRequest = request.newBuilder()
Log.d(TAG, "token: $token")
if (token != null && token.isNotBlank()) {
newRequest.addHeader("Authorization", "Bearer $token")
}
return chain.proceed(newRequest.build())
}
companion object {
private val TAG = HeaderInterceptor::class.java.toString()
}
}
和你的OkhttpProvder
class OkHttpProvider @Inject constructor(cacheHolder: CacheHolder, prefs: IPreferences, redirectInterceptor: RedirectInterceptor) : Provider<OkHttpClient> {
private val client: OkHttpClient
init {
val builder = OkHttpClient.Builder()
builder
.addNetworkInterceptor(redirectInterceptor)
.addNetworkInterceptor(HeaderInterceptor(prefs.getAuthToken()))
.readTimeout(30, TimeUnit.SECONDS)
.cache(cacheHolder.okHttpCache)
client = builder.build()
}
override fun get() = client
}
多数民众赞成!现在,您只需要将模块放在正确的范围内。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.init_view)
Toothpick.openScopes("activity scope").apply {
installModules(ActivityModule(this@YourActivity))
Toothpick.inject(this@YourActivity, this)
}
Toothpick.openScopes("activity scope", "network scope").apply {
installModules(NetworkModule())
}
// your activity code
}
答案 4 :(得分:0)
这就是拦截器在全局处理 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;
}
}
添加到改造的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
}
答案 5 :(得分:0)
一个简单的解决方案。您可能会通过一点同步来改进它。
class ExpiredTokenDetectionInterceptor() : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request: Request = chain.request()
val response = chain.proceed(request)
if (response.code == 401) {
// do something
}
return response
}
}
答案 6 :(得分:-2)
使用OkHttpClient.Builder的authenticticator()方法。
当响应为401时,将自动重试未经授权的请求。
OkHttpClient client = new OkHttpClient.Builder().authenticator(new Authenticator() {
@Override
public Request authenticate(Route route, Response response) throws IOException {
// Handle 401 error code
if (response.code() == 401) {
Intent intent = new Intent(MainActivity.this.getApplicationContext(), LoginActivity.class);
startActivity(intent);
finish();
return null;
}
return null;
}
}