android自动路由,Android 路由动态配置方案——可能是最简单的热更新实现

Abstract

说到热更新,大多数人的第一印象肯定是AndFix或者HotFix等热更新框架。但是一来这些框架学习成本较高,坑较多,二来对于大的模块更新支持不好。所以在实际开发功能中,对于一些紧急的功能上线或者bug修复,使用H5页面替换原生页面是一个较为简单和方便的方案。本文主要讲解如何使用路由动态配置思想实现App内任意页面的替换。同时会给出一个Demo工程,方便大家学习和实现。

Url规范

首先为了实现原生页面和H5页面之间的任意跳转,我们需要定义一套统一的Url规范。我们知道一个Url主要由Schema、Host、path以及QueryParameter等构成。以下分条给出定义。

Schema

为了实现从浏览器直接跳入App,我建议使用私有Schema。同时用不同的Schema来表示不同的动作。以下是我定义的两个Schema。

dynamic: 用此Schema的url表示如果能用原生页面打开,则用原生页面打开,如果没有,则经过一些转化变成http或者https协议连接使用WebView打开。主要用来上线一些原生没有的页面。比如老版本没有这个页面就用h5页面替换,新版本还是用原生的。

dynamicWeb: 用此Schema的url表示总是用WebView打开。主要用来替换原生的一些页面。比如某个运营活动明天就上线了,但是临时更版肯定来不及了,就临时把某一个页面都替换为H5页面。

Path

Path包括Host部分来区分某一个页面。某些同学可能会问,在H5那边Host是用来表示服务器域名的,这不就不兼容了吗,如果手机没有安装App,用我们这种规范定义的url肯定就无法打开了。这确实是一个问题,但是我想如果为了兼容H5在客户端页面的url中加上服务器的域名感觉也很奇怪。所以作为一个客户端程序员来说,这样比较方便,就暂且这么定。而为了解决没有安装客户端的情况,需要给H5页面的跳转链接加上备选链接。为了解决客户端原生页面跳转H5页面的情况,需要为我们定义的Url加上H5服务器的域名。这是本方案不太优雅的地方,各位有什么建议欢迎提出。

QueryParameter

所有的参数都放在QueryParameter中。对于一些Object,就只能序列化一下也放在QueryParameter中了。

路由框架

为了方便的通过Url打开Activity或者WebView,我们需要一个路由框架。这里我推荐我开源的一个路由框架 AndRouter。AndRouter会根据Url的Schema选择不同的方式来打开这个url。同时它提供了ActivityRouter(将以activity为Schema的url转为Intent并打开对应Activity)的实现和BrowserRouter(用浏览器打开以http和https为Schema的url)的实现。为了让AndRouter支持我们自定义的dynamic和dynamicWeb为Schema的连接,我们需要自定义两个Router如下。

public class DynamicRouter implements IRouter {

private static final String DEFAULT_SCHEME = "dynamic";

...

protected boolean open(Context context, IRoute route){

String routeUrl = route.getUrl();

boolean isSuccess = false;

if(canOpenTheRoute(route)) {

//首先尝试用原生的Activity打开,如果无法打开,则使用WebView打开

try {

Uri uri = Uri.parse(routeUrl)

.buildUpon()

.scheme("activity")

.build();

//尝试Activity打开

if (!Router.open(context, uri.toString())) {

//失败,换用WebView打开,但是需要给Url加上H5域名

String path = UrlConfig.BASE_URL + UrlUtils.getHost(routeUrl);

Timber.i("path : %s", path);

String url = UrlUtils.addQueryParameters(path, UrlUtils.getParameters(routeUrl));

isSuccess = Router.open(context, url);

} else {

isSuccess = true;

}

} catch (Exception e) {

Timber.e(e, "");

}

}

return isSuccess;

}

...

}

复制代码

DynamicWebRouter 同理,不过不会尝试用Activity打开。

