鸿蒙系统APP应用开发初尝试——编译时APT尝试!
背景
本人从事开发工作也有多年,目前坐标湖南长沙,以前在各种平台也发过一些文章但是都没有坚持下来;
我初步规划是写一个完整的项目系列文章期望能坚持下来。
为什么会想到要写呢?
其一、眨眼就到了而立之年,觉得自己记忆力也是下降久做过的东西总是记不起,果然是好记性不如烂笔头。
其二、这么多年白嫖了网上很多的文章,视频,一直觉得应该分享一些东西但总是沉不下心去做。
其三、可能写的不好至少也留下一些东西,也是希望能帮助到一些朋友。
说明
最近在研究鸿蒙系统写了一些简单的东西,鸿蒙的有些UI操作,比如findComponentById,setClickedListener 这些比较繁琐于是就尝试用APT做一个尝试实现一个简单的butterknife。
开始尝试
首先建立一个JAVA项目
如不知道请参考Hello world: 鸿蒙初尝试.
我这里选择了Empty Feature Ability(JAVA)
添加注解模块
添加BindView和OnClick注解
先试试在鸿蒙APP里面添加注解标记
去除不需要的代码
新建布局文件夹
添加布局文件
将这个布局添加引用到MainAbilitySlice
鸿蒙编译器做到修改布局后及时编译。这个时候我们可以做如下操作!
1.要么运行一次。
2.用命令行运行一次Gradle编译命令
3.当然我们不用这么麻烦直接图形化点一下Gradle执行
运行成功后就可以找到了
添加引用注解模块
完善添加注解代码
手动写一个将要生成试试可不可以实现运行效果
添加一个MainAbilitySlice$ViewBinding代码
添加butterknife模块
添加一个反射获取MainAbilitySlice$ViewBinding的类
添加几个断点预备Debug看看
添加butterknife模块引用以及添加调用
模块引用
添加调用
Debug运行试试看
运行结果 证明
点击第二个控件后
证明这样写在鸿蒙系统中也是没有问题的。接下来我们只需要试试用APT生成MainAbilitySlice$ViewBinding类后可不可以调用到就可以了。
试试采用APT生成MainAbilitySlice$ViewBinding
创建butterknife_compiler模块
添加ButterKnifeProcessor
添加需要的包引用
在 entry中添加对于生成的库引用
修改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<Class<? extends Annotation>> getSupportedAnnotations() {
Set<Class<? extends Annotation>> annotations = new LinkedHashSet<>();
annotations.add(BindView.class);
annotations.add(OnClick.class);
return annotations;
}
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> types = new LinkedHashSet<>();
for (Class<? extends Annotation> annotation : getSupportedAnnotations()) {
types.add(annotation.getCanonicalName());
}
return types;
}
@Override
public boolean process(Set<? extends TypeElement> typeElements, RoundEnvironment env) {
Map<Element, List<BuildObject>> elementListMap = findAndParseTargets(env);
for (Map.Entry<Element, List<BuildObject>> entry : elementListMap.entrySet()) {
Element enclosingElement = entry.getKey();
List<BuildObject> 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> 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<Element, List<BuildObject>> findAndParseTargets(RoundEnvironment env) {
Map<Element, List<BuildObject>> elementListMap = new LinkedHashMap<>();
for (Class<? extends Annotation> clazz : getSupportedAnnotations()) {
for (Element element : env.getElementsAnnotatedWith(clazz)) {
try {
Element enclosingElement = element.getEnclosingElement();
List<BuildObject> 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
执行前把之前自己手写的类改个名
执行命令
找找MainAbilitySlice$ViewBinding是否生成
运行一下试试,看能不能正常运行…
这里没有运行结果图…因为崩溃了…
错误是
大概查看了一下崩溃原因…
虽然标记中点位置都生成了但javac里面并没有。
结果
尝试失败!
当然我们可以采取一些其他方式让他打包进去…
但我们现在确实是失败了
具体为什么鸿蒙的IDE为什么没有把他编译进去的原因暂时没有去深究。
如果朋友找到原因或者我有哪个地方弄错了可以联系我!
生成代码的如果一些不懂得可以结合下面这篇文章查看
链接: 搭建Android客户端APP架构——《编译时APT技术》
链接: Javapoet说明
源码
链接: 这个项目的源码地址
吐槽
//TODO 给我感觉写代码远比写文章要轻松....致敬所有写文分享的人
我是先好Demo,再写文章的,写文章的时间远要比写Demo的时间久。
这篇文章主要写了一些简单的鸿蒙 APT的尝试,如有不懂之处或者写得不甚明了的地方可以留言。