手写Butterknife核心代码

95 篇文章 0 订阅

安卓开发者一般都知道ButterKnife,他实现了findViewById,onClick这些无脑操作,释放了程序员的双手,使我们有更多时间去处理逻辑代码。

今天我要手写ButterKnife核心代码实现,简单介绍下ButterKnife的核心原理,在编译时(java文件转class文件过程)使用注解处理器,帮我们写findViewById,通过反射获取这个生成的类,调用这个类的父类接口方法,实现调用子类实现方法。可能现在有点蒙,那撸码后就会清晰不少。

//首先我们建个Java的lib库管理所有注解,命名为annotations,里面建个BindView自定义注解类;

 

 

//再建个Java的lib库,注解处理器,对所有注解的处理,

这里需要在build.grald 依赖几个库实现注解处理器的注解注册,和Log的打印处理,配置编码格式,依赖注解管理库

dependencies {

//注解处理器
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'
//自动生成service,省去了配置resources/META-INF/services,配合@AutoService(Processor.class)使用
compileOnly 'com.google.auto.service:auto-service:1.0-rc4'

//是square推出的开源java代码生成框架,提供Java Api生成.java源文件配置才能看到日志
implementation 'com.squareup:javapoet:1.11.1'

//依赖注解管理库
implementation project(path: ':annotations')
//指定编译的编码
tasks.withType(JavaCompile) {
    options.encoding = "UTF-8"
}

}

//指定编译的编码
tasks.withType(JavaCompile) {
    options.encoding = "UTF-8"
}

//这个库建个AnnotationCompiler类 核心代码,这个类继承AbstactProcessor;

//实现process方法和重新三个方法

 

//核心代码在process方法里面处理,其他三个重新方法,都很简单。

//现在分析下核心方法

 /**
     * 生成代码 的核心逻辑
     *
     * @param roundEnvironment
     */
    private void setAnnotation(RoundEnvironment roundEnvironment) {
        Map<String, List<VariableElement>> map = new HashMap<>();
        //获取使用BindView注解的元素集合
        Set<? extends Element> elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(BindView.class);

        //元素分类保存到map
        for (Element element : elementsAnnotatedWith) {
            //获取元素所在类的类名
            VariableElement variableElement = (VariableElement) element;
            String activityName = variableElement.getEnclosingElement().getSimpleName().toString();

            //获取类里面的注解元素集合
            List<VariableElement> list = map.get(activityName);

            if (list == null) {
                list = new ArrayList<>();
                map.put(activityName, list);
            }

            list.add(variableElement);
        }
        //------------------代码执行到这元素分类结束------------

        //写文件
        if (map.size() > 0) {
            //需要对每个activity单独写一个文件
            Iterator<String> iterator = map.keySet().iterator();

            //写对象
            Writer writer = null;

            while (iterator.hasNext()) {
                //获取类名
                String activityName = iterator.next();

                //获取类名对应 元素集合
                List<VariableElement> list = map.get(activityName);

                //获取元素的包名
                //获取元数包裹成-->类元素
                TypeElement typeElement = (TypeElement) list.get(0).getEnclosingElement();
                String packageName = processingEnv.getElementUtils().getPackageOf(typeElement).toString();

                //创建文件
                try {
                    JavaFileObject sourceFile = filer.createSourceFile(packageName + "." + activityName + "_ViewBinding");
                    //获取写对象
                    writer = sourceFile.openWriter();

//                    package cn.gxy.mybutterknife;
                    writer.write("package " + packageName + ";\n");

//                    import cn.gxy.mybutterknife.IBinder;
                    writer.write("import " + packageName + ".IBinder;\n");
//                    public class MainActivity_ViewBinding implements IBinder<cn.gxy.mybutterknife.MainActivity>{
                    writer.write("public class " + activityName + "_ViewBinding implements IBinder<" + packageName + "." + activityName + ">{\n");
//                        @Override
                    writer.write("@Override\n");
//                        public void bind(cn.gxy.mybutterknife.MainActivity target){
                    //public void bind(final cn.gxy.mybutterknife.MainActivity target){
                    writer.write("public void bind(" + packageName + "." + activityName + " target){\n");
//                            target.tv_text=(android.widget.TextView)target.findViewById(2131165325);

                    //这段代码有几个地方是变的需要获取变动的数据才可以写 ---》tv_text  TextView  2131165325
                    for (VariableElement element : list) {
                        //获取元素名称 --->tv_text
                        String name = element.getSimpleName().toString();

                        //获取元素类型
                        TypeMirror typeMirror = element.asType();

                        //获取元素注解
                        BindView annotation = element.getAnnotation(BindView.class);
                        //通过注解获取id
                        int id = annotation.value();

                        //获取变的数据就可以写了
                        writer.write("target." + name + "=(" + typeMirror + ")target.findViewById(" + id + ");\n");

                    }


                    //  }}
                    writer.write("}\n}");

                    //代码执行到这文件就写完了

                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (writer != null) {
                        try {
                            writer.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }


            }


        }
    }

 

//我们主APP使用依赖注解库和注解管理库;

在buil.grald里面配

dependencies {
//注意注册处理器使用 这个导包annotationProcessor
annotationProcessor project(path: ':annotationCompiler')
//注解管理类
implementation project(path: ':annotations')

}

 

//新建个接口IBinder,代码如下和简单,主要是人注解处理器生成的类实现这接口

//暴露使用者调用的API类,代码也很简单,反射获取生成的类,使用多态调用方法

public class ButterKnife {

    public static void init(Activity activity) {
        //获取注解处理器生成类的 全类名
        String packageName = activity.getClass().getCanonicalName() + "_ViewBinding";
        //反射获取注解处理器 生成
        try {
            Class<?> aClass = Class.forName(packageName);

            IBinder iBinder = (IBinder) aClass.newInstance();
            //多态  调用子类实现方法
            iBinder.bind(activity);

        } catch (Exception e) {
            e.printStackTrace();
        }


    }
}

 

//在MainActivity代码

public class MainActivity extends AppCompatActivity {
    @BindView(R.id.tv_text)
    TextView tv_text;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.init(this);

        tv_text.setText("butterKnife成功");
    }


//    @OnClick(R.id.tv_text)
//    public void onViewClick(View view) {
//        Toast.makeText(this, "测试点击", Toast.LENGTH_SHORT).show();
//
//    }

}

//build下工程自动生成如下图类,说明注解处理器没有问题,就可以运行项目了

 

//如果想实现点击其实就回调MainActivity的方法实现点击,掌握上面实现点击就不难了

源码地址https://github.com/zhudaihao/MyButterknife

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值