从0到1实现一个Android路由——拦截请求再跳转
Posted xingfeng_coder
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从0到1实现一个Android路由——拦截请求再跳转相关的知识,希望对你有一定的参考价值。
从0到1实现一个android路由系列文章
- 从0到1实现一个Android路由(1)——初探路由
- 从0到1实现一个Android路由(2)——URL解析器
- 从0到1实现一个Android路由(3)——APT收集路由
- 从0到1实现一个Android路由(4)——多模块的APT收集路由
- 从0到1实现一个Android路由(5)——对Kotlin模块的支持
- 从0到1实现一个Android路由(6)——拦截请求再跳转
在从0到1实现一个Android路由(2)——URL解析器中,提到过请求拦截,其中有个常见的场景是某个页面是需要登录状态的,那么首先要调到登录页,完成了登录之后再跳转到路由页面,但通常登录页都是跳转到主页面的,这该怎么实现呢?上篇文章中没有解决这个问题,本文主要来解决这个问题。
解决这个问题的核心是Hook,接管startActivity(),进行偷梁换柱。因为所有的跳转最终都是通过startActivity来进行的,这里就选择了这么做。关于Hook原理,可以参考Android插件化原理解析——Hook机制之动态代理,本文主要着重说实现。
实现
关于实现,需要考虑的问题是如何保存url,在到了登录界面后,再跳转到原有页面的过程中还能找到先前的url进行跳转。
路由信息的保存与销毁
在拦截成功后,将该URL保存起来;在经过路由跳转的情况下,startActivity之前,清除URL。
EasyRouter的改造如下:
public boolean goToPages(Context context, String url)
boolean find = false;
if (TextUtils.isEmpty(url))
return find;
//判断是否拦截
if (routerListener != null && routerListener.onIntercept(url))
find = true;
//保存URL
currentUrl = url;
return find;
。。。
for (String key : urlRouterMap.keySet())
//key肯定是大于等于urlPath的,包含了绝对URL和相对URL
if (key.endsWith(urlPath))
find = true;
。。。
//消除URL
currentUrl = null;
context.startActivity(intent);
break;
//没有找到
if (!find && routerListener != null)
routerListener.onLost(url);
return find;
Hook实现
参考上面那篇文章的实现,增加了判断EasyRouter中是否有没有处理的URL,如果有,那就交给路由处理一把。
public class InstrumentationHook extends Instrumentation
private Instrumentation base;
public InstrumentationHook(Instrumentation base)
this.base = base;
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options)
//如果有URL,说明之前拦截过,交给路由继续执行
if (!TextUtils.isEmpty(EasyRouter.getInstance().getCurrentUrl()))
EasyRouter.getInstance().goToPages(who, EasyRouter.getInstance().getCurrentUrl());
return null;
// 开始调用原始的方法, 调不调用随你,但是不调用的话, 所有的startActivity都失效了.
// 由于这个方法是隐藏的,因此需要使用反射调用;首先找到这个方法
try
Method execStartActivity = Instrumentation.class.getDeclaredMethod(
"execStartActivity",
Context.class, IBinder.class, IBinder.class, Activity.class,
Intent.class, int.class, Bundle.class);
execStartActivity.setAccessible(true);
return (ActivityResult) execStartActivity.invoke(base, who,
contextThread, token, target, intent, requestCode, options);
catch (Exception e)
// 某该死的rom修改了 需要手动适配
throw new RuntimeException("do not support!!! pls adapt it");
Hook
Hook点可以设置在init()方法中,如下:
public boolean init(String scheme, String host)
try
UrlCollector urlCollector = (UrlCollector) Class.forName(URL_COLLECTOR_IMPL_CLASS_NAME).newInstance();
urlRouterMap = urlCollector.getUrlRouterMap();
this.scheme = scheme;
this.host = host;
//Hook
InstrumentationHook.attachContext();
return true;
catch (Exception e)
e.printStackTrace();
return false;
介绍完了原理后,再来看下demo。
demo
在上个版本的例子中进行增加,增加了一个LoginActivity,有个变量判断是否登录过,然后跳转到MainActivity,MainActivity对路由进行了拦截设置,如果url是启动SecondActivity,那么需要进行登录拦截判断。如下:
override fun onIntercept(url: String?): Boolean
if (url != null && url.startsWith("http"))
startActivity(Intent(this@MainActivity, WebViewActivity::class.java).apply
putExtra("external_url", url)
)
return true
if ((url == secondActivityUrl || url == dynamicUrl) && !LoginActivity.isLogin)
startActivity(Intent(this@MainActivity, LoginActivity::class.java))
return true
return false
现在来看下效果,启动SecondActivity或DynamicActivity时,会进行登录拦截判断。
这里启动SecondActivity时做了登录拦截,没有登录的时候,出现了登录界面,点击登录按钮后,本应出现MainActivity,但由于hook的原因,跳转到了本应跳转的SecondActivity。从而实现了拦截再跳转。
总结
本文主要是解决前面遗留的问题,拦截跳转的问题,本文使用的方式是Hook,记录需要跳转的路由,再Activity跳转前检测一次,需要的话就交给路由继续处理,从而hook掉原来的跳转。关于本文代码,可以参考master分支
至此,完成了从0到1实现一个Android路由的所有文章,一个好的路由是给别人用的,要有好的API接口,这儿主要是介绍思想,就没有对API接口进行很好的设计。
参考
关注我的技术公众号,不定期会有技术文章推送,不敢说优质,但至少是我自己的学习心得。微信扫一扫下方二维码即可关注:
以上是关于从0到1实现一个Android路由——拦截请求再跳转的主要内容,如果未能解决你的问题,请参考以下文章
从0到1实现一个Android路由——对Kotlin模块的支持