前言

组件化开发现在基本上归于基础操作了,咱们一般都会运用 ARouter 、LiveDataBus 作为组件化通信的处理计划,那为什么会挑选ARouter,ARouter又是+ F [ O怎样完成的呢?这篇文章首要就 建立组件化开发的准备工作 、组件化跳转剖析,假如了解了这篇文章,对于检查ARouter源码M t E S U c Q应该是会有很大的协助的。至于ARouter= ? S ! + v } k等剖析,网上有很多的解说,这儿就不剖析ARouter源码了,文章末尾会给出ARouter源码时序图和总结,可疏) s ,忽。j & I g L

ps: 为什么写本文,由于笔者最近被问道,为什么要用ARouF _ . : 3 Nter,K e J LARo; R # I j % ; 0 Suter它到底是处理什么问题,你能就一个点来剖析吗?被问到该问题了?笔者是从它的跳转回答的n * M v H A,究竟跳转简单。刚好记载并回忆一下。

参阅资料

Android c C A APT 实践

谈谈APT和JavaPoet的一些运用技巧和关键

目录

关于Android开发组件化的一些思考

一、组件化优势

组件化的优势想必咱们都知道,能够总结为四点

  • 编译速+ R | L ! e 6 E )度 咱们V R S W c a v 5 }能够按需求测验单一事务模块,而不1 D ` F f * S需求全体打包运转,: 8 C ? % p e节约了实践,有效的提升了咱们的开发速度

  • 解耦 极度的降低了模块之间的耦合,便于后期保护与更新,当产品提出一个新事务时,m r n ^ _ d F (彻底能够新建一个事务组件,集成和摒弃都很便利

  • 功用重用 某一块的功用在别的的组件化项目中运用只需求独自依靠这一模块即可

  • 团队开发效率 组件化架构是团队开发必然会挑选的一种开P 6 K P A 0 r 2 ^发方法,它l i o H能有效的使团队更好的协作

F b U、组件化开发准备工作

组件化开发,一般能够分为三层,分别为 壳工程、事务组件、基础依靠库,事务组件G R x间互不相关,而且事务组件需求能够独自运转测验,全体都是围绕解耦来开展的,下面开端进行组件化开发前所需求做的准备工作

1、包名和资源文件命名冲突问题

需求拟定规范,对包名和项目模块的划分规范化,不同模块内不能有相同名字的类文件+ 3 D * m,避免打包失利等冲突问题

2、Gradle中的0 9 4 p F I Z _ P版别好统一办理

接下来的写法是最普遍的,便S , x J p : ^ s是有点小瑕疵:不支撑 AS 的主动补充功用,也无法运用代码主动盯梢,因此* X Y能够考虑运用 buildSrc。bX e _ ] ( # _uildSrc 是 Android 项目中一个比较特殊的 projed U n L z Gct,在 buildSrc 中能够编写 Groovy 语言。

在咱们创立的模块中,有一些,例如 compileSdkVersion 、buil$ . ^dToolsVersion 或许是集成的第三方依靠库,它们都有对应的版别号,假如不进行统一办理,后续保护很麻烦,总不能对一切模块一个个手动修正版? . r A W别。所以咱们能够在graS I 9 G 4 t T Jdle.properties文件中,增加装备,例如

gradle.properties
CompileSdkVersion = 30// 这儿不能和compileSdkVersion 相同,会报错
​
模块的build.gradle
android{
compileSdkVers$ R } : f t }ion CompileSdkVersion.toInteger()
}

一切模块版别号都依照上面的写,每次改n I o n q ~ & 5版别号都依照gradle4 U Y U v.properties里边定义 Y g E – u的修正就好。可是,细D u r #心的你一定会发现,现在网上的例子,这些写的很少,已然这样写也能做到统一办理,为什么不引N K $ 6呢?答案就在 CompileSdk| B L 9Version.toInteger() n q F *儿,这儿拿到CompileSdkVersion后还需求转换,假如运用下面创立gradle文件的做法,彻底能够省去。

在项目根目录下新建一个conffig.gradle 文件,和大局buil7 p Q md.gradle同一层级

config.gradle
​
ext{
andrR R 1 toid=[
compileSdkVersion:29,
buildToolsVersion:'29.0.2',
t# Z n 2 q 9 R BargetS L j H } D / G =SdkVersion:29,
]
dependencies  = [
appCompact : 'androidx.appcompat:appcompat:1.0.2'
]
}
根目录的build.gradle中,顶部参加
apply from:"config.gradle"
​
运用的时分如8 ! l A I t c下
co+ @ 3 8 8 C TmpileSdkVersion- K B S F a s u rootProject.ext.android.compilec r G 8 T 3SdkVersion
implementation rootProject.ext.dependencies.appCompact

留意,在implementation dependencies 时分是能够这样写的

implementation 'androidx.t? A D { rest.ext:junit:1.1.0''androidx.test.espresso:espresso-core:3.1.1'

可是你在config.gradle中千万不能也相似这样写

 dependencies  = [
appCompact : ''androidx.appcompat:appcompat:1.0.2','androidx% * ; ! W P S ..test.espresso:espresso-cor| H 0 ` p 4e:3.1.1''
]

