码迷,mamicode.com
首页 > 其他好文 > 详细

ButterKnife 原理解析

时间:2019-02-26 15:29:12      阅读:188      评论:0      收藏:0      [点我收藏+]

标签:create   ==   tree   sso   tag   收集   元素   clear   span   

一、使用方法

  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 类。

 

 

ButterKnife 原理解析

标签:create   ==   tree   sso   tag   收集   元素   clear   span   

原文地址:https://www.cnblogs.com/the-wang/p/10214572.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!