Android组件化路由实践
Android应用组件化各个组件页面之间要实现跳转使用路由是一个很好的选择。本文将实现一个比较轻量级的路由组件,主要涉及以下知识:
- Annotation (声明路由目标信息)
- AnnotationProcessor (处理注解)
- JavaPoet (生成Java文件)
- UriMatcher (匹配Uri)
本文将使用Java注解来实现一个简单的路由组件,主要从这几方面来讲解:
- 注解定义与使用
- 注解跳转服务
- 使用AnnotationProcessor处理注解、生成文件
- Uri的匹配
- 安全参数
- 注解跳转服务的开发
由于使用AnnotationProcessor,所以整个路由可分为以下模块:
- lib-component-router (Android工程)
- lib-component-router-annotation (存放注解)
- lib-component-router-compiler (注解处理)
注解定义
由于我们的路由组件相对简单,主要定义以下注解:
- UriDestination (声明路由目标信息)
- DestinationUri (定义Uri路径)
- DestinationArgument (参数声明)
声明目标路由注解
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface UriDestination {
String name();
DestinationUri uri();
DestinationArgument[] out() default {};
DestinationArgument[] in() default {};
}
该注解主要用来注解Activity,声明一个路由目标的信息,各参数说明如下:
- authority (匹配Uri authority)
- scheme (匹配Uri scheme)
- path (匹配Uri路径)
- out (输出参数)
- in (输入参数)
路由Uri注解
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.ANNOTATION_TYPE)
public @interface DestinationUri {
String authority() default "imxingzhe.com";
String scheme() default "xingzhe";
String path() default "";
}
该路由主要用于声明路由目标的Uri信息,各参数说明如下:
- authority (匹配android.net.Uri authority)
- scheme (匹配android.net.Uri scheme)
- path (匹配路由路径信息)
路由参数注解
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.ANNOTATION_TYPE)
public @interface DestinationArgument {
String key();
boolean require() default false;
Class<?> type();
}
该注解主要用于声明路由的输入、输出参数信息,各参数说明如下:
- key (参数的名称)
- require (是否是必需的参数)
- type (参数的类型)
路由组件功能实现
目标Action
public interface DestinationAction {
Context getContext();
int getFlags();
int getRequestCode();
boolean getUriOnly();
Bundle getArguments();
}
Action可理解为一次跳转动作,使用端通过Builder模式生成Action实例,然后再通过DestinationService执行给定的动作。
跳转服务逻辑
public interface DestinationService {
void start(DestinationAction destinationAction);
}
此接口只包含一个start方法用于执行DestinationAction逻辑。主要实现跳转方式是使用类式ContentProvider的UriMatcher方式来实现。首先声明一个抽象Service:
public abstract class AbstractUriDestinationService implements DestinationService {
private final static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
private static boolean isDestinationDefinitionResolved;
@Override
public void start(DestinationAction ){
...
}
...
protected abstract List<DestinationDefinition> getDestinationDefinitions();
}
方法getDestinationDefinitions由子类来实现,主要提供此路由目标的相关信息, DestinationDefinition类如下:
public class DestinationDefinition {
private final String name;
private final Class<?> destination;
private final List<DestinationArgumentDefinition> inArgumentDefinitions;
private final List<DestinationArgumentDefinition> outArgumentDefinitions;
public DestinationDefinition(String name, Class<?> destination, List<DestinationArgumentDefinition> inArgumentDefinitions, List<DestinationArgumentDefinition> outArgumentDefinitions) {
this.name = name;
this.destination = destination;
this.inArgumentDefinitions = inArgumentDefinitions;
this.outArgumentDefinitions = outArgumentDefinitions;
}
public String getName() {
return name;
}
public Class<?> getDestination() {
return destination;
}
public List<DestinationArgumentDefinition> getInArgumentDefinitions() {
return inArgumentDefinitions;
}
public List<DestinationArgumentDefinition> getOutArgumentDefinitions() {
return outArgumentDefinitions;
}
}
AbstractUriDestinationService类中的start方法实现真正的跳转逻辑:
public abstract class AbstractUriDestinationService implements DestinationService {
private final static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
private static boolean isDestinationDefinitionResolved;
@Override
public void start(DestinationAction destinationAction) {
List<DestinationDefinition> destinationDefinitions = getDestinationDefinitions();
resolveDestinationDefinition(destinationDefinitions);
Context context = destinationAction.getContext();
if (context == null) {
throw new IllegalArgumentException("content == null");
}
PackageManager packageManager = context.getPackageManager();
if (destinationAction instanceof UriDestinationAction) {
Uri uri = ((UriDestinationAction) destinationAction).getUri();
int index = matcher.match(uri);
if (UriMatcher.NO_MATCH == index || index >= destinationDefinitions.size()) {
throw new IllegalStateException("Not found destination for : " + uri);
}
DestinationDefinition destinationDefinition = destinationDefinitions.get(index);
List<DestinationArgumentDefinition> destinationArgumentDefinitions = destinationDefinition.getInArgumentDefinitions();
for (DestinationArgumentDefinition argumentDefinition : destinationArgumentDefinitions) {
Bundle args = destinationAction.getArguments();
if (argumentDefinition.isRequire() && !args.containsKey(argumentDefinition.getKey())) {
throw new IllegalArgumentException("No such key: " + argumentDefinition.getKey());
}
}
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(uri);
if (packageManager.resolveActivity(intent, 0) == null) {
if (destinationAction.getUriOnly()) {
throw new IllegalStateException("Not found activity for : " + uri);
} else {
intent = new Intent(context, destinationDefinition.getDestination());
if (packageManager.resolveActivity(intent, 0) == null) {
throw new IllegalStateException("Not found activity for : " + uri);
}
}
}
intent.addFlags(destinationAction.getFlags());
Bundle args = destinationAction.getArguments();
if (args != null) {
intent.putExtras(args);
}
if (context instanceof Activity) {
((Activity) context).startActivityForResult(intent, destinationAction.getRequestCode());
} else {
context.startActivity(intent);
}
} else {
throw new IllegalStateException("Not support operate");
}
}
private static void resolveDestinationDefinition(List<DestinationDefinition> destinationDefinitions) {
if (isDestinationDefinitionResolved) {
return;
}
int index = 0;
for (DestinationDefinition destinationDefinition : destinationDefinitions) {
if (destinationDefinition instanceof UriDestinationDefinition) {
Uri uri = ((UriDestinationDefinition) destinationDefinition).getUri();
String stringForUri = uri.toString();
String path = uri.getPath();
int pathIndex = stringForUri.indexOf(path);
if (pathIndex != -1) {
path = stringForUri.substring(
pathIndex,
stringForUri.length()
);
}
matcher.addURI(uri.getAuthority(), path, index++);
}
}
isDestinationDefinitionResolved = true;
}
protected abstract List<DestinationDefinition> getDestinationDefinitions();
}
这样通过实现AbstractUriDestinationService类,提供相应的DestinationDefinition就可以实现路由的跳转功能,由于使用的注册我们可以使用AnnotationProcessor来处理注解生成DestinationService的实现类。
源码下载: https://github.com/yjwfn/AndroidRouterSample
《架构文摘》每天一篇架构领域重磅好文,涉及一线互联网公司应用架构(高可用、高性 能、高稳定)、大数据、机器学习等各个热门领域。
Android组件化路由实践的更多相关文章
- Android 组件化最佳实践 ARetrofit 原理
本文首发于 vivo互联网技术 微信公众号 https://mp.weixin.qq.com/s/TXFt7ymgQXLJyBOJL8F6xg作者:朱壹飞 ARetrofit 是一款针对Android ...
- Android组件化最佳实践 ARetrofit原理
ARetrofit原理讲原理之前,我想先说说为什么要ARetrofit.开发ARetrofit这个项目的思路来源其实是Retrofit,Retrofit是Square公司开发的一款针对Android网 ...
- Android组件化开发实践
转载请注明出处:http://blog.csdn.net/crazy1235/article/details/76533115 http://mdsa.51cto.com/art/201707/544 ...
- Android组件化
附:Android组件化和插件化开发 App组件化与业务拆分那些事 Android项目架构之业务组件化 Android组件化核心之路由实现 Android组件化开发实践
- Android组件化框架设计与实践
在目前移动互联网时代,每个 APP 就是流量入口,与过去 PC Web 浏览器时代不同的是,APP 的体验与迭代速度影响着用户的粘性,这同时也对从事移动开发人员提出更高要求,进而移动端框架也层出不穷. ...
- 我所理解的Android组件化之通信机制
之前写过一篇关于Android组件化的文章,<Android组件化框架设计与实践>,之前没看过的小伙伴可以先点击阅读.那篇文章是从实战中进行总结得来,是公司的一个真实项目进行组件化架构改造 ...
- 教你打造一个Android组件化开发框架
*本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 CC:Component Caller,一个android组件化开发框架, 已开源,github地址:https://github ...
- 得到、微信、美团、爱奇艺APP组件化架构实践
一.背景 随着项目逐渐扩展,业务功能越来越多,代码量越来越多,开发人员数量也越来越多.此过程中,你是否有过以下烦恼? 项目模块多且复杂,编译一次要5分钟甚至10分钟?太慢不能忍? 改了一行代码 或只调 ...
- Android组件化demo实现以及遇坑分享
首先贴出demo的github地址:GitHub - TenzLiu/TenzModuleDemo: android组件化demo 作者:TenzLiu原文链接:https://www.jianshu ...
随机推荐
- Android Studio安卓学习笔记(二)Android项目结构
上一篇代码,我们学习了Android的功能以及如何用Android Studio开发第一个安卓程序.下面就要介绍Android项目结构.为日后学习打基础. 一:Android项目结构 打开MyFris ...
- Spring Cloud开发人员如何解决服务冲突和实例乱窜?(IP实现方案)
一.背景 在我上一篇文章<Spring Cloud开发人员如何解决服务冲突和实例乱窜?>中提到使用服务的元数据来实现隔离和路由,有朋友问到能不能直接通过IP来实现?本文就和大家一起来讨论一 ...
- 【selenium】- 自动化框架环境搭建
本文由小编根据慕课网视频亲自整理,转载请注明出处和作者. 1. 环境搭建 本课程选用的是selenium + java. 2. java环境的搭建 环境变量配置: 以win10为例,打开控制面板& ...
- ZOJ - 3962 - Seven Segment Display-17省赛-数位DP
传送门:Seven Segment Display 题意:求一个给定区间每个数字的消耗值的和: 思路:数位DP,有点区间和的思想,还有就是这个十六进制,可以用%llx读,还是比较难的: 还有就是到最大 ...
- CodeForces round 520 div2
A:A Prank 题意:给定一个递增序列, 问最多能删除多少个连续数字,要求删除数字之后能还原成原来的数列. 题解:直接找就好了,为了方便可以使得第0个数字为0, 第n+1个元素为1001 代码: ...
- LeetCode380 常数时间插入、删除和获取随机元素
LeetCode380 常数时间插入.删除和获取随机元素 题目要求 设计一个支持在平均 时间复杂度 O(1) 下,执行以下操作的数据结构. insert(val):当元素 val 不存在时,向集合中插 ...
- Marrkdown基础用法
目录 前言 markdown简介 用法列表 标题 字符效果和横线 引用 锚点与链接 代码高亮 图片 有序列表&无序列表 表格 特殊符号与颜色处理 markdown进阶技巧 参考文章 前言 因为 ...
- Vert.x 之 HelloWorld
Hello World 欢迎来到Vert.x的世界,相信您在接触Vert.x的同时,迫不及待想动手试一试,如您在学习计算机其它知识一样,总是从Hello World开始,下面我们将引导您制作一个最基本 ...
- NOIP要炸?
今天起床,翻我的群,突然看见一条消息: “NOIP要被禁赛了!” 莫名奇妙啊...... 于是我就进去看了看,网上疯传,搞得跟真的一样,差点吓到我了. 但好在每个人心中都有一个阿Q,会精神胜利法,于是 ...
- JAVA集合框架包含的内容
Java集合框架提供了一套性能优良.使用方便的接口和类,他们位于java.util包中. Collection接口 主要有List.Set等实现类,Map接口主要有HashMap.TreeMap等实现 ...