移动端基于动态路由的架构设计

好久好久没写过文章了,一是最近项目太忙了,没时间写。二是也没有时间学习新的东西,想写点什么却又无从下笔。最近在做混合App开发这块,从开始的ionic 框架,到后来的mui框架,让我在混合开发这块有了更深的理解,等不太忙了,我想我会总结一篇这些框架的使用心得吧。但是我今天不讲这个,我们来谈一谈在原生app中(iOS android)如何使用动态路由机制来搭建整个app的框架。
路由机制在web开发中是比较常见的,app开发中还是很少听到这种概念的,目前有些大公司采用的组件化开发(手淘,携程,蘑菇街等),倒是跟我们讲的有很多相同之处,不过它们的比较复杂,而且网上很多组件化开发,路由机制,没有一个能给出完整代码示例的,看着让人云里雾里的。索性自己就借鉴它们的思想,加上一点个人的理解,搞出了一个简单实用的可行性demo出来。我们主要介绍以下三方面内容:

1 什么是动态路由
2 它能解决我们什么问题
3 如何在项目中实现

一 什么是动态路由

原生开发没这概念,我们借助angular路由机制来解释这一概念,所谓路由,就是一套路径跳转机制,事先通过配置文件定义好一个路径映射文件,跳转时根据key去找到具体页面,当然angular会做一些缓存,页面栈的管理等等一些操作,它大致的定义是这样的

        angular.module('app',[])
            .config('$routeProvider',function  ($routeProvider) {
                $routeProvider
                    .when('/',{
                        templateUrl:'view/home.html',
                        controller:'homeCtrl'
                        }
                        )
                    .when('/',{
                        templateUrl:'view/home.html',
                        controller:'homeCtrl'
                        }
                        )
                    .when('/',{
                        templateUrl:'view/home.html',
                        controller:'homeCtrl'
                        }
                        )
                    .ontherwise({
                        redirective:'/'
                    })
            })
    </script>

config函数是一个配置函数。在使用
$routeProvider这样的一个服务。when:代表当你访问这个“/”根目录的时候 去访问 templateUrl中的那个模板。 controller可想已知,就是我们配套的controller,就是应用于根目录的这个 模板时的controller。
ontherwise 就是当你路径访问错误时,找不到。最后跳到这个默认的 页面。
为此我们可以总结一下几个特点:
1 一个映射配置文件
2 路径出错处理机制
这就是路由的基本意思,我们看看,在原生开发中,采用此种方式,他能解决我们什么问题。


二 它能解决我们什么问题

首先我们来比较一下我们以前的结构模式以及与 加入路由机制后的项目结构,实现路由机制,不仅需要一个映射文件,还需要一套路由管理机制,那么采用路由机制,我们的项目架构就跟原来不一样了,如下图:

原先业务之间的调用关系.png
加入路由后的页面调用关系.png

接下来我们看一下平时我们采用的页面跳转方法:
iOS 下
[self presentViewController:controller animated:YES completion:nil];
[self.navigationController pushViewController:controller animated:YES];

android 下

Intent intent = new Intent(this, A.class); 
 startActivity(intent);
startActivityForResult(Intent intent, Int requestCode)

我们看一下它有哪些缺点:
(1)都要在当前页面引入要跳转页面的class 类。这就造成了页面的耦合性很高。
(2)遇到重大bug,不能够及时的修复问题,需要等待更新发版后才能解决。
(3)推送消息,如果入口没有关与页面的引入处理,则不能跳转到指定页面。

引入路由机制后我们能否解决这些问题呢?
试想一下,如果我们通过一个配置文件来映射页面跳转关系,而且通过反射机制来取消头文件的引入问题,是不是我们就可以解决以上那些弊端了呢,比如,我们线上应用出现bug, 导致某个页面一打开,app就跪了,那我们是不是就可以通过更新路由配置文件,把它映射到另一个页面去:一个错误提示文件,或者一个线上H5能实现相同功能的页面。这样的话,原生app也具有了一定的动态更新能力,其实想想还有很多好处,比如项目功能太多原生开发要很长时间,但是领导又急着要上线,那么我们是不是就可以先开发一个网页版的模块,app路由映射到这个web页面,让用户先用着,等我们原生开发完了,然后再改一下映射文件,没升级的依旧用H5的路由,升级的就用原生的路由,如果H5页面我们要废弃了,那我们整体就可以路由到一个升级提升的页面去了。

