上幾篇組件化專題文章,我們對路由框架基本實現了,Activity之間當跳轉和模塊間當通信。
現在思考一個問題: 不管是在Activity或者Fragment,跳轉都會傳遞一些參數,然后在對應對Activity或著Fragment獲取對應對參數,都要寫大量重復的代碼,然而我們作為一名有素質有夢想的碼農,我們是不可以做這種搬磚的操作的,不想當大牛當碼農不是好碼農。
那么我們如何用優雅的代碼,解決這個問題呢?請看下面分解
路由動態注入跳轉參數
原生的獲取intent跳轉傳遞過來的參數:
Intent intent = getIntent();
final String path = intent.getStringExtra(
"path");
......
復制代碼同時還有fragment的參數傳遞等。
如果稍微進行一些封裝的話可以這樣寫:
public class MainActivityExtra {public static void loadExtra(Main2Activity main2Activity) {Intent intent = main2Activity.getIntent();main2Activity.path = intent.getStringExtra(
"path");......}
}
復制代碼上述不管哪種寫法,都是相當麻煩和大量的重復代碼。
最為一名有夢想的碼農,我理想中的寫法:
在相應的Activity,通過一個注解就可以拿到跳轉傳遞過來的參數的值,然后直接使用。
@Extra
public String path;
復制代碼那么我們如何實現呢? 其實很簡單,我們通過注解拿到父類Activity,然后注解變量的類型和名稱,然后我們動態生成一個類,通過原生的方式來實現參數獲取。
其實跟上幾篇的文章是一樣的原理,這里就不再多復述了這是鏈接組件化專題
下面就直接一堆代碼,拋給你
首先聲明注解@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)
public @interface Extra {String name() default
"";
}
復制代碼注解處理器處理注解,這里面的邏輯其實非常簡單,主要是看你對APT使用是不是熟練@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes({Consts.EXTRA_NAME})
@SupportedOptions({Consts.ARGUMENTS_NAME})
public class ExtraProcessor extends AbstractProcessor {/*** key:類節點 value:需要注入的屬性節點集合*/private Map<TypeElement, List<Element>> extraMap = new HashMap<>();private Elements elementUtils;private Types
typeUtils;private Filer filer;private Log
log;private Messager messager;@Overridepublic synchronized void init(ProcessingEnvironment processingEnvironment) {super.init(processingEnvironment);elementUtils = processingEnvironment.getElementUtils();
typeUtils = processingEnvironment.getTypeUtils();filer = processingEnvironment.getFiler();messager = processingEnvironment.getMessager();
log = Log.newLog(messager);}@Overridepublic boolean process(Set<? extends TypeElement>
set, RoundEnvironment roundEnvironment) {
if (null !=
set && !set.isEmpty()) {//注意這里必須要判斷set.isEmpty() 否則會走兩遍log.i(
"process");//拿到注有Extra 的節點集合Set<? extends Element> elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(Extra.class);
if (null != elementsAnnotatedWith) {try {saveExtras(elementsAnnotatedWith);generateExtras();} catch (Exception e) {e.printStackTrace();}}
return true;}
return false;}/*** 保存節點的信息** @param elementsAnnotatedWith*/private void saveExtras(Set<? extends Element> elementsAnnotatedWith) {
for (Element element : elementsAnnotatedWith) {//獲取注解父類的節點類型TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
if (extraMap.containsKey(enclosingElement)) {extraMap.get(enclosingElement).add(element);}
else {List<Element> list = new ArrayList<>();list.add(element);extraMap.put(enclosingElement, list);}}}private MethodSpec.Builder builder;private TypeMirror parcelableType;private TypeMirror iServiceType;private TypeElement extraElement;private TypeMirror activity;/*** 生成表*/private void
generateExtras() {//支持Activity和Fragmentactivity = elementUtils.getTypeElement(Consts.Activity).asType();extraElement = elementUtils.getTypeElement(Consts.Extra);parcelableType = elementUtils.getTypeElement(Consts.PARCELABLE).asType();iServiceType = elementUtils.getTypeElement(Consts.Service).asType();log.i(
"generateExtras");//參數ParameterSpec target = ParameterSpec.builder(TypeName.OBJECT,
"target").build();
if (!Utils.isEmpty(extraMap)) {//遍歷被Extra注解屬性的類
for (Map.Entry<TypeElement, List<Element>> entry : extraMap.entrySet()) {TypeElement classType = entry.getKey();log.i(
"isSubtype:" +
typeUtils.isSubtype(classType.asType(), activity) +
" | " + classType.asType());
if (
typeUtils.isSubtype(classType.asType(), activity)) {generateClass(target, entry, classType);}
else {throw new RuntimeException(
"[Just Support Activity Field] : " + classType +
" --> " +
typeUtils.isSubtype(classType.asType(), activity));}}}}public void generateClass(ParameterSpec target, Map.Entry<TypeElement, List<Element>> entry, TypeElement classType) {builder = MethodSpec.methodBuilder(Consts.EXTRA_METHOD_NAME)//方法名.addAnnotation(Override.class)//注解.addModifiers(Modifier.PUBLIC)//作用域.addParameter(target);//添加參數log.i(
"generate method");//設置函數體addFuncationBody(entry, classType);log.i(
"generate type");//生成Java類TypeSpec
typeSpec = TypeSpec.classBuilder(classType.getSimpleName() +
"$$Extra").addSuperinterface(ClassName.get(extraElement)).addModifiers(Modifier.PUBLIC).addMethod(builder.build()).build();try {//生成Java類文件log.i(
"package name:" + ClassName.get(classType).packageName());JavaFile.builder(ClassName.get(classType).packageName(),
typeSpec).build().writeTo(filer);} catch (IOException e) {e.printStackTrace();log.i(
"package name error");}}private void addFuncationBody(Map.Entry<TypeElement, List<Element>> entry, TypeElement classType) {
for (Element element : entry.getValue()) {//
$T t = (
$T)targetbuilder.addStatement(
"$T t = ($T)target", classType, classType);//遍歷注解節點 生成函數體TypeMirror
typeMirror = element.asType();//獲取注解節點的類型//獲取TypeKind 枚舉類型的序列號int
type =
typeMirror.getKind().ordinal();//獲取屬性名String fieldName = element.getSimpleName().toString();//獲取注解的值String extraName = element.getAnnotation(Extra.class).name();//判斷直接的值為空的情況下的處理extraName = Utils.isEmpty(extraName) ? fieldName : extraName;String defaultValue =
"t." + fieldName;String statement = defaultValue +
" = t.getIntent().";
if (
type == TypeKind.BOOLEAN.ordinal()) {statement +=
"getBooleanExtra($S, " + defaultValue +
")";}
else if (
type == TypeKind.BYTE.ordinal()) {statement +=
"getByteExtra($S, " + defaultValue +
")";}
else if (
type == TypeKind.SHORT.ordinal()) {statement +=
"getShortExtra($S, " + defaultValue +
")";}
else if (
type == TypeKind.INT.ordinal()) {statement +=
"getIntExtra($S, " + defaultValue +
")";}
else if (
type == TypeKind.LONG.ordinal()) {statement +=
"getLongExtra($S, " + defaultValue +
")";}
else if (
type == TypeKind.CHAR.ordinal()) {statement +=
"getCharExtra($S, " + defaultValue +
")";}
else if (
type == TypeKind.FLOAT.ordinal()) {statement +=
"getFloatExtra($S, " + defaultValue +
")";}
else if (
type == TypeKind.DOUBLE.ordinal()) {statement +=
"getDoubleExtra($S, " + defaultValue +
")";}
else {//數組類型
if (
type == TypeKind.ARRAY.ordinal()) {addArrayStatement(statement, fieldName, extraName,
typeMirror, element);}
else {//ObjectaddObjectStatement(statement, fieldName, extraName,
typeMirror, element);}
return;}log.i(
"generate statement:" + statement);builder.addStatement(statement, extraName);}}/*** 添加對象 String/List/Parcelable** @param statement* @param extraName* @param
typeMirror* @param element*/private void addObjectStatement(String statement, String fieldName, String extraName,TypeMirror
typeMirror,Element element) {//Parcelable
if (
typeUtils.isSubtype(
typeMirror, parcelableType)) {statement +=
"getParcelableExtra($S)";}
else if (
typeMirror.toString().equals(Consts.STRING)) {statement +=
"getStringExtra($S)";}
else if (
typeUtils.isSubtype(
typeMirror, iServiceType)) {
// TestService
testService = (TestService) DNRouter.getInstance().build(
"/main/service1")
// .navigation();
//
testService.test();statement =
"t." + fieldName +
" = ($T) $T.getInstance().build($S).navigation()";builder.addStatement(statement, TypeName.get(element.asType()), Consts.ROUTER,extraName);
return;}
else {//ListTypeName
typeName = ClassName.get(
typeMirror);//泛型
if (
typeName instanceof ParameterizedTypeName) {//list 或 arraylistClassName rawType = ((ParameterizedTypeName)
typeName).rawType;//泛型類型List<TypeName>
typeArguments = ((ParameterizedTypeName)
typeName).typeArguments;
if (!rawType.toString().equals(Consts.ARRAYLIST) && !rawType.toString().equals(Consts.LIST)) {throw new RuntimeException(
"Not Support Inject Type:" +
typeMirror +
" " +element);}
if (
typeArguments.isEmpty() ||
typeArguments.size() != 1) {throw new RuntimeException(
"List Must Specify Generic Type:" +
typeArguments);}TypeName
typeArgumentName =
typeArguments.get(0);TypeElement
typeElement = elementUtils.getTypeElement(
typeArgumentName.toString());// Parcelable 類型
if (
typeUtils.isSubtype(
typeElement.asType(), parcelableType)) {statement +=
"getParcelableArrayListExtra($S)";}
else if (
typeElement.asType().toString().equals(Consts.STRING)) {statement +=
"getStringArrayListExtra($S)";}
else if (
typeElement.asType().toString().equals(Consts.INTEGER)) {statement +=
"getIntegerArrayListExtra($S)";}
else {throw new RuntimeException(
"Not Support Generic Type : " +
typeMirror +
" " +element);}}
else {throw new RuntimeException(
"Not Support Extra Type : " +
typeMirror +
" " +element);}}builder.addStatement(statement, extraName);}/*** 添加數組** @param statement* @param fieldName* @param
typeMirror* @param element*/private void addArrayStatement(String statement, String fieldName, String extraName, TypeMirror
typeMirror, Element element) {//數組switch (
typeMirror.toString()) {
case Consts.BOOLEANARRAY:statement +=
"getBooleanArrayExtra($S)";
break;
case Consts.INTARRAY:statement +=
"getIntArrayExtra($S)";
break;
case Consts.SHORTARRAY:statement +=
"getShortArrayExtra($S)";
break;
case Consts.FLOATARRAY:statement +=
"getFloatArrayExtra($S)";
break;
case Consts.DOUBLEARRAY:statement +=
"getDoubleArrayExtra($S)";
break;
case Consts.BYTEARRAY:statement +=
"getByteArrayExtra($S)";
break;
case Consts.CHARARRAY:statement +=
"getCharArrayExtra($S)";
break;
case Consts.LONGARRAY:statement +=
"getLongArrayExtra($S)";
break;
case Consts.STRINGARRAY:statement +=
"getStringArrayExtra($S)";
break;default://Parcelable 數組String defaultValue =
"t." + fieldName;//object數組 componentType獲得object類型ArrayTypeName arrayTypeName = (ArrayTypeName) ClassName.get(
typeMirror);TypeElement
typeElement = elementUtils.getTypeElement(arrayTypeName.componentType.toString());//是否為 Parcelable 類型
if (!
typeUtils.isSubtype(
typeElement.asType(), parcelableType)) {throw new RuntimeException(
"Not Support Extra Type:" +
typeMirror +
" " +element);}statement =
"$T[] " + fieldName +
" = t.getIntent()" +
".getParcelableArrayExtra" +
"($S)";builder.addStatement(statement, parcelableType, extraName);builder.beginControlFlow(
"if( null != $L)", fieldName);statement = defaultValue +
" = new $T[" + fieldName +
".length]";builder.addStatement(statement, arrayTypeName.componentType).beginControlFlow(
"for (int i = 0; i < " + fieldName +
"" +
".length; " +
"i++)").addStatement(defaultValue +
"[i] = ($T)" + fieldName +
"[i]",arrayTypeName.componentType).endControlFlow();builder.endControlFlow();
return;}builder.addStatement(statement, extraName);}
}
復制代碼其實上面的注解處理器,會動態生成這樣的一個類
public class Main2Activity$
$Extra implements IExtra {@Overridepublic void loadExtra(Object target) {Main2Activity t = (Main2Activity)target;t.path = t.getIntent().getStringExtra(
"path");}
}
復制代碼通過一個API去加載這個類 public void loadExtra(Activity activity) {String name = activity.getClass().getName();IExtra iExtra = extraLruCache.get(name);try {
if (iExtra == null) {iExtra = (IExtra) Class.forName(activity.getClass().getName() +
"$$Extra").getConstructor().newInstance();}iExtra.loadExtra(activity);extraLruCache.put(name, iExtra);} catch (Exception e) {e.printStackTrace();}}
復制代碼具體的調用實現public class ModuleTest2Activity extends AppCompatActivity {@Extra(name =
"path")public String url;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
setContentView(R.layout.activity_module_test2);PrimRouter.getInstance().inject(this);TextView module2_textView = findViewById(R.id.module2_textView);
// Intent intent = getIntent();
// final String path = intent.getStringExtra(
"path");module2_textView.setText(
"我是module2,我的地址是:/module2/test. 我是被地址:" + url +
" 調起的");}
}
復制代碼這樣處理不僅大大提高了編碼效率還減少了大量重復的代碼,也提高了程序的可讀性.
實現fragment的跳轉
這里我們需要在原來的基礎上,加幾句代碼。代碼地址
//拿到fragment的全類名
TypeElement fragment = elementUtils.getTypeElement(Consts.Fragment);TypeElement v4Fragment = elementUtils.getTypeElement(Consts.V4Fragment);//單個的節點
for (Element element : annotatedWith) {// 獲取類信息 如Activity類TypeMirror
typeMirror = element.asType();// 獲取節點的注解信息Router annotation = element.getAnnotation(Router.class);log.i(
typeMirror +
" | " + activity.asType());//只能指定的類上面使用
if (
typeUtils.isSubtype(
typeMirror, activity.asType())) {//存儲路由相關的信息routerMeta = new RouterMeta(RouterMeta.Type.ACTIVITY, annotation, element);}
else if (
typeUtils.isSubtype(
typeMirror, service.asType())) {//存儲路由相關的信息routerMeta = new RouterMeta(RouterMeta.Type.SERVICE, annotation, element);}
else if (
typeUtils.isSubtype(
typeMirror, fragment.asType()) ||
typeUtils.isSubtype(
typeMirror, v4Fragment.asType())) {//存儲路由相關的信息routerMeta = new RouterMeta(RouterMeta.Type.FRAGMENT, annotation, element);}
else {throw new RuntimeException(
"Just Support Activity Router!");}//檢查是否配置group如果沒有配置 則從path中截取組名checkRouterGroup(routerMeta);}
復制代碼然后在我們的API庫,返回fragment
case FRAGMENT:Class<?> fragment = jumpCard.getDestination();try {Object instance = fragment.getConstructor().newInstance();
if (instance instanceof Fragment) {((Fragment) instance).setArguments(jumpCard.getExtras());}
else if (instance instanceof android.support.v4.app.Fragment) {((android.support.v4.app.Fragment) instance).setArguments(jumpCard.getExtras());}
return instance;} catch (Exception e) {e.printStackTrace();}
復制代碼實現效果如下:
到此為止,我們的路由框架實現了以下功能: 支持直接解析標準URL路由地址進行跳轉,并自動注入參數到目標頁面中 支持多模塊工程使用 支持模塊間的通信 支持獲取其他模塊的fragment
Android的組件化專題: 組件化配置
APT實戰
路由框架原理
模塊間的業務通信
總結
以上是生活随笔為你收集整理的Android组件化专题-路由动态注入跳转参数以及获取其他模块的fragment的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。