鸿蒙系统APP应用开发初尝试——编译时APT尝试!

鸿蒙系统APP应用开发初尝试——编译时APT尝试!

  • 背景
    • 说明
    • 开始尝试
      • 首先建立一个JAVA项目
      • 添加注解模块
      • 先试试在鸿蒙APP里面添加注解标记
        • 去除不需要的代码
        • 新建布局文件夹
        • 添加布局文件
        • 将这个布局添加引用到MainAbilitySlice
        • 运行成功后就可以找到了
        • 添加引用注解模块
        • 完善添加注解代码
      • 手动写一个将要生成试试可不可以实现运行效果
        • 添加一个MainAbilitySlice$ViewBinding代码
        • 添加butterknife模块
        • 添加一个反射获取MainAbilitySlice$ViewBinding的类
        • 添加butterknife模块引用以及添加调用
        • Debug运行试试看
      • 试试采用APT生成MainAbilitySlice$ViewBinding
        • 创建butterknife_compiler模块
        • 执行生成MainAbilitySlice$ViewBinding
        • 找找MainAbilitySlice$ViewBinding是否生成
        • 运行一下试试,看能不能正常运行...
        • 大概查看了一下崩溃原因.....
      • 结果
    • 源码
    • 吐槽

背景

本人从事开发工作也有多年,目前坐标湖南长沙,以前在各种平台也发过一些文章但是都没有坚持下来;
我初步规划是写一个完整的项目系列文章期望能坚持下来。
为什么会想到要写呢?
其一、眨眼就到了而立之年,觉得自己记忆力也是下降久做过的东西总是记不起,果然是好记性不如烂笔头。
其二、这么多年白嫖了网上很多的文章,视频,一直觉得应该分享一些东西但总是沉不下心去做。
其三、可能写的不好至少也留下一些东西,也是希望能帮助到一些朋友。

说明

最近在研究鸿蒙系统写了一些简单的东西,鸿蒙的有些UI操作,比如findComponentById,setClickedListener 这些比较繁琐于是就尝试用APT做一个尝试实现一个简单的butterknife。

开始尝试

首先建立一个JAVA项目

如不知道请参考Hello world: 鸿蒙初尝试.

鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第1张图片
我这里选择了Empty Feature Ability(JAVA)

添加注解模块

鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第2张图片
鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第3张图片

鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第4张图片
添加BindView和OnClick注解
鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第5张图片
鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第6张图片

先试试在鸿蒙APP里面添加注解标记

去除不需要的代码

鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第7张图片

新建布局文件夹

鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第8张图片

添加布局文件

鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第9张图片

将这个布局添加引用到MainAbilitySlice

鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第10张图片
鸿蒙编译器做到修改布局后及时编译。这个时候我们可以做如下操作!
1.要么运行一次。
2.用命令行运行一次Gradle编译命令
3.当然我们不用这么麻烦直接图形化点一下Gradle执行
鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第11张图片

运行成功后就可以找到了

鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第12张图片

添加引用注解模块

鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第13张图片

完善添加注解代码

鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第14张图片

手动写一个将要生成试试可不可以实现运行效果

添加一个MainAbilitySlice$ViewBinding代码

鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第15张图片

添加butterknife模块

鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第16张图片
鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第17张图片

添加一个反射获取MainAbilitySlice$ViewBinding的类

鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第18张图片

添加几个断点预备Debug看看

添加butterknife模块引用以及添加调用

模块引用
鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第19张图片
添加调用
鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第20张图片

Debug运行试试看

鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第21张图片
鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第22张图片
运行结果 证明
鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第23张图片
鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第24张图片
点击第二个控件后
鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第25张图片
鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第26张图片
证明这样写在鸿蒙系统中也是没有问题的。接下来我们只需要试试用APT生成MainAbilitySlice$ViewBinding类后可不可以调用到就可以了。

试试采用APT生成MainAbilitySlice$ViewBinding

创建butterknife_compiler模块

鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第27张图片
添加ButterKnifeProcessor
鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第28张图片
添加需要的包引用
鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第29张图片
在 entry中添加对于生成的库引用
鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第30张图片
修改ButterKnifeProcessor文件

package com.lyl.butterknife.compiler;

import com.google.auto.service.AutoService;
import com.lyl.butterknife.annotations.BindView;
import com.lyl.butterknife.annotations.OnClick;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.*;

@AutoService(Processor.class)
public final class ButterKnifeProcessor extends AbstractProcessor {
    private Filer mFiler;
    private Elements mElementUtils;
    private Messager mMessager;

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

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

    private Set> getSupportedAnnotations() {
        Set> annotations = new LinkedHashSet<>();
        annotations.add(BindView.class);
        annotations.add(OnClick.class);
        return annotations;
    }

    @Override
    public Set getSupportedAnnotationTypes() {
        Set types = new LinkedHashSet<>();
        for (Class annotation : getSupportedAnnotations()) {
            types.add(annotation.getCanonicalName());
        }
        return types;
    }
    @Override
    public boolean process(Set typeElements, RoundEnvironment env) {
        Map> elementListMap = findAndParseTargets(env);
        for (Map.Entry> entry : elementListMap.entrySet()) {
            Element enclosingElement = entry.getKey();
            List viewBindElements = entry.getValue();
            String activityClassNameStr = enclosingElement.getSimpleName().toString();
            ClassName activityClassName = ClassName.bestGuess(activityClassNameStr);
            TypeSpec.Builder classBuilder = TypeSpec.classBuilder(activityClassNameStr + "$ViewBinding")
                    .addModifiers(Modifier.FINAL, Modifier.PUBLIC);

            buildElementConstructorMethod(activityClassName, classBuilder, viewBindElements);
            try {
                String packageName = mElementUtils.getPackageOf(enclosingElement).getQualifiedName().toString();
                JavaFile.builder(packageName, classBuilder.build())
                        .build().writeTo(mFiler);
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println(e.getMessage());
            }
        }

        return true;
    }

    private void buildElementConstructorMethod(ClassName activityClassName, TypeSpec.Builder classBuilder, List buildObject) {
        MethodSpec.Builder constructorMethodBuilder = MethodSpec.constructorBuilder()
                .addParameter(activityClassName, "target",Modifier.FINAL).addModifiers(Modifier.PUBLIC);
        for (BuildObject object : buildObject) {
            assembleConstructorMethod(constructorMethodBuilder, object);
        }
        classBuilder.addMethod(constructorMethodBuilder.build());
    }

    private void assembleConstructorMethod(MethodSpec.Builder constructorMethodBuilder, BuildObject buildObject) {
        System.out.println(buildObject.clazz == BindView.class);
        if (buildObject.clazz == BindView.class) {
            String filedName = buildObject.element.getSimpleName().toString();
            String type = buildObject.element.asType().toString();
            System.out.println(filedName);
            System.out.println(type);
            int resId = buildObject.element.getAnnotation(BindView.class).value();
            constructorMethodBuilder.addStatement("target.$L =($L) target.findComponentById($L)", filedName, type, resId);
        } else if (buildObject.clazz == OnClick.class) {
            String filedName = buildObject.element.getSimpleName().toString();
            String type = buildObject.element.asType().toString();
            System.out.println(filedName);
            System.out.println(type);
            int[] resIds = buildObject.element.getAnnotation(OnClick.class).value();
            System.out.println(resIds);
            for (int resId : resIds) {
                constructorMethodBuilder.addStatement(" target.findComponentById($L).setClickedListener(new ohos.agp.components.Component.ClickedListener() {@Override public void onClick(ohos.agp.components.Component component) {target.$N(component);}})", resId, filedName);
            }
        }
    }


    private Map> findAndParseTargets(RoundEnvironment env) {
        Map> elementListMap = new LinkedHashMap<>();
        for (Class clazz : getSupportedAnnotations()) {
            for (Element element : env.getElementsAnnotatedWith(clazz)) {
                try {
                    Element enclosingElement = element.getEnclosingElement();
                    List elements = elementListMap.get(enclosingElement);
                    BuildObject buildObject = new BuildObject(element, clazz);
                    if (elements == null) {
                        elements = new ArrayList<>();
                    }
                    System.out.println(enclosingElement.getSimpleName());
                    System.out.println(element.getSimpleName());
                    elements.add(buildObject);
                    elementListMap.put(enclosingElement, elements);
                } catch (Exception e) {
                }
            }
        }
        return elementListMap;
    }
}

其中用到的一个BuildObject

package com.lyl.butterknife.compiler;

import javax.lang.model.element.Element;

public class BuildObject {
    Element element;
    Class clazz;

    public BuildObject(Element element, Class clazz) {
        this.element = element;
        this.clazz = clazz;
    }

    public Element getElement() {
        return element;
    }

    public void setElement(Element element) {
        this.element = element;
    }

    public Class getClazz() {
        return clazz;
    }

    public void setClazz(Class clazz) {
        this.clazz = clazz;
    }
}

执行生成MainAbilitySlice$ViewBinding

执行前把之前自己手写的类改个名
鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第31张图片

执行命令
鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第32张图片

找找MainAbilitySlice$ViewBinding是否生成

鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第33张图片

运行一下试试,看能不能正常运行…

这里没有运行结果图…因为崩溃了…
错误是
鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第34张图片

大概查看了一下崩溃原因…

虽然标记中点位置都生成了但javac里面并没有。
鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第35张图片
鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第36张图片

鸿蒙系统APP应用开发初尝试——编译时APT尝试!_第37张图片

结果

尝试失败!
当然我们可以采取一些其他方式让他打包进去…
但我们现在确实是失败了
具体为什么鸿蒙的IDE为什么没有把他编译进去的原因暂时没有去深究。
如果朋友找到原因或者我有哪个地方弄错了可以联系我!
生成代码的如果一些不懂得可以结合下面这篇文章查看
链接: 搭建Android客户端APP架构——《编译时APT技术》
链接: Javapoet说明

源码

链接: 这个项目的源码地址

吐槽


//TODO 给我感觉写代码远比写文章要轻松....致敬所有写文分享的人
我是先好Demo,再写文章的,写文章的时间远要比写Demo的时间久。
这篇文章主要写了一些简单的鸿蒙 APT的尝试,如有不懂之处或者写得不甚明了的地方可以留言。

你可能感兴趣的:(鸿蒙,java,移动开发)