Android路由框架ARouter分析

一、路由方案

原生的路由方案缺点:

显式:直接的类依赖,耦合严重

隐式:规则集中式管理,协作困难

Manifest扩展性较差

跳转过程无法控制

失败无法降级

ARouter的优势:

使用注解,实现了映射关系自动注册 与 分布式路由管理

编译期间处理注解,并生成映射文件,没有使用反射,不影响运行时性能

映射关系按组分类、多级管理,按需初始化

灵活的降级策略,每次跳转都会回调跳转结果,避免StartActivity()一旦失败将会抛出运营级异常

自定义拦截器,自定义拦截顺序,可以对路由进行拦截,比如登录判断和埋点处理

支持依赖注入,可单独作为依赖注入框架使用,从而实现 跨模块API调用

支持直接解析标准URL进行跳转,并自动注入参数到目标页面中

支持获取Fragment

支持多模块使用,支持组件化开发

…….

这么多好处,是时候来了解一下 ARouter 了。

二、ARouter框架

上图是根据 ARouter 一次基本的路由导航过程,整理的基本框架图,涉及到主要流程,下面进行详细介绍。

三、路由管理

1.注册

通过注解,在编译时收集使用了注解的类或变量并经过Android Process Tool处理进行统一管理。

包含三种注解@Autowired,@Interceptor,@Route。

@Route

注解定义

String path();//路径URL字符串
String group() default "";//组名,默认为一级路径名;一旦被设置,跳转时必须赋值
String name() default "undefined";//该路径的名称,用于产生JavaDoc
int extras() default Integer.MIN_VALUE;//额外配置的开关信息;譬如某些页面是否需要网络校验、登录校验等
int priority() default -1;//该路径的优先级

实现 @Route 注解

BlankFragment @Route(path = "/test/fragment")
Test1Activity @Route(path = "/test/activity1")

该注解主要用于描述路由中的路径URL信息,使用该注解标注的类将被自动添加至路由表中。

@Autowired

注解定义

boolean required() default false;
String desc() default "No desc.";

实现 @Autowired 注解

@Autowired
int age = 10;
@Autowired
HelloService helloService;

该注解是在页面跳转时参数传递用的。目标Class中使用该注解标志的变量,会在页面被路由打开的时候,在调用 inject() 后自动赋予传递的参数值。

@Interceptor

注解定义

int priority();//该拦截器的优先级
String name() default "Default";//该拦截器的名称,用于产生JavaDoc

实现 @Interceptor 注解

一般应用于IInterceptor的实现类,是路由跳转过程中的拦截器,不分module,应用全局。

@Interceptor(priority = 7)
public class Test1Interceptor implements IInterceptor {
 @Override
 public void process(final Postcard postcard, final InterceptorCallback callback) {
 ............
 }
}

2.收集

在编译期间自动生成映射文件,arouter-compiler实现了一些注解处理器,目标在于生成映射文件与辅助文件。

三种类型的注解处理器,都实现了 AbstractProcessor ,主要功能如下:

首先通过注解处理器扫出被标注的类文件

按照不同种类的源文件进行分类

按照固定的命名格式生成映射文件

这样就可以在运行期初始化的时候通过固定的包名来加载映射文件。

关于注解处理的源码详解见 阿里路由框架--ARouter 源码解析之Compiler。

以官方demo为例,通过注解处理器,按照固定的命名格式生成映射文件。

具体以 ARouter$$Root$$app 为例,看下注解处理器生成的类文件的内容:

public class ARouter$$Root$$app implements IRouteRoot {
 @Override
 public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
 routes.put("service", ARouter$$Group$$service.class);
 routes.put("test", ARouter$$Group$$test.class);
 }
}

通过调用 loadInto() 方法将其管理的 group 类文件加载到集合中,方便后续路由查找。

3.加载

前面的收集都是在编译器处理获得的,那么加载就是到了运行期。 ARouter 为了避免内存和性能损耗,提出了“分组管理,按需加载”的方式。在前面的编译处理的过程中,已经按照不同种类生成对应的映射文件。

以官方demo为示例,一个app模块有一个Root结点,管理各个Group分组,每个Group分组下有着多个界面;此外app模块下还有着Interceptor结点,以及provider结点。

其中Interceptor结点对应于自定义的拦截器,provider结点对应于IOC,以实现跨模块API调用。

ARouter 在初始化的时候只会一次性地加载所有的root结点,而不会加载任何一个Group结点,这样就会极大地降低初始化时加载结点的数量。当某一个分组下的某一个页面第一次被访问的时候,整个分组的全部页面都会被加载进去。

初始加载