由于在build.gradle中你把一切依靠放到implZ p S ; K i 9 h ;ementation后面,用逗号分隔,这个逗号和字符串W u ~ 0 R !的逗号不相同V 7 ; X,你在config.gradle中那样写的其实相当于在build.gradle implementation dependencies 时这样写

implementation 'androidx.test.ext:junit:1.1.0,androidx.test.espresso:espresso-core:3.1.1'

那你8 q M J或许会问,这样写不行的话,那我怎样在config.gradle中完成对一切模块需求的Z k 8 m %公共依靠库会集办理呢?能够依照下面这样写

ext {
....
dependencies = [
publia f e ] ncI, t l l v 5 8 Ymplementation: [
'androidx.test, + 3.F 7 dext:junit:1.1.0',
'androidx.tm ] S 0 D z * &est.espresso:espresso-core:3.1.1V w M / o # . c g'
],
appCompact          : 'androidx.appcompat:appcompat:1.0.2'
]
}
​
implementata z , r n Z M 7 Hion rootProject.ext.depenW } K E 8 X f ; ydencies.publicImplementation //每个模块都写上这句话就好了

这样就完了吗?还有咱们自己写的的公共库也要会集办理,一般咱们都会在模块* N 3 S的build.gradle中一个个这样写

implementation project(path: ':basic')

现在咱们经过gradle来办P e z 4 U K # s理,如! u H +

