JessYanCoding / MVPArms

⚔️ A common architecture for Android applications developing based on MVP, integrates many open source projects, to make your developing quicker and easier (一个整合了大量主流开源项目高度可配置化的 Android MVP 快速集成框架).
Apache License 2.0
10.29k stars 2.39k forks source link

关于 java.lang.IllegalStateException: network interceptor com.jess.arms.http.RequestInterceptor@e17a924 must call proceed() exactly once 18137503798 帮忙解决一下 这个是我的电话 #239

Closed cnmao closed 6 years ago

cnmao commented 6 years ago

Look at here

Environment

Bug Description:

Related Code:


public class GlobalConfiguration implements ConfigModule {

    @Override
    public void applyOptions(Context context, GlobalConfigModule.Builder builder) {
        //使用builder可以为框架配置一些配置信息
        builder.baseurl(Api.APP_DOMAIN_TEST);

        builder.globalHttpHandler(new GlobalHttpHandler() {
            @Override
            public Response onHttpResultResponse(String httpResult, Interceptor.Chain chain, Response response) {

                return getResponse(httpResult, chain, response, context);

            }

            @Override
            public Request onHttpRequestBefore(Interceptor.Chain chain, Request request) {
                String token = SpUtils.getToken(context);
                Request xmlHttpRequest = chain.request()
                        .newBuilder()
                        .addHeader("X-Auth-Token", token)
                        .removeHeader("User-Agent")//移除旧的
                        .addHeader("User-Agent", WebSettings.getDefaultUserAgent(context))//添加真正的头部
                        .addHeader("X-Requested-With", "XMLHttpRequest")
                        .build();

                return xmlHttpRequest;
            }
        });

//                .cacheFile(httpCacheDirectory);
    }