ARouter 其实是一个代理类,它的所有函数实现都交给 _ARouter 去实现,两个都是单例模式。

public static void init(Application application) {//静态函数进行初始化,不依赖对象
 if (!hasInit) {
  logger = _ARouter.logger; //持有 日志打印的 全局静态标量
  _ARouter.logger.info(Consts.TAG, "ARouter init start.");//打印 ARouter初始化日志
  hasInit = _ARouter.init(application);//移交 _ARouter去 初始化

  if (hasInit) {
   _ARouter.afterInit();
  }

  _ARouter.logger.info(Consts.TAG, "ARouter init over.");//打印 ARouter初始化日志
 }
}

继续看一下 _ARouter 的初始化方法

protected static synchronized boolean init(Application application) {
  mContext = application;// Application的上下文
  LogisticsCenter.init(mContext, executor);//移交逻辑中心进行初始化,并传入线城池对象
  logger.info(Consts.TAG, "ARouter init success!");//打印日志
  hasInit = true;//标示是否初始化完成

  // It's not a good idea.
  // if (Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
  //  application.registerActivityLifecycleCallbacks(new AutowiredLifecycleCallback());
  // }
  return true;
 }

继续往下走,看 LogisticsCenter 的初始化方法

public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
  mContext = context; //静态持有Application的上下文
  executor = tpe;//静态持有 线城池

  try {
   // These class was generate by arouter-compiler.
   // 通过指定包名com.alibaba.android.arouter.routes,找到所有 编译期产生的routes目录下的类名(不包含装载类)
   List<String> classFileNames = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);

   for (String className : classFileNames) {//组别列表com.alibaba.android.arouter.routes.ARouter\$\$Root
    if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
     // This one of root elements, load root.
     ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
    } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {//模块内的拦截器列表com.alibaba.android.arouter.routes.ARouter\$\$Interceptors
     // Load interceptorMeta
     ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
    } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {//IOC的动作路由列表com.alibaba.android.arouter.routes.ARouter\$\$Providers
     // Load providerIndex
     ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
    }
   }

   if (Warehouse.groupsIndex.size() == 0) {
    logger.error(TAG, "No mapping files were found, check your configuration please!");
   }

   if (ARouter.debuggable()) {
    logger.debug(TAG, String.format(Locale.getDefault(), "LogisticsCenter has already been loaded, GroupIndex[%d], InterceptorIndex[%d], ProviderIndex[%d]", Warehouse.groupsIndex.size(), Warehouse.interceptorsIndex.size(), Warehouse.providersIndex.size()));
   }
  } catch (Exception e) {
   throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
  }
 }

通过上述代码,实现了“分组管理,按需加载”的方式,加载了对应的三个注解处理器生成的类中管理的结点到路由集合中。

其中内存仓库 Warehouse 缓存了全局应用的组别的清单列表、IOC的动作路由清单列表、模块内的拦截器清单列表,3个map对象。

class Warehouse {
 // Cache route and metas
 static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();//组别的列表 包含了组名与对应组内的路由清单列表Class的映射关系
 static Map<String, RouteMeta> routes = new HashMap<>();//组内的路由列表 包含了对应分组下的,路由URL与目标对象Class的映射关系

 // Cache provider
 static Map<Class, IProvider> providers = new HashMap<>(); //缓存IOC 目标class与已经创建了的对象 

 static Map<String, RouteMeta> providersIndex = new HashMap<>();//IOC 的动作路由列表包含了使用依赖注入方式的某class的 路由URL 与class映射关系

 // Cache interceptor
 //模块内的拦截器列表 包含了某个模块下的拦截器 与 优先级的映射关系
 static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
 static List<IInterceptor> interceptors = new ArrayList<>();//已排序的拦截器实例对象

}

四、路由查找

ARouter.getInstance().build("/test/activity2").navigation();</pre>

以上述例子为例,看一下 ARouter 路由查找的过程。首先看一下 build 过程

1. build()

public Postcard build(String path) {
 return _ARouter.getInstance().build(path);
}

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));
  }
 }

其使用了代理类_ARouter的build()并构建和返回PostCard对象。 一个Postcard对象就对应了一次路由请求,作用于本次路由全过程。

这部分代码主要包含两个部分:

  1. 使用 IOC byType()方式寻找PathReplaceService.class接口的实现类,该实现类的作用就是实现 “运行期动态修改路由”。
  2. 继续进行本次路由导航

首先来看一下PathReplaceService.class接口:

public interface PathReplaceService extends IProvider {

 /**
  * For normal path.
  *
  * @param path raw path
  */
 String forString(String path);