总结一下路由机制能解决我们哪些问题:
1 避免引入头文件,是页面之间的依赖大大变少了(通过反射动态生成页面实例)。
2 线上出现重大bug,给我们提供了一个及时修补的入口
3 网页和原生切换更方便,更自由。
4 可以跳转任意页面 例如我们常用的推送,要打开指定的页面,以前我们怎么做的,各种启动判断,现在呢,我们只要给发送消息配个路由路径就行了,打开消息,就能够跳转到我们指定的页面。
等等,其它好处自行发掘。


三 如何在项目中实现

说了这么多概念性问题,下面我们就用代码来实现我们的构想, 下面先以IOS平台为例:
我们先看一下demo结构

iOS demo结构图.png

说明:WXRouter 路由管理文件
demo 路由使用示例
urlMap.plist 路由配置文件
我们主要讲解一下 WXRouter里面的几个文件,以及ViewController文件,还有urlmap.plist文件,其他请下载示例demo,文末我会给出demo地址。

WXRouter.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface WXRouter : NSObject
+(id)sharedInstance;
-(UIViewController *)getViewController:(NSString *)stringVCName;
-(UIViewController *)getViewController:(NSString *)stringVCName withParam:(NSDictionary *)paramdic;
@end
WXRouter.m
#import "WXRouter.h"
#import "webView.h"
#import "RouterError.h"
#import "PlistReadUtil.h"
#define SuppressPerformSelectorLeakWarning(Stuff) \
do {
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \
Stuff; \
_Pragma("clang diagnostic pop") \
}
while (0)
@implementation WXRouter
+(id)sharedInstance {
    static dispatch_once_t onceToken;
    static WXRouter * router;
    dispatch_once(&onceToken,^{
        router = [[WXRouter alloc] init];
});
return router;
}
-(UIViewController *)controller:(UIViewController *)controller withParam:(NSDictionary *)paramdic andVcname:(NSString *)vcName {
SEL selector = NSSelectorFromString(@"iniViewControllerParam:");
    if(![controller respondsToSelector: selector]){  //如果没定义初始化参数方法,直接返回,没必要在往下做设置参数的方法
        NSLog(@"目标类:%@未定义:%@方法",controller,@"iniViewControllerParam:");
return controller;
}
if(paramdic == nil) {
//如果参数为空 URLKEY 页面唯一路径标识别
        paramdic = [[NSMutableDictionary alloc] init];
        [paramdic setValue: vcName forKey:@"URLKEY"];
SuppressPerformSelectorLeakWarning([controller performSelector: selector withObject:paramdic]);
}
else {
[paramdic setValue: vcName forKey:@"URLKEY"];
}
SuppressPerformSelectorLeakWarning( [controller performSelector:selector withObject:paramdic]);
    return controller;
}
-(UIViewController *)getViewController:(NSString *)stringVCName {
NSString *viewControllerName = [PlistReadUtil plistValueForKey: stringVCName];
Class class = NSClassFromString(viewControllerName);
    UIViewController *controller = [[class alloc] init];
    if(controller == nil){  //此处可以跳转到一个错误提示页面
        NSLog(@"未定义此类:%@",viewControllerName);
        return nil;
}
return controller;
}
-(UIViewController *)getViewController:(NSString *)stringVCName withParam:(NSDictionary *)paramdic {
UIViewController *controller = [self getViewController: stringVCName];
if(controller != nil){
        controller = [self controller: controller withParam:paramdic andVcname:stringVCName];
}
else {
//异常处理  可以跳转指定的错误页面
        controller = [[RouterError sharedInstance] getErrorController];
}
return controller;
}
@end

说明:通过反射机制根据传入的string来获取 viewcontroller实例,实现了两个方法,一个是不需要传入参数的,一个是需要传入参数的,当跳转到第二个页面需要传值 就使用第二个带参数的方法,所传的值通过NSDictionary来进行封装,跳转后的页面通过实现
-(void)iniViewControllerParam:(NSDictionary *)dic 方法来获取传过来的参数。

