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

  现在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;
	}
	@Override
	public 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后一秒后重启应用。

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



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值