// 拦截器的初始化,会在sdk初始化的时候调用该方法,仅会调用一次
}
}
- 动态注册路由信息 适用于部分插件化架构的App以及需要动态注册路由信息的场景,可以通过 ARouter 提供的接口实现动态注册 路由信息,目标页面和服务可以不标注 @Route 注解,注意:同一批次仅允许相同 group 的路由信息注册
ARouter.getInstance().addRouteGroup(new IRouteGroup() {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put(“/dynamic/activity”, // path
RouteMeta.build(
RouteType.ACTIVITY, // 路由信息
TestDynamicActivity.class, // 目标的 Class
“/dynamic/activity”, // Path
“dynamic”, // Group, 尽量保持和 path 的第一段相同
0, // 优先级,暂未使用
0 // Extra,用于给页面打标
)
);
}
});
// 构建标准的路由请求,并指定分组
ARouter.getInstance().build(“/home/main”, “ap”).navigation();
// 构建标准的路由请求,通过Uri直接解析
Uri uri;
ARouter.getInstance().build(uri).navigation();
// 构建标准的路由请求,startActivityForResult
// navigation的第一个参数必须是Activity,第二个参数则是RequestCode
ARouter.getInstance().build(“/home/main”, “ap”).navigation(this, 5);
// 指定Flag
ARouter.getInstance()
.build(“/home/main”)
.withFlags();
.navigation();
// 获取Fragment
Fragment fragment = (Fragment) ARouter.getInstance().build(“/test/fragment”).navigation();
// 对象传递
ARouter.getInstance()
.withObject(“key”, new TestObj(“Jack”, “Rose”))
.navigation();
// 使用绿色通道(跳过所有的拦截器)
ARouter.getInstance().build(“/home/main”).greenChannel().navigation();
- ARouter.init 时,通过获取
/data/app/包名/base.apk
来筛选出ARouter生成的类,如下图。
- 对于Activity类型,跳转
ARouter.getInstance().build("/login/login").navigation();
,最终执行的是,如下:
**
-
Start activity
-
@see ActivityCompat
*/
private void startActivity(int requestCode, Context currentContext, Intent intent, Postcard postcard, NavigationCallback callback) {
if (requestCode >= 0) { // Need start for result
if (currentContext instanceof Activity) {//启动context 为Activity
ActivityCompat.startActivityForResult((Activity) currentContext, intent, requestCode, postcard.getOptionsBundle());
} else {
// 启动context 为Application 时,不支持requestCode
logger.warning(Consts.TAG, “Must use [navigation(activity, …)] to support [startActivityForResult]”);
}
} else {//启动context 为Application
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);
}
}
- 两个无关的module 如何跳转的呢?我们发现最终执行startActivity时,所用的context为Application,思路是这样的,子module启动另外无关子module时,将执行权,交还给主进程/主程序去处理
- 打开生成路由文档,AROUTER_GENERATE_DOC=“enable”,会生成arouter-map-of-xx.json和3个java文件
// 更新 build.gradle, 添加参数 AROUTER_GENERATE_DOC = enable
// 生成的文档路径 : build/generated/ap_generated_sources/(debug or release)/com/alibaba/android/arouter/docs/arouter-map-of-${moduleName}.json
android {
defaultConfig {
…
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName(), AROUTER_GENERATE_DOC: “enable”]
}
}
}
}
//ARouter映射关系如何生成?Generated出三个文件
//ARouter G r o u p Group Grouplogin
//ARouter P r o v i d e r s Providers Providersloginplugin
//ARouter R o o t Root Rootloginplugin
atlas.put(“/login/login”, RouteMeta.build(RouteType.ACTIVITY, LoginActivity.class, “/login/login”, “login”, new java.util.HashMap<String, Integer>(){{put(“password”, 8); put(“username”, 8); }}, -1, -2147483648));
//map 存映射关系
//static Map<String, RouteMeta> routes = new HashMap<>();
- 以上三个文件是如何生成的呢?APT是Annotation Processing Tool的简称,即注解处理工具,apt是在编译期对代码中指定的注解进行解析,然后做一些其他处理(如通过javapoet生成新的Java文件)ARouter使用了两个库
auto-service
javapoet
,来实现从注解到代码的注入,其中auto-service
为注解处理器的库,javapoet
为代码生成器
首先我们了解一下元注解,meta-annotation(元注解)
- @Target
TYPE, // 类、接口、枚举类
FIELD, // 成员变量(包括:枚举常量)
METHOD, // 成员方法
PARAMETER, // 方法参
CONSTRUCTOR, // 构造方法
LOCAL_VARIABLE, // 局部变量
ANNOTATION_TYPE, // 注解类
PACKAGE, // 可用于修饰:包
TYPE_PARAMETER, // 类型参数,JDK 1.8 新增
TYPE_USE // 使用类型的任何地方,JDK 1.8 新增
- @Retention
SOURCE, 只在本编译单元的编译过程中保留,并不写入Class文件中。
CLASS, 在编译的过程中保留并且会写入Class文件中,但是JVM在加载类的时候不需要将其加载为运行时可见的(反射可见)的注解==是JVM在加载类时反射不可见。
RUNTIME 在编译过程中保留,会写入Class文件,并且JVM加载类的时候也会将其加载为反射可见的注解。
-
@Documented 注解的作用是:描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息.
-
@Inherited 注解的作用是:使被它修饰的注解具有继承性(如果某个类使用了被@Inherited修饰的注解,则其子类将自动具有该注解)
-
通过元注解我们定义自己的注解
注解处理器是一个在javac中的,用来编译时扫描和处理的注解的工具。你可以为特定的注解,注册你自己的注解处理器。到这里,我假设你已经知道什么是注解,并且知道怎么申明的一个注解类型。
一个注解的注解处理器,以Java代码(或者编译过的字节码)作为输入,生成文件(通常是.java文件)作为输出。
-
虚处理器
AbstractProcessor
-
init(ProcessingEnvironment env)
: 【核心】
每一个注解处理器类都必须有一个空的构造函数。然而,这里有一个特殊的init()方法,它会被注解处理工具调用,并输入ProcessingEnviroment
参数。ProcessingEnviroment
提供很多有用的工具类Elements
,Types
和Filer
process(Set< ? extends TypeElement> annotations, RoundEnvironment env)
:【核心】
这相当于每个处理器的主函数main()。你在这里写你的扫描、评估和处理注解的代码,以及生成Java文件
getSupportedAnnotationTypes()
这里你必须指定,这个注解处理器是注册给哪个注解的
getSupportedSourceVersion()
用来指定你使用的Java版本。通常这里返回SourceVersion.latestSupported()
-
APT 所用的代码生成器:JavaPoet is a Java API for generating
.java
source files.(JavaPoet 是一个java api ,为了生成 .java源文件的) -
官方helloworld
MethodSpec main = MethodSpec.methodBuilder(“main”)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(void.class)
.addParameter(String[].class, “args”)
.addStatement(“ T . o u t . p r i n t l n ( T.out.println( T.out.println(S)”, System.class, “Hello, JavaPoet!”)
.build();
TypeSpec helloWorld = TypeSpec.classBuilder(“HelloWorld”)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(main)
.build();
JavaFile javaFile = JavaFile.builder(“com.example.helloworld”, helloWorld)
.build();
javaFile.writeTo(System.out);
- 通过以上可生成以下java 文件
package com.example.helloworld;
public final class HelloWorld {
public static void main(String[] args) {
System.out.println(“Hello, JavaPoet!”);
}
}
JavaPoet
主要api
-
JavaFile 用于构造输出包含一个顶级类的Java文件
-
TypeSpec 生成类,接口,或者枚举
-
MethodSpec 生成构造函数或方法
-
FieldSpec 生成成员变量或字段
-
ParameterSpec 用来创建参数
-
AnnotationSpec 用来创建注解
JavaPoet
主要占位符
- $L(for Literals) 执行结构的字符或常见类型,或TypeSpec, $S(for Strings) 字符, $T(for Types) 类, $N(for Names) 方法 等标识符
L > L> L>S
//1.Pass an argument value for each placeholder in the format string to CodeBlock.add()
. In each example, we generate code to say “I ate 3 tacos”
CodeBlock.builder().add(“I ate $L $L”, 3, “tacos”)
//2.When generating the code above, we pass the hexDigit() method as an argument to the byteToHex() method using $N:
MethodSpec byteToHex = MethodSpec.methodBuilder(“byteToHex”)
.addParameter(int.class, “b”)
.returns(String.class)
.addStatement(“char[] result = new char[2]”)
.addStatement(“result[0] = $N((b >>> 4) & 0xf)”, hexDigit)
.addStatement(“result[1] = $N(b & 0xf)”, hexDigit)
.addStatement(“return new String(result)”)
.build();
//=======================
public String byteToHex(int b) {
char[] result = new char[2];
result[0] = hexDigit((b >>> 4) & 0xf);
result[1] = hexDigit(b & 0xf);
return new String(result);
}
//$T for Types
//We Java programmers love our types: they make our code easier to understand. And JavaPoet is on board. It has rich built-in support for types, including automatic generation of import statements. Just use $T to reference types:
.addStatement(“return new $T()”, Date.class)== return new Date();
- 新建name-annotation javaLib,定义CRoute注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface CRoute {
String path();
}
- 新建name-compiler javaLib
dependencies {
implementation project(path: ‘:TestRouter-annotation’)
annotationProcessor ‘com.google.auto.service:auto-service:1.0-rc7’
compileOnly ‘com.google.auto.service:auto-service-annotations:1.0-rc7’
implementation ‘com.squareup:javapoet:1.8.0’
}
2.@AutoService(Processor.class)
public class TestRouteProcessor extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
//dosomething
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
//dosomething
}
}
- 业务module执行顺序如下
- annotationProcessor project(‘:TestRouter-compiler’)
implementation project(‘:TestRouter-annotation’)
2.添加注解@CRoute(path = “/csetting/csetting”)
3.编译运行
4.业务module apt 生成的java 文件,如下:
public final class C c s e t t i n g C csettingC csettingCcsettingHelloWorld {
public static String holder = “/csetting/csetting:com.cnn.settingplugin.SettingsActivity”;
public static void main(String[] args) {
System.out.println(“Hello, JavaPoet!”);
}
}
- 参考
ARouter-init
方法,写出我们CRouter-init
/**
- Init, it must be call before used router.
*/
public static void init(Application application) {
if (!hasInit) {
CRouter.application=application;
hasInit=true;
try {
getFileNameByPackageName(application, ROUTE_ROOT_PAKCAGE);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- 利用反射获取到注解对应映射关系,并参考ARouter存入HashMap
- 通过隐式启动Activity模拟跳转
- 到此我们模拟出简易版本的ARouter,完整自定义CRouter
/**
-
Created by caining on 7/29/21 16:09
-
E-Mail Address:cainingning@360.cn
*/
public class CRouter {
private volatile static CRouter instance = null;
private volatile static boolean hasInit = false;
private static Application application;
public static final String ROUTE_ROOT_PAKCAGE = “com.cnn.crouter”;
private static Map<String ,String> mapHolder = new HashMap<>();
/**
- Init, it must be call before used router.
*/
public static void init(Application application) {
if (!hasInit) {
CRouter.application=application;
hasInit=true;
try {
getFileNameByPackageName(application, ROUTE_ROOT_PAKCAGE);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
-
Get instance of router. A
-
All feature U use, will be starts here.
*/
public static CRouter getInstance() {
if (!hasInit) {
throw new InitException(“ARouter::Init::Invoke init(context) first!”);
} else {
if (instance == null) {
synchronized (CRouter.class) {
if (instance == null) {
instance = new CRouter();
}
}
}
return instance;
}
}
public void navigation(String path) {
startActivity(path);
}
private void startActivity(String path) {
String classPath
= mapHolder.get(path);
if (!TextUtils.isEmpty(classPath)) {
Intent intent = new Intent();
intent.setClassName(application, classPath);//设置包路径
ActivityCompat.startActivity(application, intent, null);
}else {
Toast.makeText(application, “路径空啦”, Toast.LENGTH_SHORT).show();
}
}
/**
-
通过指定包名,扫描包下面包含的所有的ClassName
-
@param context U know
-
@param packageName 包名
-
@return 所有class的集合
*/
private static Set getFileNameByPackageName(Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException {
final Set classNames = new HashSet<>();
List paths = getSourcePaths(context);
final CountDownLatch parserCtl = new CountDownLatch(paths.size());
for (final String path : paths) {
DefaultPoolExecutor.getInstance().execute(new Runnable() {
@Override
public void run() {
DexFile dexfile = null;
try {
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
最后
在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。
附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-QUp930X6-1711925323404)]
[外链图片转存中…(img-XBpgDnlA-1711925323405)]
[外链图片转存中…(img-Wcki0G07-1711925323406)]
[外链图片转存中…(img-XXNRuyHs-1711925323406)]
[外链图片转存中…(img-gYVIEzlw-1711925323407)]
[外链图片转存中…(img-VtflOQJI-1711925323407)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-svm0WIQz-1711925323407)]
最后
在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。
附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
[外链图片转存中…(img-c8MRpmjL-1711925323408)]