1. 程式人生 > >MRouter-Android路由簡單實現

MRouter-Android路由簡單實現

路由的意義:
1. 模組間解耦,不能在程式碼中寫死Activity類名。
2. 動態配置業務需求,現在都是業務模組化開發了。

1. 註解

我們這次編寫的MRoute主要使用了編譯時註解技術,註解在我們日常使用的框架中都有體現。

執行時註解,主要集合反射來完成功能。

編譯時註解,則主要是在編譯階段生成類,來輔助我們後面實現功能。

關於註解,不做詳細描述。

2. Activity跳轉

正常情況下,我們進行Activity跳轉是這樣的:

Intent intent=new Intent(this,AnotherActivity.class);
startActivity(intent);

使用路由跳轉是這樣的:


MRouter.startActivity(context,url,bundle);

MRouter會根據傳入的引數url來查詢對應的類,然後完成Activity的跳轉。

3. 具體實現

3.1 專案結構

MRoute
|–RouteAnnotation
|
|–RouteProcessor
|
|–RouteApi

3.2 RouteAnnotation

注意該Moudule為Java Library.

RouteAnnotation中定義了MRouter使用的註解Route,具體實現如下:

/**
 * Android路由註解。
 *
 * @author
zhangshuai. */
@Documented @Inherited @Retention(RetentionPolicy.CLASS) @Target(ElementType.TYPE) public @interface Route { String url() default ""; }

要點解釋:
1. 因為使用編譯時註解,因此Route的只存在於class中,不是Runtime。
2. 註解的只能使用在類上。

3.3 RouteCompiler

該Module同樣為Java Library,用於處理註解並生成輔助類。

該專案中定義了註解處理器RouteProcessor,使用了auto-service、javapoet來輔助完成。

下面來看核心類RouteProcessor實現:


import core.zs.routeannotation.Route;

@AutoService(Processor.class)
public class RouteProcessor extends AbstractProcessor {

    private Filer mFiler;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        mFiler = processingEnv.getFiler();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Route.class);
        TypeSpec spec = processElements(elements);
        try {
            if (spec != null) {
                // 生成Java檔案
                // RouteMap.java
                JavaFile.builder("core.zs.route", spec).build().writeTo(mFiler);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return true;
    }

    private TypeSpec processElements(Set<? extends Element> elements) {
        if (elements == null || elements.size() == 0) {
            return null;
        }

        // 1. 構造引數,引數為activityMap
        // 引數型別為:HashMap<String,Class<?>>
        ParameterizedTypeName mapTypeName =
                ParameterizedTypeName.get(ClassName.get(HashMap.class), ClassName.get(String
                        .class), ClassName.get(Class.class));
        ParameterSpec mapSpec = ParameterSpec.builder(mapTypeName, "activityMap").build();

        // 2. 構造方法,方法名為initActivityMap
        // 引數為activityMap
        MethodSpec.Builder initMethodBuilder =
                MethodSpec.methodBuilder("initActivityMap").addModifiers(Modifier.PUBLIC,
                        Modifier.STATIC).addParameter(mapSpec);
        // 3. 處理使用Route註解的類資訊
        for (Element element : elements) {
            Route route = element.getAnnotation(Route.class);
            String url = route.url();
            if (null != url && !"".equals(url)) {
                // 在initActivityMap中新增語句
                // activityMap.put(url,xxx.class);
                initMethodBuilder.addStatement("activityMap.put($S,$T.class)", url, ClassName.get
                        ((TypeElement) element));
            }
        }
        // 返回我們構造的類元素
        return TypeSpec.classBuilder("RouteMap").addMethod(initMethodBuilder.build())
                .addModifiers(Modifier.PUBLIC).build();
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> set = new LinkedHashSet<>();
        set.add(Route.class.getCanonicalName());
        return set;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }
}

使用處理器後,會在project的app/build/generated/source/apt/debug/專案名 下生產一個RouteMap.java檔案。

其內容如下:

package core.zs.router;

import java.lang.Class;
import java.lang.String;
import java.util.HashMap;

public class RouteMap {
  public static void initActivityMap(HashMap<String, Class> activityMap) {
    activityMap.put("/core/zs/TestActivity",TestActivity.class);
  }
}

3.4 RouteApi

注意該Module為Android Library,該Module主要是封裝一些常用的api,供其他項使用。

只有一個類MRouter。


package core.zs.routeapi;

/**
 * Created by ZhangShuai on 2018/6/26.
 */

public class MRouter {

    public static HashMap<String, Class<?>> activityMap = new HashMap<>();

    public static void init(Context context) {
        try {
            // 通過反射來呼叫RouteMap的initActivityMap方法,將資料儲存到activityMap中
            Class clz = Class.forName("core.zs.route.RouteMap");
            if (clz != null) {
                Method initMapMethod = clz.getMethod("initActivityMap", HashMap.class);
                initMapMethod.invoke(null, activityMap);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    // 替換常規的startActivity方法
    public static void startActivity(Context context, String url, Bundle bundle) {
        if (context == null || url == null) {
            return;
        }
        Class atClazz = activityMap.get(url);
        if (atClazz != null) {
            System.out.println(atClazz.getCanonicalName());
            Intent intent = new Intent(context, atClazz);
            if (bundle != null) {
                intent.putExtras(bundle);
            }
            context.startActivity(intent);
        }else{
            System.out.println("=====Activity is null.");
        }


    }
}

3.5 如何使用

  1. 在Application中初始化
public class MyApp extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        MRouter.init(this);
    }
}
  1. 在Activity上使用Route註解。
@Route(url = "/core/zs/route/RouteActivity")
public class RouteActivity extends AppCompatActivity{
    //....
}
  1. 進行跳轉
MRouter.startActivity(MainActivity.this, "/core/zs/route/RouteActivity", null);

4. 總結

隨著專案的不斷變化,模組化開發、元件化開發逐步被引入,使用MRouter能很好的幫你實現業務之間的解耦,實現業務之間的簡單配置、順利跳轉。