• 使用javapoet优雅地编写注解解释代码


    使用annotationProcessor在编译期间,获取相关注解数据,然后动态生成java源文件,以避免手工编写大量有规律性的重复代码

    DeepLink分发实例

    核心库(注解类以及其它代码)

    创建一个 java 库 (deeplink),并新建一个注解类

    @Retention(RetentionPolicy.CLASS)
    @Target(ElementType.TYPE)
    public @interface DeepLink {
    
        String value();
    
    }
    

    注解处理库

    创建一个 java 库 (deeplink-compiler),添加下列依赖库

        //google 提供的自动注册 processor 的库
        implementation 'com.google.auto.service:auto-service:1.0-rc2'
        //强大的 java 文件生成库
        implementation 'com.squareup:javapoet:1.8.0'
        //引用注解库
        implementation project(':deeplink')
        
    

    新建一个类,继承自 AbstractProcessor,并给类加上 @AutoService(Processor.class) 注解,实现或者重写以下方法

    @AutoService(Processor.class)
    public class DeepLinkProcessor extends AbstractProcessor {
    
        private static final String DEEPLINK_PACKAGE = "com.wenkiwu.deeplink";
        private static final String DEEPLINK_LOADER = "DeepLinkLoader";
        private static final String DEEPLINK_DISPATCH = "DeepLinkDispatch";
    
        private static final ClassName ANDROID_INTENT = ClassName.get("android.content", "Intent");
        private static final ClassName ANDROID_ACTIVITY = ClassName.get("android.app", "Activity");
        private static final ClassName ANDROID_URI = ClassName.get("android.net", "Uri");
    
        private static final ClassName CLASS_DEEPLINKENTRY = ClassName.get(DeepLinkEntry.class);
        private static final ClassName CLASS_DEEPLINKLOADER = ClassName.get(DEEPLINK_PACKAGE, DEEPLINK_LOADER);
        private static final ClassName CLASS_LIST = ClassName.get(Collections.class);
        private static final ClassName CLASS_ARRAY = ClassName.get(Arrays.class);
    
        private static final Class<DeepLink> DEEP_LINK_CLASS = DeepLink.class;
    
        private Elements elementUtils;
        private Filer filer;
        private Messager messager;
    
        @Override
        public Set<String> getSupportedAnnotationTypes() {
            Set<String> stringSet = new HashSet<>();
            stringSet.addAll(super.getSupportedAnnotationTypes());
            stringSet.add(DEEP_LINK_CLASS.getName());
            return stringSet;
        }
    
        @Override
        public SourceVersion getSupportedSourceVersion() {
            return SourceVersion.latestSupported();
        }
    
        @Override
        public synchronized void init(ProcessingEnvironment processingEnv) {
            super.init(processingEnv);
            elementUtils = processingEnv.getElementUtils();
            filer = processingEnv.getFiler();
            messager = processingEnv.getMessager();
        }
    
        @Override
        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    
            Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(DeepLink.class);
            List<Pair<String, TypeElement>> entryList = new ArrayList<>();
            for (Element element : elements) {
                TypeElement typeElement = (TypeElement) element;
    
                DeepLink deepLink = element.getAnnotation(DeepLink.class);
                String path = deepLink.value();
                Pair<String, TypeElement> pair = new Pair<>(path, typeElement);
                entryList.add(pair);
            }
    
            try {
                generateLoaderClass(entryList);
                generateDispatchClass();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            return false;
        }
    
    
        private void generateDispatchClass() throws IOException {
            MethodSpec constructor = MethodSpec.constructorBuilder()
                    .addModifiers(Modifier.PUBLIC)
                    .addStatement("this.loader = new DeepLinkLoader()")
                    .build();
    
            MethodSpec dispatch = MethodSpec.methodBuilder("dispatchFrom")
                    .addModifiers(Modifier.PUBLIC)
                    .returns(void.class)
                    .addParameter(ANDROID_ACTIVITY, "activity")
                    .beginControlFlow("if(activity == null)")
                    .addStatement("throw new NullPointerException($S)", "activity is null")
                    .endControlFlow()
                    .addStatement("$T sourceIntent = activity.getIntent()", ANDROID_INTENT)
                    .addStatement("$T uri = sourceIntent.getData()", ANDROID_URI)
                    .beginControlFlow("if(uri != null)")
                    .addStatement("$T uriString = uri.toString()", String.class)
                    .addStatement("$T deepLinkEntry = loader.parseUri(uriString)", CLASS_DEEPLINKENTRY)
                    .beginControlFlow("if (deepLinkEntry != null)")
                    .addStatement("Intent newIntent = new Intent(activity, deepLinkEntry.getActivityClass())")
                    .beginControlFlow("if(sourceIntent.getExtras() != null)")
                    .addStatement("newIntent.putExtras(sourceIntent.getExtras())")
                    .endControlFlow()
                    .addStatement("newIntent.setData(sourceIntent.getData())")
                    .addComment("add some custom data")
                    .addStatement("activity.startActivity(newIntent)")
                    .endControlFlow()
                    .endControlFlow()
                    .build();
    
            FieldSpec loader = FieldSpec.builder(CLASS_DEEPLINKLOADER, "loader", Modifier.PRIVATE)
                    .build();
    
            TypeSpec deepLinkDispatch = TypeSpec.classBuilder(DEEPLINK_DISPATCH)
                    .addModifiers(Modifier.PUBLIC)
                    .addField(loader)
                    .addMethod(constructor)
                    .addMethod(dispatch)
                    .build();
    
            JavaFile.builder(DEEPLINK_PACKAGE, deepLinkDispatch).build().writeTo(filer);
        }
    
        private void generateLoaderClass(List<Pair<String, TypeElement>> entryList) throws IOException {
            CodeBlock.Builder initializer = CodeBlock.builder()
                    .add("$T.unmodifiableList($T.asList(\n", CLASS_LIST, CLASS_ARRAY)
                    .indent();
            int totalEntrys = entryList.size();
            for (int i = 0; i < totalEntrys; i++) {
                Pair<String, TypeElement> pair = entryList.get(i);
                ClassName activity = ClassName.get(pair.second);
                String path = pair.first;
                initializer.add("new DeepLinkEntry($S, $T.class)$L\n",
                        path, activity, (i < totalEntrys - 1) ? "," : "");
            }
            FieldSpec registry = FieldSpec.builder(
                    ParameterizedTypeName.get(List.class, DeepLinkEntry.class),
                    "REGISTRY", Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
                    .initializer(initializer.unindent().add("))").build())
                    .build();
    
            MethodSpec parseMethod = MethodSpec.methodBuilder("parseUri")
                    .addModifiers(Modifier.PUBLIC)
                    .addParameter(String.class, "uri")
                    .returns(DeepLinkEntry.class)
                    .beginControlFlow("for (DeepLinkEntry entry : REGISTRY)")
                    .beginControlFlow("if (uri.startsWith(entry.getPath()))")
                    .addStatement("return entry")
                    .endControlFlow()
                    .endControlFlow()
                    .addStatement("return null")
                    .build();
    
            TypeSpec deepLinkLoader = TypeSpec.classBuilder(DEEPLINK_LOADER)
                    .addModifiers(Modifier.PUBLIC)
                    .addField(registry)
                    .addMethod(parseMethod)
                    .build();
    
            JavaFile.builder(DEEPLINK_PACKAGE, deepLinkLoader)
                    .build()
                    .writeTo(filer);
        }
    
    }
    

    使用测试

    在android项目中引入 deeplink 库和 deeplink-compiler 注解处理器

        implementation project(':deeplink')
        annotationProcessor project(':deeplink-compiler')
    

    在各个需要跳转的 Activity 上添加 DeepLink 注解,将不同的 uri 导向不同的 Activity

    @DeepLink("acdef://wenkiwu.com/a/")
    public class ActivityA extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_a);
        }
    }
    
    @DeepLink("acdef://wenkiwu.com/b/")
    public class ActivityB extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_b);
        }
    }
    
    @DeepLink("acdef://wenkiwu.com/c/")
    public class ActivityC extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_c);
        }
    }
    

    做完以上工作后,选择 ReBuild Project,即可在项目的 app/build/generated/source/apt 目录下看到生成的 DeepLinkDispatchDeepLinkLoader 代码

    public class DeepLinkLoader {
      private static final List<DeepLinkEntry> REGISTRY = Collections.unmodifiableList(Arrays.asList(
        new DeepLinkEntry("acdef://wenkiwu.com/a/", ActivityA.class),
        new DeepLinkEntry("acdef://wenkiwu.com/b/", ActivityB.class),
        new DeepLinkEntry("acdef://wenkiwu.com/c/", ActivityC.class)
      ));
    
      public DeepLinkEntry parseUri(String uri) {
        for (DeepLinkEntry entry : REGISTRY) {
          if (uri.startsWith(entry.getPath())) {
            return entry;
          }
        }
        return null;
      }
    }
    
    
    public class DeepLinkDispatch {
      private DeepLinkLoader loader;
    
      public DeepLinkDispatch() {
        this.loader = new DeepLinkLoader();
      }
    
      public void dispatchFrom(Activity activity) {
        if(activity == null) {
          throw new NullPointerException("activity is null");
        }
        Intent sourceIntent = activity.getIntent();
        Uri uri = sourceIntent.getData();
        if(uri != null) {
          String uriString = uri.toString();
          DeepLinkEntry deepLinkEntry = loader.parseUri(uriString);
          if (deepLinkEntry != null) {
            Intent newIntent = new Intent(activity, deepLinkEntry.getActivityClass());
            if(sourceIntent.getExtras() != null) {
              newIntent.putExtras(sourceIntent.getExtras());
            }
            newIntent.setData(sourceIntent.getData());
            // add some custom data
            activity.startActivity(newIntent);
          }
        }
      }
    }
    

    最后写一个 Activity 来处理 deeplink 的分发

    public class DispatchActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            DeepLinkDispatch deepLinkDispatch = new DeepLinkDispatch();
            deepLinkDispatch.dispatchFrom(this);
    
            finish();
        }
    }
    

    注意配置 AndroidMainfest.xml ,使用 DispatchActivity 来接收 scheme 的拉起

        <activity android:name=".DispatchActivity"
            android:theme="@android:style/Theme.NoDisplay">
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE"/>
                <data android:scheme="acdef"/>
            </intent-filter>
        </activity>
    

    运行项目,在 adb shell 中测试一下

    am start -W -a android.intent.action.VIEW -d "acdef://wenkiwu.com/b/test?k=123"
    

    发现会直接跳转到 ActivityB

  • 相关阅读:
    判断一棵二叉树是否为二叉搜索树
    分离链接法的删除操作函数
    线性探测法的查找函数
    Bzoj1251 序列终结者
    POJ2396 Budget
    Bzoj3531: [Sdoi2014]旅行
    Codeforces Round #389 Div.2 E. Santa Claus and Tangerines
    Codeforces Round #389 Div.2 D. Santa Claus and a Palindrome
    Codeforces Round #389 Div.2 C. Santa Claus and Robot
    Codeforces Round #389 Div.2 B. Santa Claus and Keyboard Check
  • 原文地址:https://www.cnblogs.com/wenhui92/p/9264245.html
Copyright © 2020-2023  润新知