ext {
....
d8 x + q x hependencies = [
other:[
':basic',
]
]
}
​
rootProject.ext.depende3 $ I u Xncies.other.each{
imp ; c ( a t | Z @lementation project(it)
}

3、组件在Application和Library之间随意切换

Library不能在Gradle文件中有applicatioH ! ^ ! A % #nId

A` r E D , u , UndroidManifest.xml文件区别

在开发过程中,需求独立测验,避免不了经常在Application和Library之间随意切换M F a g k w @ D v。在模块,包括壳工程app模块运转时,Appli5 O Ycation类只能有一个。

首先咱们在config.gradle中装备,为什么1 L ( *不在gradle.properties中装备,之前也说了

ext {
android = [
compileSdkVersion: 29,
buildToolsVersion: '29.0.2',
targetSdkVersion : 29,
isApplication:false,
]
....
}

然后在各个模块的b} t v j `uild.gradle文件顶部参加以下判断

if(rootPro( 5 : )ject.ext.android.isApplication) {
apply plugin: 'com.android.application'
}else{
apply plugin: 'coR t K ) t nm.android.liA l 2 f 0 q C q |brary'
}
​
  • Library不能在Gradle文件中有applicationId

android {
defaultConm m n xfig {
if(rootProject.ext.android.isApplication){w O O $ ] m
applicationId "com.cov.moduletest" //作为依靠库,是不能有applicationId的
}
....
}
  • 在app模块的gradle中也需求有区别

 dependencies {
.....
if(!rootProject.eM V n / t { 5 _ txt.android.isApplication){
implementation project(path: ':customer')5  P % v 6  //只有当事务模块是依靠的时分去依靠 ,看事务需求
}
}
  • AndroidManifest.xml文件区别

    在各个模块的build.gradle中区~ [ P Z Y t e

    sourceSets {
main{
if(rootProject.ext.android.isApplication){
manifest.srcFile '/src/main/AndroidManifest.xml'
}else{
manifest.srcFile % z ] k"src/main/manifest/AndroidManif_ F ] y _est.xml"
}
}
}
  • Application装备

由于咱们会在Application中做一些初始化操作,假如模块独自运转的话,那么这些操作需求放到模块的Application中,所以这儿需求独自装备一下,新建module 文件夹,装备好下面+ . a文件时o Q w R Z ) T m,新建自定义的Application类,然后在manifest文件夹下的清单文件内指m d v S W定Application。这样作为依靠库运转时c u + M & K,module 文件夹下的文件不会进行编译。

          main{
if(rootProject.ext.android.isApplication){
manifest.srcFile '/src/main/AndroidManifesI ) 6 s 6t.xml'
}else{
manifest.srcFile "srcB ^ / g 5  H O [/mainW S S/manC H $ifest/Andro: 5 [ a H #idManifest.xml"
java.srcDirs 's ] , Xrc/main/module','sr1  m 7 / ~ pc/main/java'
}
}

以上是o 7 I装备独自模块时,Applicatir B 8 8 Yon能够这样写,但这儿还需求考虑Application的初始化问题,壳工程的Applicatu , m J k l ; ~ 9ion初始化完成后需求分别初始化依靠组件的ApplicationV / f # { J Z。能够这样写

basic 模块中定义
public interface IApp{
vR @ @ =oiq M ) l P A 3 j Pd init(Application app);
}
然后各个模块相似这样写
public AM* 1 ? ; e z C zoduleApplicaW g Ktion implements IApp{
public void init(Application app){ 初始化操作 }
}
在壳工程的Application里保护一个数组 {"com.cv.AModuleApplicat,  mion.class","xx"}
可是这样不高雅,主张在basic中建E D | g个类专门保护
接下来,作为一个独立AP运转P时,只需求在壳工程Application的onCreate办法中对该数组的类悉数进行反射结构,
调用init办法即可。

上面这样写确– v s实能够,仅有缺乏的是需求保护一个 @ G l X包括各个模块作为Library时需求初始m x r E化的类,有没有更好的办法呢?答案肯定是有的,运用注解,对每个模块中,需求在~ YApplicat& W @ ; 7ion初始化调用的类,即上述数组中保护的类,加上注= f B b Q n F 1 C解,编译期收集起来,Application的onCreate办法调用,没了解的同学能够看下面的组件化跳转剖析,道理相似。

4、在Java代码中判断是否独立运转仍是集成运转

在运转时,每个模块都会G / h 3 M M v生成一个对应的BuildP ^ %Config类,B c – E寄存包途径或许不同,那咱们怎样做呢?

在basic模块的build.gradle中参加以下代码