 /**
  * For uri type.
  *
  * @param uri raw uri
  */
 Uri forUri(Uri uri);
}

主要包含forString()和forUri两个方法,针对路径进行预处理,实现 “运行期动态修改路由”。

接下下,继续通过build(path, extractGroup(path))进行路由导航,其中extractGroup()是从路径中获取默认的分组信息。

然后build()方法会返回一个Postcard对象,并把对应的路径和分组信息传入该对象。

分析完上面的过程,下面来详细看下PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);中的navigation()方法,该方法实际调用了代理类_ARouter的navigation(Class<? extends T> service)方法。

2. navigation(Class<? extends T> service)

protected <T> T navigation(Class<? extends T> service) {
 try {
  Postcard postcard = LogisticsCenter.buildProvider(service.getName());

  // Compatible 1.0.5 compiler sdk.
  if (null == postcard) { // No service, or this service in old version.
   postcard = LogisticsCenter.buildProvider(service.getSimpleName());
  }

  LogisticsCenter.completion(postcard);
  return (T) postcard.getProvider();
 } catch (NoRouteFoundException ex) {
  logger.warning(Consts.TAG, ex.getMessage());
  return null;
 }
}

首先 LogisticsCenter.buildProvider(service.getName()) 根据 Warehouse 保存的 providersIndex 的信息查找并构建返回一个 PostCard 对象

然后执行 LogisticsCenter.completion(postcard) ,该方法会根据 Warehouse 保存的 routes 的路由信息完善postcard对象,该方法在下面还会出现,到时候具体介绍

再回到上文介绍 ARouter.getInstance().build("/test/activity2").navigation() ,返回 PostCard 对象后,开始调用对应的 navigation() 方法。

3. navigation()

观察 PostCard 中的该方法

public Object navigation() {
  return navigation(null);
 }

 public Object navigation(Context context) {
  return navigation(context, null);
 }

 public Object navigation(Context context, NavigationCallback callback) {
  return ARouter.getInstance().navigation(context, this, -1, callback);
 }

 public void navigation(Activity mContext, int requestCode) {
  navigation(mContext, requestCode, null);
 }

 public void navigation(Activity mContext, int requestCode, NavigationCallback callback) {
  ARouter.getInstance().navigation(mContext, this, requestCode, callback);
 }

最终调用了 ARouter 中的 navigation() 方法,在其中其实是调用了 _ARouter 中的 navigation() 方法。

该方法包含查找回调的调用、降级处理、拦截器处理具体路由操作。

protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
  try {
   LogisticsCenter.completion(postcard);
  } catch (NoRouteFoundException ex) {
   logger.warning(Consts.TAG, ex.getMessage());

   if (debuggable()) { // Show friendly tips for user.
    Toast.makeText(mContext, "There's no route matched!\n" +
      " Path = [" + postcard.getPath() + "]\n" +
      " Group = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show();
   }

   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() {
    /**
     * Continue process
     *
     * @param postcard route meta
     */
    @Override
    public void onContinue(Postcard postcard) {
     _navigation(context, postcard, requestCode, callback);
    }

    /**
     * Interrupt process, pipeline will be destory when this method called.
     *
     * @param exception Reson of interrupt.
     */
    @Override
    public 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;
 }

其中最重要的两个方法就是 LogisticsCenter.completion()_navigation() ,下面详细介绍。

public synchronized static void completion(Postcard postcard) {
 if (null == postcard) {
  throw new NoRouteFoundException(TAG + "No postcard!");
 }
 //根据路径URL获取到路径元信息
 RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
 if (null == routeMeta) { // Maybe its does't exist, or didn't load.
  //可能没加载组内清单路径,从组别的清单列表拿到对应组
  Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup()); // Load route meta.
  if (null == groupMeta) {
   throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
  } else {
   //将该组的组内清单列表加入到内存仓库中,并把组别移除
   try {
    if (ARouter.debuggable()) {
     logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] starts loading, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
    }

    IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
    iGroupInstance.loadInto(Warehouse.routes);
    Warehouse.groupsIndex.remove(postcard.getGroup());

    if (ARouter.debuggable()) {
     logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] has already been loaded, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
    }
   } catch (Exception e) {
    throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
   }

   completion(postcard); // 再次触发完善逻辑
  }
 } else {
  postcard.setDestination(routeMeta.getDestination());//目标 class
  postcard.setType(routeMeta.getType());//路由类
  postcard.setPriority(routeMeta.getPriority());//路由优先级
  postcard.setExtra(routeMeta.getExtra());//额外的配置开关信息

  Uri rawUri = postcard.getUri();
  if (null != rawUri) { // Try to set params into bundle.
   Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);
   Map<String, Integer> paramsType = routeMeta.getParamsType();

   if (MapUtils.isNotEmpty(paramsType)) {
    // Set value by its type, just for params which annotation by @Param
    for (Map.Entry<String, Integer> params : paramsType.entrySet()) {
     setValue(postcard,
       params.getValue(),
       params.getKey(),
       resultMap.get(params.getKey()));
    }

    // Save params name which need auto inject.
    postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));
   }

   // Save raw uri
   postcard.withString(ARouter.RAW_URI, rawUri.toString());
  }

  switch (routeMeta.getType()) {
   case PROVIDER: // if the route is provider, should find its instance
    // Its provider, so it must implement IProvider
    Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
    IProvider instance = Warehouse.providers.get(providerMeta);
    if (null == instance) { // There's no instance of this provider
     IProvider provider;
     try {
      provider = providerMeta.getConstructor().newInstance();
      provider.init(mContext);
      Warehouse.providers.put(providerMeta, provider);
      instance = provider;
     } catch (Exception e) {
      throw new HandlerException("Init provider failed! " + e.getMessage());
     }
    }
    postcard.setProvider(instance);
    postcard.greenChannel(); // Provider should skip all of interceptors
    break;
   case FRAGMENT:
    postcard.greenChannel(); // Fragment needn't interceptors
   default:
    break;
  }
 }
}

