目录介绍

  • 01.原生跳转实现
  • 02.实现组件跳转方式
    • 2.1 传统跳转方式
    • 2.2 为何需要路由
  • 03.ARouter配置与优势
  • 04.跨进程组件通信
    • 4.1 URLScheme
    • 4.2 AIDL
    • 4.3 BroadcastReceiver
    • 4.4 路由通信注意要点
  • 05.ARouter的结构
  • 06.ARouter的工作流程
    • 6.1 初始化流程
    • 6.2 跳转页面流程
  • 07.ARouter简单调用api
    • 7.1 最简单调用
    • 7.2 build源码分析
    • 7.3 navigation分析
  • 08.Postcard信息携带
  • 09.LogisticsCenter
  • 10.DegradeService降级容错服务
  • 11.Interceptor拦截器
  • 12.数据传输和自动注入
  • 13.多dex的支持
  • 14.InstantRun支持
  • 15.生成的编译代码

好消息

  • 博客笔记大汇总【16年3月到至今】,包括Java基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计N篇[近100万字,陆续搬到网上],转载请注明出处,谢谢!
  • 链接地址:https://github.com/yangchong211/YCBlogs
  • 如果觉得好,可以star一下,谢谢!当然也欢迎提出建议或者问题,万事起于忽微,量变引起质变!

注解学习小案例

  • 注解学习小案例,比较系统性学习注解并且应用实践。简单应用了运行期注解,通过注解实现了setContentView功能;简单应用了编译器注解,通过注解实现了防暴力点击的功能,同时支持设置时间间隔;使用注解替代枚举;使用注解一步步搭建简单路由案例。结合相应的博客,在来一些小案例,从此应该对注解有更加深入的理解……
  • 开源项目地址:https://github.com/yangchong211/YCApt

01.原生跳转实现

  • Google提供的原声路由主要是通过intent,可以分成显示和隐式两种。显示的方案会导致类之间的直接依赖问题,耦合严重;隐式intent需要的配置清单中统一声明,首先有个暴露的问题,另外在多模块开发中协作也比较困难。只要调用startActivity后面的环节我们就无法控制了,在出现错误时无能为力。

02.实现组件跳转方式

2.1 传统跳转方式

  • 第一种,通过intent跳转
  • 第二种,通过aidl跳转
  • 第三种,通过scheme协议跳转

2.2 为何需要路由

  • 显示Intent:项目庞大以后,类依赖耦合太大,不适合组件化拆分
  • 隐式Intent:协作困难,调用时候不知道调什么参数
  • 每个注册了Scheme的Activity都可以直接打开,有安全风险
  • AndroidMainfest集中式管理比较臃肿
  • 无法动态修改路由,如果页面出错,无法动态降级
  • 无法动态拦截跳转,譬如未登录的情况下,打开登录页面,登录成功后接着打开刚才想打开的页面
  • H5、Android、iOS地址不一样,不利于统一跳转

03.ARouter配置与优势

3.1 ARouter的优势

  • 如下所示

    • 直接解析URL路由,解析参数并赋值
    • 支持多模块项目
    • 支持InstantRun
    • 允许自定义拦截器
    • ARouter可以提供IoC容器
    • 映射关系自动注册
    • 灵活的降级策略

3.2 至于配置和使用

  • 直接看https://www.jianshu.com/p/fed5d5b95bae

04.跨进程组件通信

4.1 URLScheme【例如:ActivityRouter、ARouter等】

  • 优势有:

    • 基因中自带支持从webview中调用
    • 不用互相注册(不用知道需要调用的app的进程名称等信息)
  • 劣势有:
    • 只能单向地给组件发送信息,适用于启动Activity和发送指令,不适用于获取数据(例如:获取用户组件的当前用户登录信息)
    • 需要有个额外的中转Activity来统一处理URLScheme
    • 如果设备上安装了多个使用相同URLScheme的app,会弹出选择框(多个组件作为app同时安装到设备上时会出现这个问题)
    • 无法进行权限设置,无法进行开关设置,存在安全性风险

4.2 AIDL

  • 优势有:

    • 可以传递Parcelable类型的对象
    • 效率高
    • 可以设置跨app调用的开关
  • 劣势有:
    • 调用组件之前需要提前知道该组件在那个进程,否则无法建立ServiceConnection
    • 组件在作为独立app和作为lib打包到主app时,进程名称不同,维护成本高