buildTypes {
rj Z Helease {
buildConf~ s A W &igField 'boolel g O ` B Dan', 'isApplication', rootPro! W 4 S I } g X -ject.ext.android.isApplication.toStr[ B ` y m , @ / (ingB ) c()
}
debug {
buil~ x C m ? _ O tdConfigField 'boole6 ? # % O { G t .an', 'isApplication', rootProject.ext.android.isAppli6  # 7cation.toString()
}
}

为什么q U *要在basic模_ | L { ] l ?块下参加呢?便是由于BuildConfig每个模块都会有,总不能在一切模块都参加这句话s Y + – G * m #吧。在basic模块参加后,其它模块6 _ K 2依靠这个模块,然后经过在baI E c L 4 N % ssic模块中定义的BaseActivity中,增加获取该值的办法即可,其他模块承继BaseAy k l A S k D cctivitS a u o Yy,就能够拿D / k 1 1 ,到父类办法进行判断了8 E X /,这仅仅一种,具体要看事务进行剖析。

三、组件化跳转剖析

1、自定义组件化跳转模块

依照上述装~ – | [ n k ^ O k备后,接下啦第一步就需求处理组件化通信问题,其中第一类问题便是跳转相关。由于事务组件y Z J之间不能耦合,所以咱们只能经过自定义一个新的 router 模块,各个事务组件内经过承继该依靠,然后完成跳转。

咱们只需求在roV W *uter模块中定义一个ARouter容器类* 3 | 9 F k G a,然后各个模块进行注册Activity,就能够运用了,代码如下

public clat : M Z n  ] #ss ARouter {
private static ARouter aRouter = new ARouter();
private HashMap<String, Class<? extends Activity>> map = new HashMap<>();
private Context mContext;
​
private ARouter(){
}
public static ARouter getInstance(){
return aRouter;
}
​
public void init(Context context){
this.mContext = context;
}
/**
* 将类目标& j B p q K增加到容器中
* @param key
* @param clazz
*/
pZ h V 2 k b d s ublic void registerActivity(String key,Class&v = | } ~ 5 .lt;?extends  Activ^ 2 3 : A Xity> clazz){
if(key != null && clazz != null && !map.containsKey(key)){
map.put(key,clazz);
}
}
public void navigation(String key){
navigation(key,null);
}
​
public void navigation(String key, Bund] E & ) zle bundle){
if(mContext == null){
reb = o turn;
}
Cla( , S S . 3 f gss<?extends  Activity > clazz = map.get(key);
if(clazz != null){
Intent intent = new Intent(mConu b r z k o @text,clazz);
if(bundle != null){
intent.putExtras(bundle);
}
mContU N ~ jext.startActivity(intent);
}
}
}

经过ARouter.getI/ v p I s @ K tnstance().navigation(“keyQ = d i 6 U ? $ 2“) 就能跳转了,可是条件是需求调用regist{ O t W , perActivity将每个Activity和对应途径注册进来,那不或许在每个A# ? c a N 2 X O Lct. w y s % nivity中都调用该办法将类目标加到ARouter路由表吧?咱们或许会想到在BasicActivity里边加一5 Y / Q %个笼统办法,将一切类目标回来,然后你拿到后调用registerActivity办法注册,可是这个条件是 需求你承继BasicActs 3 = – Nivity的类现已创立了,现已实b M A ^例化了,所以这B ~ 0 | D 1 e /不或许在没发动Activity时进行注册。那怎样样才能在Activity没发动时,将一_ ) . E S F W @, . x w类目标增加到ARouter容器内呢?有什么办法能够在Application创立时分能够收集到一切} 2 y w N E J L未发动的Activity呢?

或许咱们还会想到} c k l : Q I N,在每一个模n k b a w 0块里边新建一个ActivityUtils类,然后定义一个办法,里边调用ARouter.registerActivity ,注册该模块一切需求注册的类,然后在Application类里触发该办法。模块少还好说,能够一个个手动敲,模块一多,每个模块都得写,保护太麻烦了,可C L g不能够主动生成这样的办法,主动找到需求注册的类,收集起来呢?

这就需求运用APT技术来完成了,经过对需求跳转的Activity进行注解,然后在编译时生成类文件及类办法,该类办法内运用Map收集对应的注解了的类,在Application创立时,履行这些类文件相关办法,收集到ARouter容器内。

2、组件 ^ = j化跳转完成计划晋级

J . ? f C # X $了解如何操作APT的同学能够参阅

Androidp ! K APT 实践

谈谈APT和JavaPoa G U / qet的一些运用技巧和关键

要完成上述说的计划,需求了解一下APT(Anno` t ~ & _ ^tation Processing Tool)技术,即注解处理器,它是Javac的一个工具,首要用来在编译时扫描和 A V [ R处理注解。

  • 创立注解,U $ Z B ? t J E对需求注册的Activi{ z | i y ~ Kty类用注解符号 (annotati~ C k ron模块)

    @Target 声明注解的作用域

    @Retention 生命注解的生命周期

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Activityk G e 1 1Path {
    String value();
    }
  • 创立注解处理器 并生成类 (annotation_compiler模块)

    @AutoService(Processor.class) 虚拟e [ ` r机在编译的时分,会经过这个判断AnnotationCompiler是注解处理器, 是固定的写法,加个注解即可,经过auto-service中的@AutoService能够主动生r F Z H A成AutoService注解处理器,用来注册用来生成 META-INF/services/javax.annB M n 0 zotation.processing.Processor 文件

    @S] b `upportedSourceVersion(9 ` q t eSourceVersion.RELEASm ~ o O j C HEZ t M F K D_7) 指定JDK编译版别

    @SupportedAnnota2 { l P ` W ( = ItionTypes({Constant.ACTIVITY_PATH}) 指定注解,这儿填写ActivityPath的类的全限定称号 包名.ActivityPath

    Filer 目标,用来生成Java文件的工具

    Element 官方解说 表明程序元素,如程序包,类或办法,TypeElement表明一个类或接口程序元素,Vas R _ 8 0 SriableElement表明一个字段、枚举常量或结构函数参数、局部变量,TypeParameterElement表明通用类、接口、办法、或结构函数元素的正式类型参数,这儿简单举个例R ! 3 b

    package  com.example  //PackageElement
    public class A{ //TypeEleP q O * N Z { ImeX & p B _ vnt
    private int a;//VariableElement
    po t : crivate A m, S & Q 1 | L )A;//VariableElement
    public A(){}  //0 t R m J C ExecC V n #uteableElement
    public  void setA(int a){ // ExecuteableElement   参数a是VariableElement
    }
    }

    关于Android开发组件化的一些思考还需求留意一点,为了在编译时不出现GBK编码过错等问题,@ 7 8 J X V需求在gradle中增加

    tasks.withType(JavaCompile) {
    options.encoding = 'UTF-8'
    }

    接下来就开端真实完成了,现在annotation_compile的依靠中增加

        implementaZ P & 3tion'com.google.auto.service:auto-service:1.0-rc4'
    annotationProcessor'com.google.auto.service:auto-service:1.0-rcJ : P v r k L4'
    implementation 'com.squareup:javapoet:1.11.1'

    然后完成注解处理器类

    @AutoService(Processor.c2 ` 6lass)
    @SupportedAnm o ZnotM ; Q s & ( S v !ationType. ; Os({Constant.ACTIVITY_PATH})
    // 注解处理器接纳的参数
    @SupportedOptions(Constant.Mw g K [ z D bODULA 7 3 * HE_NAME)
    public class An] L 3 [ jnot` J u _ O j iationCompiler extend= . Os AbstractProcessor {
    ​
    //生成java文件的工具
    private Filer filer;
    private String moudleName;
    ​
    @Override
    public synchronized void init(Pr. u 3 1 xocessingEnvironment processing) Z cEnviron5  b n sment) {[ B 5 x U l m I ]
    super.init(procesR 6  @singEnvironment);
    fi* T J ` l n L c fler = processingEnv.getFiler();
    moudleName = processingEnv.getOptions().geg j F [ s W  (t(Constant.MODx b 2 R & | {ULE_NAME);
    }
    ​
    /**
    * 得到最新的Java版别g { P W 6 T U
    *
    * @return
    */
    @Overj 6 | 1 ` b q , wride
    public SourceVersion getSupportedSourm v  ; W 0ceVersion() {
    return processingEnv.getSourceVersion();s 0 t $ E !
    }
    ​
    /**
    * 找注解 生成类
    *
    * @param set
    * @param roundEnvironment
    * @return
    */
    @Override
    public boolean procy  L Uess(Set<? extends TypeElement> set, RoundEnvi# l } T @ 8ronment rounQ 6 & 5 u  0 & ldEnvironment) {
    if (moudleName == null) {
    return false;
    }
    /q D E I Z K Y/得到模块中符号了ActivityPath的注解
    Set<? extends Element> elements = rou 2 2 ? ,ndEnvironmentd k ( j + w.getElementsAnnotatedWith(ActivityPath.class);
    //寄存 途径 类文件称号
    Map<String, String> map = new HashMap<>();
    //TypeElement 类节点
    for (Element element : elementsa v e F) {
    TypeElement typeElement =h N | M U e l (Typei g H k D #Element) element;
    ActivityPath activityPath = typeElement.getAnnotati, h ! ; {on(Act^ 1 K : 7 G z a uivityPath.class);
    String key = activL V W 4 U (ityPath.value();
    StrT G p R 4 2 &ing activityName = typeElement.getQualifiedName().toString();//得到此类型元素的彻底限定称号
    map.put(key, activityName + ".class");
    }
    ​
    //生成文件
    if (map.size() > 0) {
    createC V 8 5 + u Y 0lassFile(map);
    }
    return false;
    }
    ​
    private void createCla9  Z v LssFile(Map<String^ | d T ) R b d ^, String> m| J = W q a Pap) {
    //1.创立办法
    MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("registerActivity")
    .addModifiers(Modifier.PUBLIC)
    .returns(void.class);
    ​
    IteratoD I 9 N C Y R *r<String> iterator = map3 N T 4.keySet().iterator();
    while (iterator.hasNext())o o ! K c 8 E ) {
    String key = iterator.next();
    String className = map.get(key);
    ​
    //2.增加办法体
    methodBuilder.addStatement(Constant.AROUTER_NAME + Q @ M H X q G H".getInstance().registerActivity("" + key + ""," + className + ")");
    ​
    }
    //3.生成办法
    MethodSpec methodSpec = methodBuilder.build();
    ​
    //4.获取接口类
    ClassName iRouter = Cla= 2 n Z r w F *ssName.get(Constant.PACKAGE_NAME, Cons: | utantF 6 I s.IROUTER);
    //5.创立工具类
    TypeSpec typeSpec = TyH f , F tpeSpec.classBuL A |ilder(Constant.CLASS_NAME + "$$" + moudleName)
    .addModifier? K + = h K h 1 Ns(Modifier.PUBLIC)
    .addSuperinterface(iRouter) //父类
    .addMethodS y  S R 5 h _ r(methodSq % U v * y - Opec) //增加办法
    .build();
    ​
    //6.指定目录构建
    JavaFile javaFile = Java6 h }File.builder(Constant.PACKAGE_NN O ) j l FAME, typeSpec).build();
    ​
    //W 8 v X & v Y m !7.写: V S d # &道文件
    try {
    javaFile.writeToJ 3 6 c(filer7 + r u);
    } catch (IOException e)? & S ` {
    ​
    }
    }
    ​
    ​
    }

    生成的文件作用如下

    public class RouterGroup$$moduletest implements IRouter {
    public void regX - j cisterActivity() {
    com.cv.router.ARouter.getInstance().registerActivity("/main/login",com.cv.moduletest.LoginActivity. d C m q G pclass);
    }
    }
  • k n ! e ~ = E 0 pARouter中完成init办法,触发类文件的办法

       public void init(Context context){
    thW ! Sis.mContext = context;
    //1.得到生成的RouterGroup$$.. 相关文件 找到这些类
    try {
    List<String> clazzes = getClv 6 ~ vassName();
    if(clazzes.size() &r C q egt; 0){
    for(StringR E ] ( a m | 9 = className:clazzes){
    Class<:  [ u j?> activityClazz = Class.foc m E i crName(className);
    if(IRouter.class.isAssignableFrom(activityClazz)){
    //2.是否是IRouter 子类
    IRouter router = (IRouter) activityClazz.newInstance();
    router.registerActivity();
    }
    }
    }
    } catch (Exceptionx h = m A e) {
    e.V [ t 2 V 8 4printStaw ; 6 jckTrace();
    }
    }
    private List<String> getClassName() throws IOException {
    List<String>j t ( ` clazz9 V H l ZList = new ArrayList<>();
    //加载apk存储途0 L . x , b径给DexFile
    DexFile df = new DexFile(mContext.getPackageCod_ x 2ePath());
    Enumeration<String> enumeration = df.entries();
    while (enumeration.hasM1 9 3oreElements()){
    String className = enumeratio[ E ]n.nextElement();
    if(className.contains(Constant.CLAv H ~ & BSS_NAME)){
    clazzList.add(className);
    }
    }
    return clazzList;
    }

    到此就完成了主动化收集类信息。

四、ARouter总结

当你了解了上述办法时,你再去Y | l 4 _ y U y K看ARouter的源码,会轻松点,跳转完成原理,都差不多。当然ARouter也支撑阻拦等功用,想要检查ARouter源码,能够自行在掘金上查找。这儿给出曾经c Q O 1 j ]看ARouter时做的! ) I 7 $ ^ % j /笔记,只针对客户端运用ARouter时的时序图和文字描述,或许总结写得不全不好,不喜勿喷

关于Android开发组件化的一些思考

  1. 首先在ARouter.getInstance().init()中会调用_ARouter的init()办法,然后回调用after办法,after办法是经过byName方法获取的阻拦器Service。

  2. 这儿首要是init()办法,里边会构建一个Handler,首要用来发动运用组件跳转和在de! # n ubug模式下显现提示,然后还有一个线程池,首要是用于履行后续阻拦器阻拦逻辑,然后这个init中,最重要的应该便是LogisticsCenter.init()办法5 b 6 n g ; ,在这儿( q W + 0 u面,他会获取arouter.router包名下的一切类文件名,然后加载到Set调集中,然后遍历这些class/ P ; F $ K,RootJ 1 – X相关的类反射调用loadInto办法加载到groupIndex调集中,Interceptors相关的类加载到interceptorsIndex中,Providers相关的类加载都providersIndN a S g oex中。这些类文件都是arouter-comw B W – I upile依据注解生成的,文件名规则是ARouter $ $Root $$ 模块名或许是ARouter $$Provider $ $模块名,或许是ARouter $$ Group $$ group名,例如R| v )oot相关类的loadInto办法便是把group值和group 相关8 + k i . @类匹配放在grou3 y G n ! +pIndex中,然后在需求运用时再去加groX M N u L i /up相关类p U 7 V I的信息。

  3. 咱们运用ARouter.getInstance().build().navigation获取Fragment或许跳转时,它先是_ARouter的build办法, 这个P ; ]办法里,2 F l q u他会bayType方法调用PathReplaceService,对build()办法传入的途径path做修正,然后假如运用RouterPath注解时没有指定group,会获取path中第一个/后面的字符串作为grouU 0 s – 9 2 { 0 Pp并回来一个Poscard,内部有一个bundle用于接纳传入的参数,然后调用自身的navigax 4 [ y @ n S B Etion办法,最后仍是回调到了 _ARouter的navigation()办法,这个办法内会按需获取加载指定path对应的类信息,首先是从groupIndex里边需求group组名对应的类信息,然后经过反射调用load` / fInto办法,将该组名下的一切途径对应联系保存到routes Map中,然后去完* b 5 k X z –善传入的Path对应的RouteMeta信息,最后依据元信息的类型,构建对应的信息,并指定provider和fragment的敞开绿色通道。然后接下来,便是假如没有敞开绿色通道,将运用CountDownlaunch和线程池将一切阻拦器按需进行处理,然后通行后,会依据元信息类型s – W i f x,结构相应参数,发动Activity或许反射构建Fragment回来。

五、最长公共子字符串

这周被问到一个问题,android列表上显现的一切数据,如何找出最长公V O ] + C –共子标签,我立马想到动态规划,可是总感觉会有更好的完成方法,究竟LCS问题大多都是给定两个字符串,总不能每两个比较后 (O(n2)),再跟第三个、第四个比较,这样时刻复杂度不是很好。最后回过头想想,其实思路应该便是这样的,经过系统API操作~ + 3 u O o n i,也要这样比较。

/**
* str1的长度为M,str2的长度为N,生成大小为M*N的矩阵dp
* dp[i][j] 的意义是str1[0....i] 与 str2[0......j]的公共子序列的长度
* 假如dp[i][0] dp[0][i] 为1 后面就都为1
* @author xxx
*
*/
publu B lic class DemoFive {
//dp[i][j] 的意义是str1[0....s - F L F _ O . _i] 与 str2[0......j]的公共子序列的长度
public static String findLCS(String A,int n,String B) g J 2 Z @ s,int m) {
char[] arrA = A.toCharArray();
char[] arrB = B.toCharAr: ( bray();
// n * m 矩阵6 T j 4 / j ~ ( A * B
int[][] dp = new int[n][m];
​
int length = 0;
int start = 0;
for(int i = 1;i<n;i++) {
for(int j = 1;j<m;j++) {
if(arrA[i]== arrB[j] ) {
dp[i][j]J g k ` = dp[i-1][j-1]+1;
if(dp[i][j] > length) {
length = dp[i][j];
start = i - length+1 ; //留意这儿 下标是从0开端的
}
}
}
}
String ree : } z ` = V } xsult = A.substring(start,start + length);
return result;
}
}

笔记八