PlistReadUtil.h
#import <Foundation/Foundation.h>
@interface PlistReadUtil : NSObject
@property(nonatomic,strong) NSMutableDictionary *plistdata;
+(id)sharedInstanceWithFileName:(NSString *)plistfileName;
+(NSString *)plistValueForKey:(NSString *)key;
@end
PlistReadUtil.h
#import "PlistReadUtil.h"
@implementation PlistReadUtil
+(id)sharedInstanceWithFileName:(NSString *)plistfileName {
    static dispatch_once_t onceToken;
    static PlistReadUtil * plistUtil;
    dispatch_once(&onceToken,^{
        plistUtil = [[PlistReadUtil alloc] init];
        NSString *plistPath = [[NSBundle mainBundle] pathForResource: plistfileName ofType:@"plist"];
    plistUtil.plistdata = [[NSMutableDictionary alloc] initWithContentsOfFile: plistPath];
});
return plistUtil;
}
+(NSString *)plistValueForKey:(NSString *)key {
PlistReadUtil *plist =  [PlistReadUtil sharedInstanceWithFileName: @"urlMap"];
return [plist.plistdata objectForKey: key];
}
@end

说明:路由配置文件读取工具类,我这里读取的是plist 文件,我这里也可以读取json,或则访问网络获取后台服务器上的路由配置文件,这个根据我们业务需求的不同,可以添加不同的读取方法。

RouterError.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface RouterError : NSObject
+(id)sharedInstance;
-(UIViewController *)getErrorController;
@end
RouterError.m
#import "RouterError.h"
#import "WXRouter.h"
@implementation RouterError
+(id)sharedInstance {
    static dispatch_once_t onceToken;
    static RouterError * routerError;
    dispatch_once(&onceToken,^{
        routerError = [[RouterError alloc] init];
});
return routerError;
}
#pragma mark  自定义错误页面 此页面一定确保能够找到,否则会进入死循环
-(UIViewController *)getErrorController {
NSDictionary *diction = [[NSMutableDictionary alloc] init];
    [diction setValue: @"https://themeforest.net/item/octopus-error-template/2562783" forKey:@"url"];
UIViewController *errorController = [[WXRouter sharedInstance] getViewController: @"MSG003" withParam:diction];
return errorController;
}
@end

说明:在读取配置文件时如果没有读到相应的路径,或者未定义相应的class,我们可以在这里处理,我这边处理的是如果出现错误,就返回一个webview页面,我们可以在项目里写一个统一的错误处理webview页面,其实每个页面默认都添加了一个参数[paramdic setValue:vcName forKey:@"URLKEY"]; 就是这个URLKEY,这个key标示配置文件中每个跳转动作的key,这个key是唯一的,我们可以根据不同的URLKEY然后通过后台统一的一个接口来判断跳转到不同的错误处理H5页面。

