关于Android路由的实现

  先说一下背景,目前有需求从外部包括其余应用和WEB跳转到咱们本身的APP,就这么个简单的需求……java

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

  可是,要作这种需求,不多会仅仅是完成对外部的支持而已,一般也要进行必定的内部逻辑跳转映射。因此要作这种需求一般分为两个种,一种是对内的(应用内部本身的跳转逻辑),一种对外的(其余应用以及Web跳转逻辑)。git

  咱们先说一下对外的情形,因为考虑到统一性,咱们目前只有URL这种手段可使用了。下面咱们一一来讲github

  一、对外跳转说明微信

  1.一、关于URL的说明。restful

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

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

  首先,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 打开股票详情页面等等。ui

  要是对外部的支持,一般咱们不会对每个要支持的Activity都进行相应的intent-filter限制,而是定义一个公共的Activity进行全部外部请求的拦截形如:

 1     <activity
 2             android:name=".activity.SchemeFilterActivity"
 3             android:exported="true"
 4             android:theme="@android:style/Theme.NoDisplay">
 5 
 6             <intent-filter>
 7 
 8                 <action android:name="android.intent.action.VIEW" />
 9 
10                 <category android:name="android.intent.category.DEFAULT" />
11                 <category android:name="android.intent.category.BROWSABLE" />
12 
13                 <data android:scheme="wytings" />
14 
15             </intent-filter>
16 
17             <intent-filter
18                 android:autoVerify="true"
19                 tools:targetApi="m">
20                 <action android:name="android.intent.action.VIEW" />
21 
22                 <category android:name="android.intent.category.DEFAULT" />
23                 <category android:name="android.intent.category.BROWSABLE" />
24 
25                 <data
26                     android:host="native.app.wytings.com"
27                     android:scheme="http" />
28                 <data
29                     android:host="native.app.wytings.com"
30                     android:scheme="https" />
31             </intent-filter>
32         </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.一、创建路由映射关系

  这个是为了可以知道特定的url到底应该展现哪一个页面。一般创建一个Map,而后查找。

  2.三、打开相关页面

  在Android中,打开一个页面老是有本身的一套逻辑,系统那一套则是经过Intent去启动相应的组件展现。

  2.四、取出参数

  这个步骤,仍是基于系统的Intent方式,要经过intent.getXXXExtra来取出相关参数。

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

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

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

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

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

  有兴趣的同窗,本身跑一下,胜读十年书!  

相关文章
相关标签/搜索