该方法就是完善 PostCard ,来实现一次路由导航。

接下来介绍另一个方法 _navigation()

private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
  final Context currentContext = null == context ? mContext : context;

  switch (postcard.getType()) {
   case ACTIVITY://如果是Acitvity,则实现Intent跳转
    // Build intent
    final Intent intent = new Intent(currentContext, postcard.getDestination());
    intent.putExtras(postcard.getExtras());

    // Set flags.
    int flags = postcard.getFlags();
    if (-1 != flags) {
     intent.setFlags(flags);
    } else if (!(currentContext instanceof Activity)) { // Non activity, need less one flag.
     intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    }

    // Navigation in main looper.
    new Handler(Looper.getMainLooper()).post(new Runnable() {
     @Override
     public void run() {
      if (requestCode > 0) { // Need start for result
       ActivityCompat.startActivityForResult((Activity) currentContext, intent, requestCode, postcard.getOptionsBundle());
      } else {
       ActivityCompat.startActivity(currentContext, intent, postcard.getOptionsBundle());
      }

      if ((-1 != postcard.getEnterAnim() && -1 != 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://如果是IOC,则返回目标对象实例
    return postcard.getProvider();
   case BOARDCAST:
   case CONTENT_PROVIDER:
   case FRAGMENT://如果是Fragment,则返回实例,并填充bundle
    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;
 }

至此我们就完成了一次路由跳转。

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

(0)

相关推荐

  • Android ARouter路由框架解析

    一.ARouter介绍及主要应用场景: 1.介绍: 是ARouter是阿里巴巴开源的Android平台中对页面.服务提供路由功能的中间件,提倡的是简单且够用. 2.原生的路由方案存在的问题 首先谈一谈原生的路由方案存在的问题以及为什么需要路由框架.我们所使用的原生路由方案一般是通过显式intent和隐式intent两种方式实现的,而在显式intent的情况下,因为会存在直接的类依赖的问题,导致耦合非常严重:而在隐式intent情况下,则会出现规则集中式管理,导致协作变得非常困难.而且一般而言配置

  • 阿里路由框架ARouter 源码解析之Compiler

    前段时间,公司项目在做组件化重构,过程中当然会有很多痛点. 组件化最重要的是根据项目和业务进行分模块,至于模块的粒度就看大家自己来把控了! 这里要说的就是模块之间的数据传输问题 组件化之后,各个模块不相互依赖,那么怎么相互跳转和传递数据呢? 答案就是通过隐式Intent 的方式来跳转和传递数据. 以往的显示Intent 跳转,会存在类直接依赖的问题,这样会导致耦合性非常严重:相比而言,隐式Intent则不需要类之间的直接依赖,但是会出现规则集中式管理,扩展性比较差. 所以在调研期间就发现阿里开源

  • [Alibaba-ARouter]浅谈简单好用的Android页面路由框架

    开发一款App,总会遇到各种各样的需求和业务,这时候选择一个简单好用的轮子,就可以事半功倍 前言 Intent intent = new Intent(mContext, XxxActivity.class); intent.putExtra("key","value"); startActivity(intent); Intent intent = new Intent(mContext, XxxActivity.class); intent.putExtra(&

  • 浅谈android组件化之ARouter简单使用

    ARouter是阿里巴巴开源出来的一款android路由框架,github地址为 : https://github.com/alibaba/ARouter 至于ARouter的诸多好处我就不介绍了,这里主要讲解在项目组件化下,ARouter的一些简单使用 先贴上工程目录: 工程一共分为4个模块,基础组件app.基础服务(包涵路由服务)basecommonlibrary模块.业务模块libraryone.业务模块librarytwo; 在4个模块的gradle文件当中加入如下代码: android

  • Android路由框架ARouter分析

    一.路由方案 原生的路由方案缺点: 显式:直接的类依赖,耦合严重 隐式:规则集中式管理,协作困难 Manifest扩展性较差 跳转过程无法控制 失败无法降级 ARouter的优势: 使用注解,实现了映射关系自动注册 与 分布式路由管理 编译期间处理注解,并生成映射文件,没有使用反射,不影响运行时性能 映射关系按组分类.多级管理,按需初始化 灵活的降级策略,每次跳转都会回调跳转结果,避免StartActivity()一旦失败将会抛出运营级异常 自定义拦截器,自定义拦截顺序,可以对路由进行拦截,比如

  • Android路由框架ARouter的使用示例

    目录 一.添加依赖和初始化框架 1.添加依赖 1.1.java版本的依赖 1.2.kotlin版本的依赖 2.初始化SDK 二.ARouter的简单使用 1.界面跳转 1.1.Activity界面跳转 1.2.获取fragment实例 1.3.注意事项 2.携带基本参数的界面跳转 3.携带对象的界面跳转 3.1.携带序列化对象的界面跳转 3.2.携带无序列化对象的界面跳转 3.3.携带集合和数组的界面跳转 4.界面跳转回调 5.未用到的知识点 一.添加依赖和初始化框架 1.添加依赖 在需要使用A

  • Android路由框架Router分析详解

    什么是路由?说简单点就是映射页面跳转关系的,当然它也包含跳转相关的一切功能. 路由框架的意义 Android系统已经给我们提供了api来做页面跳转,比如startActivity,为什么还需要路由框架呢?我们来简单分析下路由框架存在的意义: 在一些复杂的业务场景下(比如电商),灵活性比较强,很多功能都是运营人员动态配置的,比如下发一个活动页面,我们事先并不知道具体的目标页面,但如果事先做了约定,提前做好页面映射,便可以自由配置. 随着业务量的增长,客户端必然随之膨胀,开发人员的工作量越来越大,比

  • Android注解框架对比分析

    Java的注解(Annotation)相当于一种标记,在程序中加入注解就等于为程序打上某种标记,标记可以加在包,类,属性,方法,本地变量上.然后你可以写一个注解处理器去解析处理这些注解(人称编译时注解),也可以在程序运行时利用反射得到注解做出相应的处理(人称运行时注解). 开发Android程序时,没完没了的findViewById, setOnClickListener等等方法,已经让大多数开发者头疼不已.好在市面上有所谓的注解框架可以帮助开发者简化一些过程.比较流行的有butterknife

  • 5步教你快速写一个android Router路由框架

    Router路由框架 什么是路由?说简单点就是映射页面跳转关系的,当然它也包含跳转相关的一切功能. 前言 网上大片的路由框架实在太多了,实现的方式都大同小异,通过注解实现路由表:但是在多module开发的时候怎么合并路由表,不同的框架有着自己的解决方案. ARouter:通过类查找进行合并路由表. ActivityRouter:通过注解进行路由表合并. 路由框架的意义 Android系统已经给我们提供了api来做页面跳转,比如startActivity,为什么还需要路由框架呢?我们来简单分析下路

  • 详解Android框架MVVM分析以及使用

    Android MVVM 分析以及使用 首先我们需要知道什么是MVVM,他的功能和优点,以及他的缺点. MVVM是Model-View-ViewModel的简写.它本质上就是MVC 的改进版.MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI 和业务逻辑分开.当然这些事 ViewModel 已经帮我们做了,它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑.微软的WPF带来了新的技术体验,如Silverlight.音频.视频.3D.动画-

  • Android Volley框架全面解析

     Volley简介 我们平时在开发Android应用的时候不可避免地都需要用到网络技术,而多数情况下应用程序都会使用HTTP协议来发送和接收网络数据.Android系统中主要提供了两种方式来进行HTTP通信,HttpURLConnection和HttpClient,几乎在任何项目的代码中我们都能看到这两个类的身影,使用率非常高. 不过HttpURLConnection和HttpClient的用法还是稍微有些复杂的,如果不进行适当封装的话,很容易就会写出不少重复代码.于是乎,一些Android网络

随机推荐