ViewController.m
#import "ViewController.h"
#import "view2.h"
#import "WXRouter.h"
#import "PlistReadUtil.h"
@interface ViewController ()
@end
@implementation ViewController
-(void)viewDidLoad {
    [super viewDidLoad];
    UILabel *lable = [[UILabel alloc] initWithFrame: CGRectMake(0, 0, 100, 50)];
    lable.textColor = [UIColor blueColor];
    lable.text =@"hello word";
    [self.view addSubview: lable];
    UIButton *button = [[UIButton alloc] initWithFrame: CGRectMake(0, 50, 200, 50)];
    [button setTitle: @"访问view1" forState:UIControlStateNormal];
    [button setTitleColor: [UIColor blackColor] forState:UIControlStateNormal];
    button.tag = 1;
    [button addTarget: self action:@selector(back:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview: button];
    UIButton *button2 = [[UIButton alloc] initWithFrame: CGRectMake(0, 110, 200, 50)];
    [button2 setTitle: @"访问view3" forState:UIControlStateNormal];
    [button2 setTitleColor: [UIColor blackColor] forState:UIControlStateNormal];
    button2.tag = 2;
    [button2 addTarget: self action:@selector(back:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview: button2];
    UIButton *butto3 = [[UIButton alloc] initWithFrame: CGRectMake(0, 170, 200, 50)];
    [butto3 setTitle: @"访问webview" forState:UIControlStateNormal];
    [butto3 setTitleColor: [UIColor blackColor] forState:UIControlStateNormal];
    butto3.tag = 3;
    [butto3 addTarget: self action:@selector(back:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview: butto3];
    UIButton *button4 = [[UIButton alloc] initWithFrame: CGRectMake(0, 230, 200, 50)];
    [button4 setTitle: @"访问wei定义的页面" forState:UIControlStateNormal];
    [button4 setTitleColor: [UIColor blackColor] forState:UIControlStateNormal];
    button4.tag = 4;
    [button4 addTarget: self action:@selector(back:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview: button4];
}
-(void)back:(UIButton *)btn {
    switch (btn.tag) {
        case 1: {
            NSMutableDictionary *dic = [[NSMutableDictionary alloc] init];
    [dic setValue: @"nihao shijie" forKey:@"title"];
    UIViewController *controller = [[WXRouter sharedInstance] getViewController: @"MSG001" withParam:dic];
    [self presentViewController: controller animated:YES completion:nil];
}
break;
        case 2: {
    NSMutableDictionary *dic = [[NSMutableDictionary alloc] init];
            [dic setValue: @"nihao shijie" forKey:@"title"];
    UIViewController *controller = [[WXRouter sharedInstance] getViewController: @"MSG002"  withParam:dic];
    [self presentViewController: controller animated:YES completion:nil];
}
break;
        case 3: {
    NSMutableDictionary *dic = [[NSMutableDictionary alloc] init];
            [dic setValue: @"https://www.baidu.com" forKey:@"url"];
    UIViewController *controller = [[WXRouter sharedInstance] getViewController: @"MSG003" withParam:dic];
    [self presentViewController: controller animated:YES completion:nil];
}
break;
        case 4: {
    UIViewController *controller = [[WXRouter sharedInstance] getViewController: @"MSG005"  withParam:nil];
    [self presentViewController: controller animated:YES completion:nil];
}
default:
            break;
}
}
-(void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
@end

说明:这个是使用示例,为了获取最大的灵活性,这里我并没有把跳转动作presentViewcontroller,pushViewController,以及参数的组装封装在路由管理类里。看过很多大神写的路由库,有些也通过url schema的方式。类似于:xml:id/123/name/xu,这样的路径方式,但是个人感觉,如果界面之间传递图片对象,或者传嵌套的类对象,就有点麻烦了。因为怕麻烦,所以就先写个简单的吧。

view3.m
#import "view3.h"
@interface view3 ()
@end
@implementation view3
- (void)viewDidLoad {
    [super viewDidLoad];
    UILabel *lable = [[UILabel alloc] initWithFrame: CGRectMake(0, 0, 100, 50)];
    lable.textColor = [UIColor blueColor];
    lable.text =@"我是view3";
    [self.view addSubview: lable];
    UIButton *button = [[UIButton alloc] initWithFrame: CGRectMake(200, 200, 200, 200)];
    [button setTitle: @"back" forState:UIControlStateNormal];
    [button setTitleColor: [UIColor blackColor] forState:UIControlStateNormal];
    [button addTarget: self action:@selector(back) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview: button];
}
-(void) back {
    [self dismissViewControllerAnimated: YES completion:nil];
}
-(void)iniViewControllerParam:(NSDictionary *)dic {
    self.title = [dic objectForKey: @"title"];
}

说明:这个是要跳转的页面我们可以通过iniViewControllerParam:(NSDictionary *)dic方法获取上一个界面传过来的参数。

urlMap.plist

说明:路由配置文件,key:value的形式,页面里的每个跳转动作都会对应一个唯一的key,这里如果两个页面都跳转到同一个页面,就会产生不同的key 对应相同的value,感觉是有点冗余了,如果有更好的优化,我会更新下文章的,这里的配置文件我们可以怎么玩,由于我在android的这块的描述已经很详细了,所以这里就不再赘述。只是android的配置有点坑,类前需要加上包名,这点就没有iOS方便灵活了,至此iOS示例我就讲完了。


下面让我们来看下Android下的示例
Android平台示例:

我们先看一下demo的结构

Paste_Image.png

说明:example.mixu.wxrouter 路由使用示例
wx.router 路由管理文件
assets 路由配置文件
接下来,我们主要讲解一下,wx.router里面的文件,以及assets配置文件结构,还有MainActivity 文件,其他的请下载示例demo,文末我会给出demo地址。

#MainActivity.java
package com.example.mlxu.wxrouter;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import com.wx.router.Router;
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       Log.v("activity","activity:" + RouterUtil.getActivity(this, "MSG001"));
       Log.v("activity", "activity:" + RouterUtil.getActivity(this, "MSG002"));
       Log.v("activity", "activity:" + RouterUtil.getActivity(this, "MSG003"));
    }
    public void buttonClick(View v) {
        int id = v.getId();
        switch (id) {
            case R.id.btn1: {
                startActivity(Router.initIntentWithActivityKey(this, "MSG001"));
            }
            break;
            case R.id.btn2: {
                startActivity(Router.initIntentWithActivityKey(this, "MSG002"));
            }
            break;
            case R.id.btn3: {
                Intent intent = Router.initIntentWithActivityKey(this, "MSG003");
                intent.putExtra("url", "http://www.baidu.com");
                startActivity(intent);
            }
            break;
            case R.id.btn4: {
                Intent intent = Router.initIntentWithActivityKey(this, "MSG005");
                intent.putExtra("url", "http://www.baidu.com");
                startActivity(intent);
            }
            break;
        }
    }
}

说明: 主要是调用Router.initIntentWithActivityKey(Context context, String key)方法来获取一个intent,这个intent里面会根据你传入的key去urlmap.json文件中查找对应的Activity,并设置好Intent要跳转的class,获取这个intent 后你只需要再设置一些跳转需要传的参数就可以了,为了提供最大的灵活性,我并没有把跳转参数,以及跳转动作统一封装在一块,我们现在只做一个最简单的路由跳转,虽然简单但我相信已经能够实现我们绝大部分需求了,此处也参考过一些大神的路由,他们都是机遇url schema 的方式。有时还是感觉不太灵活,比如我要传图片对象,基与url 路径的就不太好传。有空再深入研究他们的实现细节吧,呵呵。

#Router.java
package com.wx.router;
import android.content.Context;
import android.content.Intent;
public class Router {
    public static Class getActivityClassForName(Context context, String name) {
        Class clazz;
        try {
            clazz = Class.forName(name);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        return clazz;
    }
    public static Intent initIntentWithActivityKey(Context context, String key) {
        Class clazz;
        String activityName = RouterUtil.getActivity(context, key);
        try {
            if ((activityName == "") || (activityName == null)) {
                clazz = RouterError.errorClass(context);
            } else {
                clazz = Class.forName(activityName);
            }
        } catch (ClassNotFoundException e) {
            //            throw new RuntimeException(e);
            clazz = RouterError.errorClass(context);
        }
        Intent intent = new Intent(context, clazz);
        intent.putExtra("URLKEY", key); //??key??????????
        return intent;
    }
}

说明:此处很简单,就是通过把字符串路径转换成class对象,然后通过Intent setClass(Context packageContext, Class<?> cls)设置好要跳转的 Activity.注意这行代码intent.putExtra("URLKEY", key);这个我把每个跳转页面都传入也一个参数,URLKey,就是我们配置文件(urlmap.json)中的key,这个key是唯一的,为什么设置这个参数,主要就是为了识别唯一的跳转动作,假如我们这个页面跳转出错了,我们让它跳转到一个统一的错误页面,那么我们根据URLKEY这个参数就能知道是哪个页面跳转出错,该做什么操作,我可以展示一个相应的错误页面,或者跳转到一个好的有相同功能的H5页面。

RouterUtil.java
package com.wx.router;
import android.content.Context;
import android.content.res.AssetManager;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class RouterUtil {
    private final static String fileName = "urlmap.json";
    private static Map<String,String> maplist;
    public static  void initMaplistOfFile(Context context, String fileName)
    {
        if(maplist == null){
            synchronized (RouterUtil.class){
                if(maplist == null){
                    maplist = new HashMap<>();
                    StringBuffer sb = new StringBuffer();
                    AssetManager am = context.getAssets();
                    try {
                        BufferedReader bf = new BufferedReader(new InputStreamReader(am.open(fileName)));
                        String next = "";
                        while (null != (next = bf.readLine())){
                            sb.append(next);
                        }
                    }catch (IOException e){
                        e.printStackTrace();
                        sb.delete(0,sb.length());
                    }
                    try {
                        JSONArray jsonArray = new JSONArray(sb.toString().trim());
                        for(int i = 0; i < jsonArray.length(); i++){
                            JSONObject jsonObject = (JSONObject)jsonArray.get(i);
                            Iterator<String> keyIter= jsonObject.keys();
                            Map<String,String> map = new HashMap<String,String>();
                            String key;
                            Object value;
                            while (keyIter.hasNext()) {
                                key = keyIter.next();
                                value = jsonObject.get(key);
                                maplist.put(key, (String)value);
                            }
                        }
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    public static String getActivity(Context context,String urlkey){
        initMaplistOfFile(context, fileName);
        String value = "";
        try {
            value = maplist.get(urlkey);
        }catch (Exception e){
            value = "";
            Log.w("UrlError",urlkey+"值不存在");
        }
        return value;
    }
}

说明:路由读取工具类,就是读取urlmap.json文件,并转换成map,然后又提供一个根据key获取value的方法,在获取value时因为我们的配置文件也不能保证格式都是正确的,难免开发中会写错,我们要做一些异常捕获,并给出一些提示,我们有可能没配置key,也有可能没配置value,或者都没配置,一方配置出错,我们都无法实例化要跳转的Activity.

RouterError.java
package com.wx.router;
import android.content.Context;
public class RouterError {
  public static Class errorClass(Context context){
      return Router.getActivityClassForName(context,RouterUtil.getActivity(context,"MSG003"));
  }
}

说明:路由错误类,如果配置文件中没配置相应的antivity,我们可以引导跳转到一个统一的错误页面。

urlmap.json
[  {"MSG001":"com.example.mlxu.wxrouter.View1"},  {"MSG002":"com.example.mlxu.wxrouter.View2"},  {"MSG003":"com.example.mlxu.wxrouter.webview"},  {"MSG004":"com.example.mlxu.wxrouter.webview1"}]

说明:路由配置文件,是一个json文件,里面都是键值对 MSG001是key,后面就是相应的activity,key是唯一的,每一个页面跳转动作都对应一个Key,当然了这边也有一个问题,当两个页面都跳转到同一个页面时,会出现重复的value.这个有时间再想下有没有好的解决方法, 当然了这个配置文件我们可以打包在app内,也可以从服务器上拉取,或者两者结合,配合版本控制,我们就能够动态指定页面跳转路径,比如说MSG001对应的页面是个支付页面,但是突然出现了大bug,支付不了了。那么我们可以把MSG001改成一个我们统一的webview页面,这个页面中,我们可以让它跳转到我们线上临时的H5页面。这里面我们可以让所有发生错误的页面,都跳转到统一的webview,然后访问同一个后台接口,后台根据我们传的参数不同,然后引导跳转到不同的线上H5页面。
其实这里面还有个可以改进的,我们可以优化[ {"MSG001":"Native:com.example.mlxu.wxrouter.View1"}]
{"MSG001":"web:http://www.baidu.com"}]
加个前缀来识别自动跳转原生页面还是web页面,等等,想想的空间还很多,好了android 的demo 到这里也基本介绍完了。

总结:代码是简陋的,只是简单的实现了自己的构想,还有很多值得细细琢磨的地方,关键是架构思路,通过中间路由根据下发的路由配置文件来动态跳转页面,解决原生开发的遇到的一些问题,不同的项目有不同的业务逻辑,这种思路有什么缺陷,或者解决不了什么问题,大家一起讨论分享。基于这种思路搭建架子的话,对于将来的组件化开发,应该也会很方便转换吧。😊
demo地址
android:https://github.com/lerpo/WXAndroidRouter.git
iOS :https://github.com/lerpo/WXiOSRouter.git

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,688评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,559评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,749评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,581评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,741评论 3 271
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,684评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,122评论 2 292
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,847评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,441评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,939评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,333评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,783评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,275评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,830评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,444评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,553评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,618评论 2 249

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,471评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,050评论 18 139
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,158评论 0 17
  • 前言 随着用户的需求越来越多,对App的用户体验也变的要求越来越高。为了更好的应对各种需求,开发人员从软件工程的角...
    一缕殇流化隐半边冰霜阅读 86,382评论 214 1,095
  • 吃饭时无聊就思考了一个问题,一个关于餐饮行业的问题,下面我从背景和现象、分析与总结三个部分阐述一下我的思考。 思考...
    生椒牛肉阅读 937评论 8 15