zoukankan      html  css  js  c++  java
  • ButterKnife 原理解析

    一、使用方法

      1、添加依赖。

      

    implementation 'com.jakewharton:butterknife:8.8.1'
    annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'

      2、使用。

    public class MainActivity extends AppCompatActivity {
        // 1、控件id绑定
        @BindView(R.id.myBtn)
        Button myBtn;
    
        Unbinder unbinder = null;
        // 2、点击事件绑定
        @OnClick(R.id.myBtn)
        public void click() {
            Toast.makeText(this,"btn click",Toast.LENGTH_SHORT).show();
            myBtn.setText("hello world");
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            //3、activity注册
            unbinder = ButterKnife.bind(this);
        }
    
        @Override
        protected void onDestroy() {
            //4、activity 取消注册
            unbinder.unbind();
            super.onDestroy();
        }
    }

      3、编译运行。

    二、原理解析

      很明显的我们可以看出,ButterKnife.bind(this)   是 activity和ButterKnife建立关系的地方,我们从这里入手分析。

    ----->>点击进入    bind

    public static Unbinder bind(@NonNull Activity target) {
        //获取decorView 就是页面的跟布局View 本质 是FrameLayout
      View sourceView = target.getWindow().getDecorView();
    // 获取unbinder    
      return createBinding(target, sourceView);
    }
     

    ---->> 点击进入  createBinding

      private static Unbinder createBinding(@NonNull Object target, @NonNull View source) {
        Class<?> targetClass = target.getClass();
        if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());
        Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);
    
        if (constructor == null) {
          return Unbinder.EMPTY;
        }
    
        //noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
        try {
          return constructor.newInstance(target, source);
        } catch (IllegalAccessException e) {
          throw new RuntimeException("Unable to invoke " + constructor, e);
        } catch (InstantiationException e) {
          throw new RuntimeException("Unable to invoke " + constructor, e);
        } catch (InvocationTargetException e) {
          Throwable cause = e.getCause();
          if (cause instanceof RuntimeException) {
            throw (RuntimeException) cause;
          }
          if (cause instanceof Error) {
            throw (Error) cause;
          }
          throw new RuntimeException("Unable to create binding instance.", cause);
        }
      }

     主要的过程就是

    生成constructor 先findBindingConstructorForClass

      如果找不到就 返回  Unbinder.EMPTY,这里的Unbinder.EMPTY就是new Unbinder() 然后直接结束函数 ,不做处理,

      如果得到constructor就 调用constructor.newInstance得到一个unbinder返回,我们稍微看一下newInstance 返回的是一个泛型,至于是在何时传入的泛型,我们先保留下来。

     public T newInstance(Object ... initargs)
            throws InstantiationException, IllegalAccessException,
                   IllegalArgumentException, InvocationTargetException
        {
            if (serializationClass == null) {
                return newInstance0(initargs);
            } else {
                return (T) newInstanceFromSerialization(serializationCtor, serializationClass);
            }
        }

    我们继续探查他是如何得到construtor的

    ---->>点击进入 findBindingConstructorForClass

      @Nullable @CheckResult @UiThread
      private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
      // 1、先从BINDINGS 里边获取construtor
     Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
        if (bindingCtor != null) {
          if (debug) Log.d(TAG, "HIT: Cached in binding map.");
          return bindingCtor;
        }
    
        String clsName = cls.getName();
        if (clsName.startsWith("android.") || clsName.startsWith("java.")) {
          if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
          return null;
        }
        try {
     // 2、如果没有就,利用反射生成construtor
          Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
          //noinspection unchecked
      bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class);
          if (debug) Log.d(TAG, "HIT: Loaded binding class and constructor.");
        } catch (ClassNotFoundException e) {
          if (debug) Log.d(TAG, "Not found. Trying superclass " + cls.getSuperclass().getName());
          bindingCtor = findBindingConstructorForClass(cls.getSuperclass());
        } catch (NoSuchMethodException e) {
          throw new RuntimeException("Unable to find binding constructor for " + clsName, e);
        }
    // 3、将生成的construtor 放入BINDINGS做备份
        BINDINGS.put(cls, bindingCtor);
        return bindingCtor;
      }

    我们重点看,生成construtor这一段:

    String clsName = cls.getName();

    Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");

    bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class);

      1、这里向Contrutor传入Unbinder 泛型,这就能够解释newInstance的返回值是Unbinder。

      2、这里泛型使用的类名(clsName + "_ViewBinding")中,竟然包含了我们传进来的类名,这里完整的类名就是MainActivity_ViewBinding   ,能够反射获取实例,说明这个类是确实存在的,二我们并没有编写相关的类,而在app运行过程更不可能产生类,那就只能是,app运行之前 由ButterKnife 生成的

    我们不妨看看这个类里有什么:

    public class MainActivity_ViewBinding implements Unbinder {
      private MainActivity target;
      private View view2131165258;
      @UiThread
      public MainActivity_ViewBinding(MainActivity target) {
        this(target, target.getWindow().getDecorView());
      }
      @UiThread
      public MainActivity_ViewBinding(final MainActivity target, View source) {
        this.target = target;
        View view;
        view = Utils.findRequiredView(source, R.id.myBtn, "field 'myBtn' and method 'click'");
        target.myBtn = Utils.castView(view, R.id.myBtn, "field 'myBtn'", Button.class);
        view2131165258 = view;
        view.setOnClickListener(new DebouncingOnClickListener() {
          @Override
          public void doClick(View p0) {
            target.click();
          }
        });
      }
      @Override
      @CallSuper
      public void unbind() {
        MainActivity target = this.target;
        if (target == null) throw new IllegalStateException("Bindings already cleared.");
        this.target = null;
        target.myBtn = null;
        view2131165258.setOnClickListener(null);
        view2131165258 = null;
      }
    }

     从代码中我们看到了 我们view的id以及activity中的变量名,可以联想到,是我们添加Bind注解时传进来的。

    ---->>findRequiredView我们可以猜测出是 寻找控件使用的。我们可以看一看, 

     public static View findRequiredView(View source, @IdRes int id, String who) {
        View view = source.findViewById(id);
        if (view != null) {
          return view;
        }
        String name = getResourceEntryName(source, id);
        throw new IllegalStateException("Required view '"
            .......
      }

        我们终于看到了  findViewById。

    ---->>  castView  传入 view  传入 class  ,很明显是转型使用的。

      还有一个问题就是ButterKnife 如何能够在运行前根据我们的代码 ,生成相应的  _ViewBinding  文件的,请继续看。

    三、注解处理器

      在添加依赖时我们还添加了一个,annotationProcessor,就是完成呢些文件生成的。

    
    
    annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
       这其中涉及到了annotationProcessor技术,和 APT(Annotation Processing Tool)技术,他是一种注解处理器,
    在项目编译期可以对源代码进行扫描,找出存活时间为RetentionPolicy.CLASS的指定注解,然后对注解进行解析处理。
      至于后边java类的生成,涉及到了JavaPoet技术



      这里我们先看用注解处理器收集类信息的过程,之前我们已经在app的 build.gradle引入了 ButterKnife 的注解处理器: butterknife-compiler,其中有一个ButterKnifeProcessor 类完成了注解处理器的核心逻辑。

    @AutoService(Processor.class)
    public final class ButterKnifeProcessor extends AbstractProcessor {
      
        @Override
        public synchronized void init(ProcessingEnvironment env) {
            super.init(env);
            String sdk = env.getOptions().get(OPTION_SDK_INT);
            ......
            debuggable = !"false".equals(env.getOptions().get(OPTION_DEBUGGABLE));
            elementUtils = env.getElementUtils();
            typeUtils = env.getTypeUtils();
            filer = env.getFiler();
            try {
                trees = Trees.instance(processingEnv);
            } catch (IllegalArgumentException ignored) {
            }
        }
    
        @Override
        public Set<String> getSupportedOptions() {
            return ImmutableSet.of(OPTION_SDK_INT, OPTION_DEBUGGABLE);
        }
    
        @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> elements, RoundEnvironment env) {
            Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env);
    
            for (Map.Entry<TypeElement, BindingSet> entry : bindingMap.entrySet()) {
                TypeElement typeElement = entry.getKey();
                BindingSet binding = entry.getValue();
    
                JavaFile javaFile = binding.brewJava(sdk, debuggable);
                try {
                    javaFile.writeTo(filer);
                } catch (IOException e) {
                    error(typeElement, "Unable to write binding for type %s: %s", typeElement, e.getMessage());
                }
            }
            return false;
        }
    
       @Override
       public SourceVersion getSupportedSourceVersion() {
           return SourceVersion.latestSupported();
       }
    }

    注意,ButterKnifeProcessor类上使用了@AutoService(Processor.class)注解,来实现注解处理器的注册,注册到 javac 后,在项目编译时就能执行注解处理器了。

    ButterKnifeProcessor继承了AbstractProcessor抽象类,并重写以上五个方法,如果我们自定义解处理器也是类似的,看下这几个方法:

    1、init()

    首先 init() 方法完成sdk版本的判断以及相关帮助类的初始化,帮助类主要有以下几个:

    • Elements elementUtils,注解处理器运行扫描源文件时,以获取元素(Element)相关的信息。Element 有以下几个子类:
      包(PackageElement)、类(TypeElement)、成员变量(VariableElement)、方法(ExecutableElement)
    • Types typeUtils,
    • Filer filer,用来生成 java 类文件。
    • Trees trees,
    2、getSupportedAnnotationTypes()

    该方法返回一个Set<String>,代表ButterKnifeProcessor要处理的注解类的名称集合,即 ButterKnife 支持的注解:butterknife-annotations

    3、getSupportedSourceVersion()

    返回当前系统支持的 java 版本。

    4、getSupportedOptions()

    返回注解处理器可处理的注解操作。

    5、process()

    最后,process() 方法是我们要重点分析的,在这里完成了目标类信息的收集并生成对应 java 类。

     

  • 相关阅读:
    门禁复制
    ImportError: cannot import name 'COMMAND' from 'MySQLdb.constants'
    Python3:模块:ModuleNotFoundError No module named 'MySQLdb'
    zookeeper问题:关于Unable to read additional data from server sessionid 0x0问题的解决
    Linux内存分析free与cache清理
    X-pack结合LDAP进行权限认证
    Django2.2框架:ORM数据库操作
    Django框架:模板继承和静态文件配置
    Djiango框架:模板语法
    Django2.2.x框架:基础篇(二)
  • 原文地址:https://www.cnblogs.com/the-wang/p/10214572.html
Copyright © 2011-2022 走看看