欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

浅谈关于Android路由的实现

程序员文章站 2023-12-13 18:30:10
先说一下背景,目前有需求从外部包括其他应用和web跳转到我们自己的app,就这么个简单的需求…… 要实现这种外部跳转的功能,我们可以理解为打算跳转的一方有多少方式通知到a...

先说一下背景,目前有需求从外部包括其他应用和web跳转到我们自己的app,就这么个简单的需求……

要实现这种外部跳转的功能,我们可以理解为打算跳转的一方有多少方式通知到app进行相对的响应行为。所以,如果是应用之间的跳转,则有多种,你可以直接通过包名和具体的类名去打开已经exported=true的activity,又或者直接通过android的广播通知进行相关的app,又或者通过自定义的url去打开应用。但是如果涉及到web打开外部应用的话,目前只有一种办法,那就是自定义应用的url进行拦截,系统会自动调起相应的组件响应这个url。

但是,要做这种需求,很少会仅仅是完成对外部的支持而已,通常也要进行一定的内部逻辑跳转映射。所以要做这种需求通常分为两个种,一种是对内的(应用内部自己的跳转逻辑),一种对外的(其他应用以及web跳转逻辑)。

我们先说一下对外的情形,由于考虑到统一性,我们目前只有url这种手段可以使用了。下面我们一一来说

1、对外跳转说明

1.1、关于url的说明。

首先,我们得了解一下url,这里直接引用 https://en.wikipedia.org/wiki/url 的说明。为了方便说明,我稍稍修改一下,大概的格式如下:

scheme:[//host[:port]][/path][?query][#fragment]

首先,scheme是必须的,其他的都是不必须的,但是对于跳转来说,显然不可能,因为你要从这个url中取出跳转相关的信息。所以,通常一定要要有host和query。我们经常看到一些开源的路由实现,都会支持所谓的restful风格的url,比如:wytings://app/{city}/{id} ,但我个人认为是没有必要的。主要是因为这种外部跳转的行为,通常量比较少,其次应该尽量统一而且方便,而不是为了追求各种技术炫酷…我刻意看了微信的scheme就甚合我意~都是类似于这种格式:weixin://qrscan?a=1&b=2

我们进行一下归纳,就可以进行应用的url定义了,首先scheme是必须项,看个人和公司要求,比如接下来要举的例子,我定义的scheme为wytings,然后支持的模块都集中于host字段,具体参数则全部通过query补充。比如:wytings://user?uin=10000 打开个人页面,wytings://stockdetail?marketcode=hk&stockcode=00376 打开股票详情页面等等。

要是对外部的支持,通常我们不会对每一个要支持的activity都进行相应的intent-filter限制,而是定义一个公共的activity进行所有外部请求的拦截形如:

<activity
      android:name=".activity.schemefilteractivity"
      android:exported="true"
      android:theme="@android:style/theme.nodisplay">

      <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="wytings" />

      </intent-filter>

      <intent-filter
        android:autoverify="true"
        tools:targetapi="m">
        <action android:name="android.intent.action.view" />

        <category android:name="android.intent.category.default" />
        <category android:name="android.intent.category.browsable" />

        <data
          android:host="native.app.wytings.com"
          android:scheme="http" />
        <data
          android:host="native.app.wytings.com"
          android:scheme="https" />
      </intent-filter>
    </activity>

我们对这个activity的定义进行一下说明:

a、android:exported这个属性其默认是false就是对外不开放,我们必须要设置为true,因为我们要让外部能够对其进行访问。

b、android:theme="@android:style/theme.nodisplay" 由于是作为拦截的activity,所以,没必要展示,但是这个nodisplay的theme要求必须在onresume前finish掉activity,否则要报错。

c、第一个intent-filter自定义scheme为wytings,也就是拦截该类url。

d、第二个scheme为http,但是加了特别的host=nativ.app.wytings.com,进一步详细拦截url为:http://nativ.app.wytings.com 的url。为什么要拦截这种url,通常情况下不用,但是特殊情况下,有时候自定义的scheme可能失效,所以而外再加层保障,当然,也要与调用方预定好url格式,比如:http://nativ.app.wytings.com/stockdetail?marketcode=hk&stockcode=00376,由于host已经被定义为别的,所以我们把具体模块定义在path里面,参数依然保留在query中。

再来看看schemefilteractivity的实现情况:

/**
 * created by rex on 06/10/2017.
 *
 * @author wytings@gmail.com
 */

public class schemefilteractivity extends activity {

  @override
  protected void oncreate(bundle savedinstancestate) {
    super.oncreate(savedinstancestate);
    uri uri = getintent().getdata();
    log.i("wytings", "uri = " + uri);

    string scheme = uri.getscheme();
    if ("http".equals(scheme) || "https".equals(scheme)) {
      string routemodule = uri.getlastpathsegment();
      if (!textutils.isempty(routemodule)) {
        routemanager.getinstance().build(routemodule + "?" + uri.getquery()).go(this);
      }
    } else {
      routemanager.getinstance().build(uri.tostring()).go(this);
    }

    finish();
  }

}

大体就是拦截,然后通过内部的routemanager进行解析处理跳转。routemanager怎么处理和实现就太细节了,总的来说,这个manager的职责就是把url翻译成具体的intent,然后启动相应的activity。有兴趣的同学可以自己去看看本篇文章的所有源码:https://github.com/wytings/androidroute

2、对内跳转说明

由于是应用内的实现,所以基本上,你想怎么实现就怎么实现。但是,无论多么变幻莫测,都绕不开一个核心那就是建立路由映射关系,打开相关页面,取出请求参数这三大步骤。我们逐个来分析一下。

2.1、建立路由映射关系

这个是为了能够知道特定的url到底应该展示哪个页面。通常建立一个map,然后查找。

2.3、打开相关页面

在android中,打开一个页面总是有自己的一套逻辑,系统那一套则是通过intent去启动相应的组件展示。

2.4、取出参数

这个步骤,还是基于系统的intent方式,要通过intent.getxxxextra来取出相关参数。

这么一看好像,也没什么难度。也确实没什么难度,就单纯实现功能来说。那难点在哪呢?难点在于你决定使用注解去做这件事……为什么要用注解?因为为了哪一丁点洁癖,解藕的洁癖。结果掉进坑里了…

用注解理论上,也还好,遍历反射嘛,而且我个人测试了一下,就目前的机器真的感受不出来。当然再怎么样,也没在编译时直接生成相关代码来得快倒是真的……

于是乎,进入第三个大难题,那就是进行编译时生成代码,类似于butterknife一样,在编译期就生成相关代码,而不是在运行时通过反射来给变量赋值。

这里就涉及到一个东西,那就是java 的 abstractprocessor,这个类是在编译时生成代码最关键的类。要讲解这个得再开一篇《关于java注解实现编译时生成代码》的文章了。同学们可以网上搜索一下基本知识,然后再看这个项目中的代码,我自己也看了很多关于注解的文章,但是很遗憾,我没看到哪篇是值得捧的,同样也没看到那篇值得喷的……我现在也没时间专门写篇关于注解的文章,但是可以给个方向,那就是先学会调试,annotatioprocessor的调试,跟普通java调试有点区别(自己google一下),然后就可以自己摸索了。另外,我审视了一下,我写的annotation compiler还是蛮清晰的,你也可以试着看看。

最后,再说一遍,项目地址:androidroute

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

上一篇:

下一篇: