Retrofit2+OkHttp3如何高效率的切换线上线下环境

来源:互联网 发布:淘宝怎么优化店铺 编辑:程序博客网 时间:2024/05/06 23:13

  现在Android开发非常流行使用Retrofit2+OkHttp3的组合做网络请求,在平时开发测试中,会有频繁切换线上线下环境的需求。一般情况下,线上线下环境url地址就是前缀不一样,修改一下前缀,重新编译打包。相当的费时间,特别是,产品,测试,后端,leader随时会丢过来一句:这是线下包,给我打个线上包;这个是线上包给我打个线下包。。。如果你正在全力奋战修改一个bug时,感觉就要崩溃了有木有。

        这时就会想,如果不需要重新编译该有多好,在app里面留一个后门,在某个地方连击多少下切换线上线下环境该有多好,当然为了不被用户发现后门,可以限制在debug包里面才有这个后门。

        我解决这个问题的核心是拦截器Interceptor,okhttp支持拦截器,在拦截器里面可以添加header,可以打印请求网络的日志等等,总之功能很多。我们就是要在拦截器里面替换掉url的前缀。代码如下:

public class ChangeUrlInterceptor implements Interceptor{private Context context;public ChangeUrlInterceptor(Context context){this.context = context;}@Overridepublic Response intercept(Chain chain) throws IOException {Request request = chain.request();Request.Builder requestBuilder = request.newBuilder();String url = request.url().toString();if(Config.isDebugMode(context)) {//默认是线上环境,如果是线下调试,将url前缀替换成线下的url前缀if (url.contains(Config.UC_REQUEST_PREFIX)) {url = url.replace(Config.UC_REQUEST_PREFIX, Config.DEBUG_UC_REQUEST_PREFIX);}}request = requestBuilder.url(url).build();return chain.proceed(request);}}
默认采用线上环境, 也就是Config.UC_REQUEST_PREFIX,拦截器里做一个判断如果是线下环境而且url里也包含线上url前缀,那么将前缀替换成线下环境的前缀Config.DEBUG_UC_REQUEST_PREFIX。

添加拦截器到OKHttp

OkHttpClient.Builder builder =                new OkHttpClient.Builder().connectTimeout(20 * 1000, TimeUnit.MILLISECONDS)                        .readTimeout(20 * 1000, TimeUnit.MILLISECONDS);        ChangeUrlInterceptor changeUrlInterceptor = new ChangeUrlInterceptor(context);        builder.addInterceptor(changeUrlInterceptor);        return builder.build();

再看看请求

@POST(Config.UC_REQUEST_PREFIX + "member/mobilelogin")    Observable<UCResponse<UCMobileLoginData>> mobileLogin(@Body String request);
因为我项目中采用了Dagger2注入,所以我项目中只用了一个retrofit对象,然后就交给Dagger2了,加上项目中请求有好几种不同的url前缀,所以baseUrl我基本上没用,请求上用的是全量的url地址。而且是用了注解,注解不支持方法,所以我才用拦截器拦截,不然的话可以写个方法获取url前缀,是线下就返回一个线下的url前缀,是线上就返回一个线上的url前缀。所以我使用拦截器来实现是有很多前提条件的,大家如果不受上面所说的限制,可以使用更轻量的解决方案。

回到正题,我们可以再应用的某个地方留一个后门,比方说点击连续点击五下,弹出一个对话框,告诉我们当前是线上还是线下环境,然后是否切换环境。重点是点击切换环境要做什么事情。当然为了规避风险,这个后门可以做到只在debug模式的的时候才有。

    exitConfirmDialog.dismiss();                            if(isOnline==0){                                SharedPreferencesUtils.saveIntData(getActivity(),"isOnline",1);                            }else if(isOnline==1){                                SharedPreferencesUtils.saveIntData(getActivity(),"isOnline",0);                            }                            //退出登录                            //清除一切跟登录信息相关的缓存                            RestartAPPTool.restartAPP(getContext(),1000);
先改变一下线上线下环境的状态值,然后退出登录,清除跟登录信息相关的一切缓存,为什么呢,因为切换了环境,再登录相当于是另外一个用户了,缓存里不能再有当前账号相关的东西了。最后一个操作是重启app。
public class RestartAPPTool {    public static void restartAPP(Context context, long Delayed){        /**开启一个新的服务,用来重启本APP*/        Intent intent1=new Intent(context,KillSelfService.class);        intent1.putExtra("PackageName",context.getPackageName());        intent1.putExtra("Delayed",Delayed);        context.startService(intent1);        /**杀死整个进程**/        killAllProcess(context);    }    /***重启整个APP*/    public static void restartAPP(Context context){        restartAPP(context,2000);    }    public static void killAllProcess(Context context) {        String processName = getServiceProcessName(context,NimService.class);        if(!TextUtils.isEmpty(processName)){            XLog.e("NimService processName: " + processName);            final ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);            // ActivityManager getRunningAppProcesses()            List<ActivityManager.RunningAppProcessInfo> appProcessList = am                    .getRunningAppProcesses();            if (appProcessList == null) {                return;            }            for (ActivityManager.RunningAppProcessInfo appProcess : appProcessList) {                XLog.e("processName: " + appProcess.processName);                if(processName.equals(appProcess.processName)){                    android.os.Process.killProcess(appProcess.pid);                }            }        }        android.os.Process.killProcess(android.os.Process.myPid());    }    private static String getServiceProcessName(Context context, Class<? extends Service> serviceClass) {        PackageManager packageManager = context.getPackageManager();        ComponentName component = new ComponentName(context, serviceClass);        ServiceInfo serviceInfo;        try {            serviceInfo = packageManager.getServiceInfo(component, 0);        } catch (Throwable ignored) {            // Service is disabled.            return null;        }        return serviceInfo.processName;    }}

这里就额外有一个杀死网易云信的进程,因为项目中使用了网易云信,然后这个网易云信跟咱们的用户系统是绑定的,我们用户登录了之后还要去网易云信登录。如果不杀死网易云信这个进程,去网易云信登录就会失败。

public class KillSelfService extends Service {    /**关闭应用后多久重新启动*/    private static  long stopDelayed=2000;    private Handler handler;    private String PackageName;    public KillSelfService() {        handler=new Handler();    }    @Override    public int onStartCommand(final Intent intent, int flags, int startId) {        stopDelayed=intent.getLongExtra("Delayed",2000);        PackageName=intent.getStringExtra("PackageName");        handler.postDelayed(new Runnable() {            @Override            public void run() {                Intent LaunchIntent = getPackageManager().getLaunchIntentForPackage(PackageName);                startActivity(LaunchIntent);                KillSelfService.this.stopSelf();            }        },stopDelayed);        return super.onStartCommand(intent, flags, startId);    }    @Override    public IBinder onBind(Intent intent) {        return null;    }}


杀死app后一秒后重启应用。

介绍完了,这样以后切换线上线下环境就方便高效多了。



2 0