Android路由
什么是路由?最初接触路由是在大学计算机网络中,网络层IP报文传输会涉及一个路由表的概念,路由表由源IP、目的IP组成,起始就是一个映射表。Android路由也是一个映射表,映射什么呢? 这里先类比一下,如果把手机类比于浏览器,那么每个app就可以类比于一个个的网站,比如百度、头条等等,那么每个app的一个页面就可以类比于一个个网站里面的页面,浏览器的每个页面由url定义,给不同url传递不同参数,页面的表现形式还稍有不通过,这里的映射关系就是url对应页面,每个app的每个页面也可以类比于网站的页面,那是不是可以采用url的方式来定义每个页面呢?这样是不是也就有了url对应app页面的映射关系,如果有了这样的映射关系,给定一个url,那是不是就可以知道跳转到某一个具体的Activity了?Android路由其实就是解决这样的问题,那么实现一个最基础的Android路由主要有以下几步:
- 定义url,分配url给Activity;
- 建立路由表,url----->Activity
- 处理跳转,给定一个url,能跳转到具体的Activity。
第一个五张俱全的例子
第一个例子呢,很扯淡,但是呢,足以说明Android路由的意义。这里定义了两个Activity,其中SecondActivity用url(/secondActivity)表示,MainActivity启动SecondActivity是通过路由表来启动的,处理跳转的逻辑如下:
代码语言:javascript复制val routeMap = mapOf("easyrouter://demo/secondActivity" to SecondActivity::class.java)
fun goToPages(context: Context, url: String) { for ((key, value) in routeMap) { if (key == url) { context.startActivity(Intent(context, value)) } }}
这里就做了匹配,然后跳转,虽然很扯,但足以说明路由的精髓,后面会一步步地丰满。 这里对应最基本的路由,每一点都有:
- "/secondActivity"是url
- routeMap是路由表,这里可以理解成一个静态路由,即对应关系是硬编码的
- goToPages()方法处理url的跳转
下面一步步地丰满我们的EasyRouter。
外部app打开链接进入app
经常有这样的场景,在浏览器里会出现欢唤醒app的情况,唤醒后如何跳转到指定页面的呢?这里先解决这个问题,这样至少,我们的路由,外部打开内部页面是没有问题的。
清单文件注册scheme
解决这个问题,需要给我们的app里面某个页面添加scheme。这里我们用ProxyActivity进行scheme的设置,清单文件里的配置如下:
代码语言:javascript复制<activity android:name=".ProxyActivity"> <intent-filter> <action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="easyrouter" android:host="demo"/> </intent-filter> </activity>
这里,ProxyActivity可以被easyrouter://demo/xxxx这样的url打开。而action/category表示支持打开一些url,类似浏览器,只不过是打开easyrouter这样的scheme,而不是http或https。
Activity实现scheme跳转
再来看ProxyActivity的实现,
代码语言:javascript复制override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) intent.data?.apply { goToPages(this@ProxyActivity, path) finish() } }
intent.data可以获取从外部or其他页面传递的Uri参数,当从浏览器中打开时,链接的信息就会带过来,这里可以看到如果是easyrouter的scheme,那就交给goToPages()进行跳转。
HTML页面
html页面比较简单,就一个点击跳转的链接,
代码语言:javascript复制 <a href="easyrouter://demo/secondActivity">点击跳转到App</a></body>
然后用浏览器加载,第一次会提示是否用我们的demo打开,打开后就不再提示了,可以看到上面的链接也能实现从外部打开SecondActivity了。
应用内部打开外部url
所谓外部url,通常是网页,比如是应用的h5页面,Android加载网页需要用到WebView,WebView的定义如下:
代码语言:javascript复制override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_web_view)
webView.apply { settings.apply { javaScriptEnabled = true javaScriptCanOpenWindowsAutomatically = true setSupportMultipleWindows(true) } webViewClient = WebViewClient() webChromeClient = WebChromeClient() }
webView.loadUrl(intent.getStringExtra("external_url")) }
再回头看goToPages()对url的处理,如下:
代码语言:javascript复制fun goToPages(context: Context, url: String) { for ((key, value) in routeMap) { if (key == url) { context.startActivity(Intent(context, value)) } else if (url.startsWith("http")) { context.startActivity(Intent(context, WebViewActivity::class.java).apply { putExtra("external_url", url) }) } }}
可以看到,针对http开头的url,交由WebViewActivity进行加载。
总结
至此,第一个足以说明路由概念的例子就结束了,主要有静态路由表,控制路由跳转的逻辑,支持了外部应用跳转到应用,也支持应用内跳转原生页面和网页页面。那么一个优秀的路由应该是怎样的呢?或者说这个例子有哪些不足呢?
- 静态路由表,如果应用有很多应用,一个个的添加是不是很难维护?
- 跳转还不支持传参、不支持拦截
- 没有降级策略,比如有人恶意输入一个不存在页面的scheme,那app就显示不正常了。
后面会根据上面的几个不足一步步地完善路由。
关于代码,可以参考first_demo分支的代码。
参考阅读
- 通过Scheme协议打开APP界面
- android-Scheme与网页跳转原生的三种方式
- android webview loadurl弹出系统浏览器问题
- ARouter
- 安居客 Android 项目架构演进
- 大规模团队协同开发利器:阿里Atlas正式开源!
- Atlas
- Android Router 从 0 到 1
- 一文了解Android中路由(Router)的实现
- 考拉Android客户端路由总线设计
- WMRouter
- 美团外卖Android开源路由框架