4.3 BroadcastReceiver

  • BroadcastReceiver + Service + LocalSocket。该方案是参考cc路由框架!
  • 跨组件间通信实现的同时,应该满足以下条件:
    • 每个app都能给其它app调用
    • app可以设置是否对外提供跨进程组件调用的支持
    • 组件调用的请求发出去之后,能自动探测当前设备上是否有支持此次调用的app
    • 支持超时、取消

4.4 路由通信注意要点

05.ARouter的结构

  • ARouter主要由三部分组成,包括对外提供的api调用模块、注解模块以及编译时通过注解生产相关的类模块。

    • arouter-annotation注解的声明和信息存储类的模块
    • arouter-compiler编译期解析注解信息并生成相应类以便进行注入的模块
    • arouter-api核心调用Api功能的模块
  • annotation模块
    • Route、Interceptor、Autowired都是在开发是需要的注解。
  • compiler模块
    • AutoWiredProcessor、InterceptorProcessor、RouteProcessor分别为annotation模块对应的Autowired、Interceptor、Route在项目编译时产生相关的类文件。
  • api模块
    • 主要是ARouter具体实现和对外暴露使用的api。

06.ARouter的工作流程

6.1 初始化流程

  • 初始化代码如下所示

    /*** Init, it must be call before used router.*/
    public static void init(Application application) {//如果没有初始化,则if (!hasInit) {logger = _ARouter.logger;_ARouter.logger.info(Consts.TAG, "ARouter init start.");//做初始化工作hasInit = _ARouter.init(application);if (hasInit) {_ARouter.afterInit();}_ARouter.logger.info(Consts.TAG, "ARouter init over.");}
    }
    
  • 之后接着看_ARouter.init(application)这行代码,点击去查看
    protected static synchronized boolean init(Application application) {//赋值上下文mContext = application;//初始化LogisticsCenterLogisticsCenter.init(mContext, executor);logger.info(Consts.TAG, "ARouter init success!");hasInit = true;mHandler = new Handler(Looper.getMainLooper());return true;
    }
    
  • 接下来看看LogisticsCenter里面做了什么
    public class LogisticsCenter {/*** LogisticsCenter init, load all metas in memory. Demand initialization*/public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {mContext = context;executor = tpe;try {long startInit = System.currentTimeMillis();Set<String> routerMap;//debug或者版本更新的时候每次都重新加载router信息// It will rebuild router map every times when debuggable.if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {logger.info(TAG, "Run with debug mode or new install, rebuild router map.");// These class was generate by arouter-compiler.//加载alibaba.android.arouter.routes包下载的类routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);if (!routerMap.isEmpty()) {context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();}PackageUtils.updateVersion(context);    // Save new version name when router map update finish.} else {logger.info(TAG, "Load router map from cache.");routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));}logger.info(TAG, "Find router map finished, map size = " + routerMap.size() + ", cost " + (System.currentTimeMillis() - startInit) + " ms.");startInit = System.currentTimeMillis();for (String className : routerMap) {if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {// This one of root elements, load root. //导入ARouter$$Root$$app.java,初始化Warehouse.groupsIndex集合((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {// Load interceptorMeta//导入ARouter$$Interceptors$$app.java,初始化Warehouse.interceptorsIndex集合((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {// Load providerIndex//导入ARouter$$Providers$$app.java,初始化Warehouse.providersIndex集合((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);}}/*******部分代码省略********/} catch (Exception e) {throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");}}
    }
    
  • 综上所述,整个初始化的流程大概就是:
    • 初始化运行时的上下文环境
    • 初始化日志logger
    • 寻找router相关的类
    • 解析并且缓存路由相关信息
    • 初始化拦截服务

6.2 跳转页面流程

07.ARouter调用api

7.1 最简单调用

  • 最简单的调用方式

    ARouter.getInstance().build("/user/UserFragment").navigation();
    

7.2 build源码分析

  • 这个主要是添加跳转的路径

    public Postcard build(String path) {return _ARouter.getInstance().build(path);
    }
    
  • 然后把这个路径添加到默认的组中
    /*** Build postcard by path and default group*/
    protected Postcard build(String path) {if (TextUtils.isEmpty(path)) {throw new HandlerException(Consts.TAG + "Parameter is invalid!");} else {PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);if (null != pService) {path = pService.forString(path);}return build(path, extractGroup(path));}
    }
    

7.3 navigation分析

  • 如下所示

    final class _ARouter {protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {try {LogisticsCenter.completion(postcard);} catch (NoRouteFoundException ex) {/**************部分代码省略***************/if (null != callback) {callback.onLost(postcard);} else {    // No callback for this invoke, then we use the global degrade service.DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);if (null != degradeService) {degradeService.onLost(context, postcard);}}return null;}if (null != callback) {callback.onFound(postcard);}//是否为绿色通道,是否进过拦截器处理if (!postcard.isGreenChannel()) {   // It must be run in async thread, maybe interceptor cost too mush time made ANR.interceptorService.doInterceptions(postcard, new InterceptorCallback() {@Overridepublic void onContinue(Postcard postcard) {_navigation(context, postcard, requestCode, callback);}@Overridepublic void onInterrupt(Throwable exception) {//中断处理if (null != callback) {callback.onInterrupt(postcard);}}});} else {return _navigation(context, postcard, requestCode, callback);}return null;}private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {//没有上下文环境,就用Application的上下文环境final Context currentContext = null == context ? mContext : context;switch (postcard.getType()) {case ACTIVITY:// Build intent 构建跳转的intentfinal Intent intent = new Intent(currentContext, postcard.getDestination());intent.putExtras(postcard.getExtras());// Set flags. 设置flagint flags = postcard.getFlags();if (-1 != flags) {intent.setFlags(flags);} else if (!(currentContext instanceof Activity)) {    // Non activity, need less one flag.//如果上下文不是Activity,则添加FLAG_ACTIVITY_NEW_TASK的flagintent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);}// Navigation in main looper.  切换到主线程中new Handler(Looper.getMainLooper()).post(new Runnable() {@Overridepublic void run() {if (requestCode > 0) {  // Need start for resultActivityCompat.startActivityForResult((Activity) currentContext, intent, requestCode, postcard.getOptionsBundle());} else {ActivityCompat.startActivity(currentContext, intent, postcard.getOptionsBundle());}if ((0 != postcard.getEnterAnim() || 0 != postcard.getExitAnim()) && currentContext instanceof Activity) {    // Old version.((Activity) currentContext).overridePendingTransition(postcard.getEnterAnim(), postcard.getExitAnim());}if (null != callback) { // Navigation over.callback.onArrival(postcard);}}});break;case PROVIDER:return postcard.getProvider();case BOARDCAST:case CONTENT_PROVIDER:case FRAGMENT:Class fragmentMeta = postcard.getDestination();try {Object instance = fragmentMeta.getConstructor().newInstance();if (instance instanceof Fragment) {((Fragment) instance).setArguments(postcard.getExtras());} else if (instance instanceof android.support.v4.app.Fragment) {((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras());}return instance;} catch (Exception ex) {logger.error(Consts.TAG, "Fetch fragment instance error, " + TextUtils.formatStackTrace(ex.getStackTrace()));}case METHOD:case SERVICE:default:return null;}return null;}
    }
    

08.Postcard信息携带

  • Postcard主要为信息的携带者,内容是在构造一次路由信息的时候生产的,其继承于RouteMeta。RouteMeta是在代码编译时生成的内容,主要在初始化WareHouse时对跳转信息做了缓存。
  • 看看代码如下所示
    //Postcard继承于RouteMeta
    public final class Postcard extends RouteMeta//然后看看编译生成的文件
    /*** DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
    public class ARouter$$Group$$me implements IRouteGroup {@Overridepublic void loadInto(Map<String, RouteMeta> atlas) {atlas.put("/me/ExperienceCouponActivity", RouteMeta.build(RouteType.ACTIVITY, ExperienceCouponActivity.class, "/me/experiencecouponactivity", "me", null, -1, -2147483648));atlas.put("/me/ServiceActivity", RouteMeta.build(RouteType.ACTIVITY, ServiceActivity.class, "/me/serviceactivity", "me", null, -1, -2147483648));atlas.put("/me/SettingActivity", RouteMeta.build(RouteType.ACTIVITY, SettingActivity.class, "/me/settingactivity", "me", null, -1, -2147483648));atlas.put("/me/UdeskServiceActivity", RouteMeta.build(RouteType.ACTIVITY, UdeskServiceActivity.class, "/me/udeskserviceactivity", "me", null, -1, -2147483648));}
    }
    

10.DegradeService降级容错服务

  • 首先,自定义一个类,需要继承DegradeService类,如下所示

    /*** <pre>*     @author 杨充*     blog  : https://github.com/yangchong211*     time  : 2018/08/24*     desc  : ARouter路由降级处理*     revise:* </pre>*/
    @Route(path = DegradeServiceImpl.PATH)
    public class DegradeServiceImpl implements DegradeService {static final String PATH = "/service/DegradeServiceImpl";@Overridepublic void onLost(Context context, Postcard postcard) {if (context != null && postcard.getGroup().equals("activity")) {Intent intent = new Intent(context, WebViewActivity.class);intent.putExtra(Constant.URL, Constant.GITHUB);intent.putExtra(Constant.TITLE, "github地址");ActivityCompat.startActivity(context, intent, null);}}@Overridepublic void init(Context context) {}
    }
    
  • 如何使用该降级方案,十分简单。
    NavigationCallback callback = new NavCallback() {@Overridepublic void onArrival(Postcard postcard) {LogUtils.i("ARouterUtils"+"---跳转完了");}@Overridepublic void onFound(Postcard postcard) {super.onFound(postcard);LogUtils.i("ARouterUtils"+"---找到了");}@Overridepublic void onInterrupt(Postcard postcard) {super.onInterrupt(postcard);LogUtils.i("ARouterUtils"+"---被拦截了");}@Overridepublic void onLost(Postcard postcard) {super.onLost(postcard);LogUtils.i("ARouterUtils"+"---找不到了");DegradeServiceImpl degradeService = new DegradeServiceImpl();degradeService.onLost(Utils.getApp(),postcard);}
    };
    

11.Interceptor拦截器

  • 在ARouter模块的时候讲述Interceptor的使用,如果本次路由跳转不是走的绿色通道那么则会触发拦截器进行过滤。

    final class _ARouter {protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {/************部分代码省略************/if (!postcard.isGreenChannel()) {   // It must be run in async thread, maybe interceptor cost too mush time made ANR.interceptorService.doInterceptions(postcard, new InterceptorCallback() {/*** Continue process** @param postcard route meta*/@Overridepublic void onContinue(Postcard postcard) {_navigation(context, postcard, requestCode, callback);}/*** Interrupt process, pipeline will be destory when this method called.** @param exception Reson of interrupt.*/@Overridepublic void onInterrupt(Throwable exception) {if (null != callback) {callback.onInterrupt(postcard);}logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());}});} else {return _navigation(context, postcard, requestCode, callback);}return null;}
    }
    
  • 拦截器的初始化
    • 在刚开始初始化的时候,就已经做了这个操作。
    final class _ARouter {static void afterInit() {// Trigger interceptor init, use byName.interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();}
    }
    
  • InterceptorServiceImpl的init方法:
    @Route(path = "/arouter/service/interceptor")
    public class InterceptorServiceImpl implements InterceptorService {@Overridepublic void init(final Context context) {LogisticsCenter.executor.execute(new Runnable() {@Overridepublic void run() {if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {//循环遍历仓库中的拦截器for (Map.Entry<Integer, Class<? extends IInterceptor>> entry : Warehouse.interceptorsIndex.entrySet()) {Class<? extends IInterceptor> interceptorClass = entry.getValue();try {//反射机制构造自定义的每一个拦截器实例IInterceptor iInterceptor = interceptorClass.getConstructor().newInstance();iInterceptor.init(context);//并将其添加在缓存中Warehouse.interceptors.add(iInterceptor);} catch (Exception ex) {throw new HandlerException(TAG + "ARouter init interceptor error! name = [" + interceptorClass.getName() + "], reason = [" + ex.getMessage() + "]");}}interceptorHasInit = true;logger.info(TAG, "ARouter interceptors init over.");synchronized (interceptorInitLock) {interceptorInitLock.notifyAll();}}}});}
    }
    
  • 拦截器的工作过程
    @Route(path = "/arouter/service/interceptor")
    public class InterceptorServiceImpl implements InterceptorService {@Overridepublic void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {if (null != Warehouse.interceptors && Warehouse.interceptors.size() > 0) {//检测是否初始化完所有的烂机器checkInterceptorsInitStatus();//没有完成正常的初始化,抛异常if (!interceptorHasInit) {callback.onInterrupt(new HandlerException("Interceptors initialization takes too much time."));return;}//顺序遍历每一个拦截器,LogisticsCenter.executor.execute(new Runnable() {@Overridepublic void run() {CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());try {_excute(0, interceptorCounter, postcard);interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);//拦截器的遍历终止之后,如果有还有没有遍历的拦截器,则表示路由事件被拦截if (interceptorCounter.getCount() > 0) {    // Cancel the navigation this time, if it hasn't return anythings.callback.onInterrupt(new HandlerException("The interceptor processing timed out."));} else if (null != postcard.getTag()) {    // Maybe some exception in the tag.callback.onInterrupt(new HandlerException(postcard.getTag().toString()));} else {callback.onContinue(postcard);}} catch (Exception e) {callback.onInterrupt(e);}}});} else {callback.onContinue(postcard);}}//执行拦截器的过滤事件private static void _excute(final int index, final CancelableCountDownLatch counter, final Postcard postcard) {if (index < Warehouse.interceptors.size()) {IInterceptor iInterceptor = Warehouse.interceptors.get(index);iInterceptor.process(postcard, new InterceptorCallback() {@Overridepublic void onContinue(Postcard postcard) {// Last interceptor excute over with no exception.counter.countDown();//如果当前没有拦截过滤,那么使用下一个拦截器_excute(index + 1, counter, postcard);  // When counter is down, it will be execute continue ,but index bigger than interceptors size, then U know.}@Overridepublic void onInterrupt(Throwable exception) {// Last interceptor excute over with fatal exception.postcard.setTag(null == exception ? new HandlerException("No message.") : exception.getMessage());    // save the exception message for backup.counter.cancel();}});}}
    }
    

12.数据传输和自动注入

13.多dex的支持

  • 可查看multidex源码:

    public class ClassUtils {  /*** Identifies if the current VM has a native support for multidex, meaning there is no need for* additional installation by this library.** @return true if the VM handles multidex*/private static boolean isVMMultidexCapable() {boolean isMultidexCapable = false;String vmName = null;try {if (isYunOS()) {    // YunOS需要特殊判断vmName = "'YunOS'";isMultidexCapable = Integer.valueOf(System.getProperty("ro.build.version.sdk")) >= 21;} else {    // 非YunOS原生AndroidvmName = "'Android'";String versionString = System.getProperty("java.vm.version");if (versionString != null) {Matcher matcher = Pattern.compile("(\\d+)\\.(\\d+)(\\.\\d+)?").matcher(versionString);if (matcher.matches()) {try {int major = Integer.parseInt(matcher.group(1));int minor = Integer.parseInt(matcher.group(2));isMultidexCapable = (major > VM_WITH_MULTIDEX_VERSION_MAJOR)|| ((major == VM_WITH_MULTIDEX_VERSION_MAJOR)&& (minor >= VM_WITH_MULTIDEX_VERSION_MINOR));} catch (NumberFormatException ignore) {// let isMultidexCapable be false}}}}} catch (Exception ignore) {}Log.i(Consts.TAG, "VM with name " + vmName + (isMultidexCapable ? " has multidex support" : " does not have multidex support"));return isMultidexCapable;}
    }
    

14.InstantRun支持

  • 什么是InstantRun支持?

    • Android Studio 2.0 中引入的 Instant Run 是 Run 和 Debug 命令的行为,可以大幅缩短应用更新的时间。尽管首次构建可能需要花费较长的时间,Instant Run 在向应用推送后续更新时则无需构建新的 APK,因此,这样可以更快地看到更改。

15.生成的编译代码

  • 如下所示

关于其他内容介绍

01.关于博客汇总链接

  • 1.技术博客汇总
  • 2.开源项目汇总
  • 3.生活博客汇总
  • 4.喜马拉雅音频汇总
  • 5.其他汇总

02.关于我的博客

  • 我的个人站点:www.yczbj.org,www.ycbjie.cn
  • github:https://github.com/yangchong211
  • 知乎:https://www.zhihu.com/people/yczbj/activities
  • 简书:http://www.jianshu.com/u/b7b2c6ed9284
  • csdn:http://my.csdn.net/m0_37700275
  • 喜马拉雅听书:http://www.ximalaya.com/zhubo/71989305/
  • 开源中国:https://my.oschina.net/zbj1618/blog
  • 泡在网上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
  • 邮箱:yangchong211@163.com
  • 阿里云博客:https://yq.aliyun.com/users/article?spm=5176.100- 239.headeruserinfo.3.dT4bcV
  • segmentfault头条:https://segmentfault.com/u/xiangjianyu/articles
  • 掘金:https://juejin.im/user/5939433efe88c2006afa0c6e

ARouter路由解析相关推荐

  1. php 各种路由分析_php框架中的路由解析类

    摘要:实现两个功能:1.路由解析  2.请求分发 namespace pig; class Route{ //路由配置信息 protected $route = []; &n实现两个功能:1. ...

  2. ARouter原理解析

    一.ARouter概述 用简单一两句话总结出来:ARouter通过Apt技术,生成保存路径(路由path)和被注解(@Router)的组件类的映射关系的类,利用这些保存了映射关系的类,Arouter根 ...

  3. 人人php 路由,关于人人分销/人人商城的二次开发路由解析

    最近在做人人分销的二次开发,遇到奇葩客户,没办法,只能硬着头皮上,需要做一个后台设定可以设置是否默认会员自动升级的设置,然后就扒了一边微擎+人人分销v2的路由.记录分享下~ 首先微擎目录下有app 和 ...

  4. openresty lor框架路由解析

    app 中间件 router 请求处理过程 lor 不足 lor框架 openresty web框架lor,lor 是一个基于 ngx_lua的 MVC 框架,其 API 很类似于 Node 社区的著 ...

  5. php 框架 路由解析,来!狂撸一款PHP现代化框架 (路由的设计)

    前言 上一篇的标题改了一下,以一.二.三为章节对读者来说是种困扰,现在的标题是依照项目进度来编写的.上篇文章地址为 https://segmentfault.com/a/11... 这一系列文章并不准 ...

  6. wordpress url index.php,WordPress对URL的路由解析过程详解

    本文说明WP 对URL rewrite并生成当前请求的过程. 实际内容并不复杂, 说的比较啰嗦啦- 关于Query Vars 这是Wordpress全部代码中最重要的变量,所谓的query vars是 ...

  7. 想转行当程序员的必看!揭秘ARouter路由机制,Android校招面试指南

    Android没凉,只是比以前难混了 多年前Android异军突起,成了新的万亿级市场,无数掘金人涌入,期待可以一展拳脚. 那时候大环境下的手游圈,只要你能有个可以运行的连连看就能找到工作,走上赛道被 ...

  8. 微擎学习--路由解析

    1. http://weiqin.com/web/index.php?c=site&a=entry&m=ewei_shopv2&do=web&r=shop.adv 当传 ...

  9. OpenWrt 软路由解析公网IPV6域名访问家庭NAS的问题答疑

    1.非要使用 Padavan 或者 OpenWrt固件的软路由才能IPV6公网访问吗? 答:这个并不是这样的,一般家用路由器都无法放行防火墙规则,这种情况当然无法实现IPV6公网访问,但是少部分路由器 ...

最新文章

  1. 大数据调度平台Airflow(一):什么是Airflow
  2. Excel公式与函数案例速查手册/电脑技巧从入门到精通丛书
  3. A wizard’s guide to Adversarial Autoencoders: Part 3, Disentanglement of style and content.
  4. oracle开发项目流程,如何开发ORACLE存储过程
  5. 高并发解决方案_高并发提交订单的解决方案
  6. effective java ---读书笔记一
  7. 中国最美街景,带你一次看个够
  8. (转)oracle表分区详解
  9. mvc html绑定变量,c# – Asp.Net MVC 3使用变量对象进行自定义模型绑定
  10. Tao 1.2.0图形框架发布
  11. 如何从“点子”落地到“执行”?—完整解析1个手游传播类mini项目的进化
  12. HDU1556 Color the ball【差分数组+线段树】
  13. 关于U盘物理读写锁失效解决(只读状态):工具解锁或U盘量产
  14. python怎么打开cmd-python中调用cmd
  15. 麻烦不断的国外科技公司排行榜:苹果惨上榜
  16. powerbi 线性回归_精选 | 实用炫酷的Power BI自定义图表
  17. RabbitMQ(七)延迟队列
  18. esxi怎么传输文件到虚拟机_软路由篇2:3865U软路由折腾记——Esxi软虚拟机+OpenWrt教程
  19. 一文带你学会linux系统 史上最全linux命令大全
  20. 数字音乐各种高保真音乐格式

热门文章

  1. bootstrap的学习-基础样式和排版一
  2. pandas加载csv出错:UnicodeDecodeError: ‘utf-8‘ codec can‘t decode byte 0xca in position 0: invalid contin
  3. caffe 损失函数
  4. Mysql优势和特点
  5. [ Linux ] PCF8563数据手册解析 |CSDN创作打卡
  6. FreeRTOS学习,适用于FreeRTOS初学者,FreeRTOS整体知识框架
  7. w10取消自带杀毒服务器,如何关闭win10自带杀毒软件 Windows Defender 看完你就知道了...
  8. 修复花雨庭服务器,我的世界手机版怎么进花雨庭服务器 | 手游网游页游攻略大全...
  9. How to install VMware workstation15pro on Manjaro
  10. 小米最大的竞争对手不是苹果而是华为