    private Response getResponse(String httpResult, Interceptor.Chain chain, Response response, Context context) {
        String refToken = SpUtils.getRefToken(context);
        if (!TextUtils.isEmpty(httpResult) && RequestInterceptor.isJson(response.body().contentType())) {
            Gson gson = new Gson();
            BaseJson baseJson = gson.fromJson(httpResult, BaseJson.class);

            if (!baseJson.isSuccess()) {
                if (baseJson.getErrorCode().equals("999")) {
                    String url = APP_DOMAIN_TEST + "login";
                    OkHttpClient okHttpClient = new OkHttpClient();
                    RequestBody body = new FormBody.Builder().add("refreshToken", refToken).build();
                    Request request = new Request.Builder().url(url)
                            .removeHeader("User-Agent")//移除旧的
                            .addHeader("User-Agent", WebSettings.getDefaultUserAgent(context))//添加真正的头部
                            .addHeader("X-Requested-With", "XMLHttpRequest")
                            .post(body).build();
                    Call call = okHttpClient.newCall(request);

                    try {
                        ResponseBody body1 = call.execute().body();
                        String string = body1.string();

                        Gson gson1 = new Gson();
                        LoginEntity loginEntity = gson1.fromJson(string, LoginEntity.class);
                        if (loginEntity.isSuccess()) {
                            LoginEntity.DataBean data = loginEntity.getData();
                            if (null != data && !TextUtils.isEmpty(data.getAccessToken())) {
                                SpUtils.saveToken(context, data.getAccessToken());
                                SpUtils.saveRefToken(context, data.getRefreshToken());

                                Request newRequest = chain.request().newBuilder().header("X-Auth-Token", data.getAccessToken())
                                        .build();
                                body1.close();
                                return chain.proceed(newRequest);
                            }
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                } else if (baseJson.getErrorCode().equals("997")) {
                    Intent intent = new Intent(context, LoginActivity.class);
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
                    context.startActivity(intent);
                }
            }
        }

        return response;
    }

    @Override
    public void injectAppLifecycle(Context context, List<AppLifecycles> lifecycles) {
        //AppDelegate.Lifecycle 的所有方法都会在基类Application对应的生命周期中被调用,所以在对应的方法中可以扩展一些自己需要的逻辑
        lifecycles.add(new AppLifecycles() {

            @Override
            public void attachBaseContext(Context base) {

            }

            @Override
            public void onCreate(Application application) {
                if (BuildConfig.LOG_DEBUG) {//Timber日志打印
                    Timber.plant(new Timber.DebugTree());
                }
                //leakCanary内存泄露检查
//                ((App) application).getAppComponent().extras().put(RefWatcher.class.getName(), BuildConfig.USE_CANARY ? LeakCanary.install(application) : RefWatcher.DISABLED);

                //ARouter初始化
                if (BuildConfig.LOG_DEBUG) {
                    ARouter.openLog();     // 打印日志
                    ARouter.openDebug();   // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
                }
                ARouter.init(application); // 尽可能早,推荐在Application中初始化
//                GreenDaoHelper.initDatabase(application);

            }

            @Override
            public void onTerminate(Application application) {

            }
        });
    }

    @Override
    public void injectActivityLifecycle(Context context, List<Application.ActivityLifecycleCallbacks> lifecycles) {
        //向Activity的生命周期中注入一些自定义逻辑
        lifecycles.add(new Application.ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                Timber.w(activity + " - onActivityCreated");
                //这里全局给Activity设置toolbar和title,你想象力有多丰富,这里就有多强大,以前放到BaseActivity的操作都可以放到这里
                if (activity.findViewById(R.id.toolbar) != null) {
                    if (activity instanceof AppCompatActivity) {
                        ((AppCompatActivity) activity).setSupportActionBar((Toolbar) activity.findViewById(R.id.toolbar));
                        ((AppCompatActivity) activity).getSupportActionBar().setDisplayShowTitleEnabled(false);
                    } else {
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                            activity.setActionBar((android.widget.Toolbar) activity.findViewById(R.id.toolbar));
                            activity.getActionBar().setDisplayShowTitleEnabled(false);
                        }
                    }
                }
                if (activity.findViewById(R.id.toolbar_title) != null) {
                    ((TextView) activity.findViewById(R.id.toolbar_title)).setText(activity.getTitle());
                }
                if (activity.findViewById(R.id.toolbar_back) != null) {
                    activity.findViewById(R.id.toolbar_back).setOnClickListener(v -> {
                        activity.onBackPressed();
                    });
                }

            }

            @Override
            public void onActivityStarted(Activity activity) {

            }

            @Override
            public void onActivityResumed(Activity activity) {

            }

            @Override
            public void onActivityPaused(Activity activity) {

            }

            @Override
            public void onActivityStopped(Activity activity) {

            }

            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

            }

            @Override
            public void onActivityDestroyed(Activity activity) {

            }
        });
    }

    @Override
    public void injectFragmentLifecycle(Context context, List<FragmentManager.FragmentLifecycleCallbacks> lifecycles) {
        //向Fragment的生命周期中注入一些自定义逻辑
        lifecycles.add(new FragmentManager.FragmentLifecycleCallbacks() {

            @Override
            public void onFragmentCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) {
                // 在配置变化的时候将这个 Fragment 保存下来,在 Activity 由于配置变化重建是重复利用已经创建的Fragment。
                // https://developer.android.com/reference/android/app/Fragment.html?hl=zh-cn#setRetainInstance(boolean)
                // 在 Activity 中绑定少量的 Fragment 建议这样做,如果需要绑定较多的 Fragment 不建议设置此参数,如 ViewPager 需要展示较多 Fragment
                f.setRetainInstance(true);
            }

            @Override
            public void onFragmentDestroyed(FragmentManager fm, Fragment f) {
//                ((RefWatcher) ((App) f.getActivity().getApplication()).getAppComponent().extras().get(RefWatcher.class.getName())).watch(f);
            }
        });
    }

}

Bug Log:

09-22 14:31:29.934 29202-29202/wawscm.com.oa W/System.err: java.lang.IllegalStateException: network interceptor com.jess.arms.http.RequestInterceptor@e17a924 must call proceed() exactly once
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:84)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
        at wawscm.com.oa.app.GlobalConfiguration.getResponse(GlobalConfiguration.java:150)
        at wawscm.com.oa.app.GlobalConfiguration.access$100(GlobalConfiguration.java:58)
        at wawscm.com.oa.app.GlobalConfiguration$1.onHttpResultResponse(GlobalConfiguration.java:91)
        at com.jess.arms.http.RequestInterceptor.intercept(RequestInterceptor.java:91)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
        at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:45)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
        at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
09-22 14:31:29.944 29202-29202/wawscm.com.oa W/System.err:     at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
        at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
        at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:120)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
        at com.jess.arms.di.module.ClientModule$1.intercept(ClientModule.java:87)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
        at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:185)
        at okhttp3.RealCall.execute(RealCall.java:69)
        at retrofit2.OkHttpCall.execute(OkHttpCall.java:180)
        at retrofit2.adapter.rxjava2.CallExecuteObservable.subscribeActual(CallExecuteObservable.java:41)
        at io.reactivex.Observable.subscribe(Observable.java:10903)
        at retrofit2.adapter.rxjava2.BodyObservable.subscribeActual(BodyObservable.java:34)
        at io.reactivex.Observable.subscribe(Observable.java:10903)
        at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96)
        at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:452)
        at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:61)
        at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:52)
        at java.util.concurrent.FutureTask.run(FutureTask.java:237)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:154)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:269)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
        at java.lang.Thread.run(Thread.java:818)

Others:

检查到 999 token过期 通过refreshToken 获取最新token 但是: 无法自动的请求原来的那个接口 ,

现象: token过期 , 请求下个接口 出现 上边的错误 ,然后 空白页, 需要手动刷新 才会出来数据

cnmao commented 6 years ago

// If we already have a stream, confirm that this is the only call to chain.proceed(). if (this.httpCodec != null && calls > 1) { throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)

JessYanCoding commented 6 years ago

https://github.com/JessYanCoding/MVPArms/blob/master/demo/src/main/java/me/jessyan/mvparms/demo/app/GlobalHttpHandlerImpl.java#L74 ,注释已经说的很清楚了,在 onHttpResultResponse() 之前 proceed() 已经被调用过了

cnmao commented 6 years ago

@JessYanCoding 嗯 解决了 不过加了两天班 O(∩_∩)O哈哈~ 很好的框架

cnmao commented 6 years ago

if (null != data && !TextUtils.isEmpty(data.getAccessToken())) { SpUtils.saveToken(context, data.getAccessToken()); SpUtils.saveRefToken(context, data.getRefreshToken());

                                Request request1 = chain.request();
                                Request.Builder builder = request1.newBuilder();
                                builder.removeHeader("X-Auth-Token")
                                        .header("X-Auth-Token", data.getAccessToken())
                                        .removeHeader("User-Agent")
                                        .header("User-Agent", WebSettings.getDefaultUserAgent(context))
                                        .removeHeader("X-Requested-With")
                                        .header("X-Requested-With", "XMLHttpRequest")
                                        .build();

                                Response execute = okHttpClient.newCall(request1).execute();

// networkResponse != null if (execute.code() == HTTP_OK) { ResponseBody body2 = execute.body(); String bodyString = body2.string(); okhttp3.MediaType contentType = body2.contentType(); return response.newBuilder().body(ResponseBody.create(contentType, bodyString)).build(); } return execute; }

JessYanCoding commented 6 years ago

... 你这代码不是和注释里描述的几乎一样吗,你早认真看注释不就早解决了

cnmao commented 6 years ago

哈哈 是呀 , 最后那个networkResponse != null 我整了好半天 。 但是现在还不知道 我是怎么解决的 。 他好像每次都是拿 cacheResponse 里边的 , 然后一直报这个警告 。 ResponseErrorListenerImpl 中 的msg 报 “未知错误” 。 所以就有了 上边的 execute.code() == HTTP_OK 的代码 。 应该是重新包装的意思吧?(猜想的!!!) @JessYanCoding