public class DynamicWebRouter extends BaseRouter {

private static final String DEFAULT_SCHEMA = "dynamicWeb";

...

protected boolean open(Context context, IRoute route){

boolean isSuccess = false;

if(canOpenTheRoute(route)){

//强制使用WebViewActivity打开

String routeUrl = route.getUrl();

try {

String host = UrlUtils.getHost(routeUrl);

String path = UrlConfig.BASE_URL + host;

Timber.i("path %s", path);

String url = UrlUtils.addQueryParameters(path, UrlUtils.getParameters(routeUrl));

isSuccess = Router.open(context, url);

} catch (Exception e){

Timber.e(e, "");

}

}

return isSuccess;

}

...

}

复制代码

给路由框架加上以上两个Router的实现,然后AndRouter就支持打开我们自定义的Schema Url了。

Router.initActivityRouter(getContext());

Router.addRouter(new WebRouter());

Router.addRouter(new DynamicRouter());

Router.addRouter(new DynamicWebRouter());

复制代码

### 路由动态刷新

为了让后台控制客户端的页面跳转,App在初始化的时候需要同步一下动态路由表接口,并缓存下来,每次需要做页面跳转的时候去表里查一下有没有动态配置项,如果有,则需要使用动态路由来做页面跳转。为了方便大家测试,我写了一个Spring项目,用来提供该接口,该项目同时也在github上开源了 RouterSender。

//客户端更新路由表

void refreshRouter(){

service.getRouters()

.enqueue(new Callback>() {

@Override

public void onResponse(Call> call, Response> response) {

if(response != null && response.body() != null) {

//RouterCache 用来缓存路由表

RouterCache.updateRouter(response.body());

} else {

Timber.w("路由更新失败");

}

}

@Override

public void onFailure(Call> call, Throwable t) {

Timber.e(t, "路由更新失败");

}

});

}

复制代码

路由选择

在每次跳转的时候进行的路由选择代码如下:

btn2.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

Map map = new HashMap();

map.put(TwoActivity.KEY_NAME, "Tango");

//如果有动态配置,则用动态url代开,某则用activity://three 对应的页面打开 并带上map中的参数

RouterTry.tryOpenOr(MainActivity.this, RouterCache.getRoute(KEY_ACTION_TWO), "activity://three", map);

}

});

复制代码

网页到App的统一入口

为了能够从网页跳入原生页面,我们设置了一个统一的Activity入口,不管是从浏览器跳入App还是从WebView跳到某一个原生页面,都从该Activity中转。这主要是为了方便做一些过滤,以及实施一些安全策略。

public class RouterDispatchActivity extends Activity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

Intent intent = getIntent();

Uri uri = intent.getData();

Router.open(RouterDispatchActivity.this, uri.toString());

finish();

}

}

复制代码

该Activity需要添加如下Intent-filter

复制代码

同时我们需要给WebView设置WebViewClient拦截网页内部的页面跳转,在发现是我们自定义协议链接,使用上面的RouterDispatchActivity来打开该Url。

private class CaptureWebViewClient extends WebViewClient {

...

@Override

public boolean shouldOverrideUrlLoading(WebView view, String url) {

if(TextUtils.equals(UrlUtils.getScheme(url), "dynamic") || TextUtils.equals(UrlUtils.getScheme(url), "dynamicWeb")) {

Intent intent = new Intent();

intent.setAction(Intent.ACTION_VIEW);

intent.setData(Uri.parse(url));

mContext.startActivity(intent);

return true;

} else {

return false;

}

}

}

复制代码

可能安全隐患

外部网页通过自定义Schema的链接在App内部打开一些恶意网页,引导用户进行一些危险性操作(比如输入用户名和密码)

参数泄露以及跨站攻击

Intent Schema Url攻击

规避策略

在UrlDispathActivity中对Url进行过滤和安全处理。不过我感觉问题不大,这里只是给大家提供个思路,大家可以想想会不会有问题。

总结

以上即为我的路由动态配置思路和实现。如有问题和建议,欢迎提出。

Demo项目地址

[服务器实现] (https://github.com/beautifulSoup/RouterSender)

广告

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值