ButterKnife source code analysis

Analysis method, single-step debugging from the entry Debug

Entry, start with ButterKnife.bind(this) in OnCreate of Activity


@NonNull @UiThread
public static Unbinder bind ( @NonNull Activity target) {
View sourceView = target.getWindow().getDecorView() ;
return createBinding (target , sourceView) ;

Get the DecorView, the root View, and pass in the createBinding method

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) ;
}
}

Get the Class object of the target through reflection, where the target is the Activity to be bound
Then call  findBindingConstructorForClass (targetClass) ;


@Nullable @CheckResult @UiThread
private static Constructor<? extends Unbinder> findBindingConstructorForClass (Class<?> cls) {
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 {
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) ;
}
BINDINGS .put(cls , bindingCtor) ;
return bindingCtor ;
}

此方法是找出Acitvity_ViewBinding.class的Constructor对象
Activity_ViewBing是ButterKnife编译生成的class
在此class中,将注入的View和Activity关联起来

BINDINGS 是XXXActivity_ViewBinding的缓存,就是LinkedHashMap

在createBindging方法中调用
constructor.newInstance(target , source) ;
即调用XXXActivity_ViewBindng的构造方法


public class AppSettingsActivity_ViewBinding implements Unbinder {
private AppSettingsActivity target ;

private View view2131296303 ;

private View view2131296299 ;

private View view2131296479 ;

@UiThread
public AppSettingsActivity_ViewBinding (AppSettingsActivity target) {
this (target , target.getWindow().getDecorView()) ;
}

@UiThread
public AppSettingsActivity_ViewBinding ( final AppSettingsActivity target , View source) {
this . target = target ;

View view ;
target. mAppName = Utils. findRequiredViewAsType (source , R.id. app_name , "field 'mAppName'" , TextView. class ) ;
view = Utils. findRequiredView (source , R.id. app_upgrade , "field 'mAppUpdate' and method 'updateApp'" ) ;
target. mAppUpdate = Utils. castView (view , R.id. app_upgrade , "field 'mAppUpdate'" , TextView. class ) ;
view2131296303 = view ;
view.setOnClickListener( new DebouncingOnClickListener() {
@Override
public void doClick (View p0) {
target .updateApp() ;
}
}) ;
target. mCacheSize = Utils. findRequiredViewAsType (source , R.id. cache_size , "field 'mCacheSize'" , TextView. class ) ;
target. mToolbar = Utils. findRequiredViewAsType (source , R.id. toolbar , "field 'mToolbar'" , Toolbar. class ) ;
view = Utils. findRequiredView (source , R.id. app_clean , "method 'cleanCache'" ) ;
view2131296299 = view ;
view.setOnClickListener( new DebouncingOnClickListener() {
@Override
public void doClick (View p0) {
target .cleanCache() ;
}
}) ;
view = Utils. findRequiredView (source , R.id. ic_launcher , "method 'setDevelopMode'" ) ;
view2131296479 = view ;
view.setOnClickListener( new DebouncingOnClickListener() {
@Override
public void doClick (View p0) {
target .setDevelopMode() ;
}
}) ;
}

@Override
@CallSuper
public void unbind () {
AppSettingsActivity target = this . target ;
if (target == null ) throw new IllegalStateException( "Bindings already cleared." ) ;
this . target = null;

target. mAppName = null;
target. mAppUpdate = null;
target. mCacheSize = null;
target. mToolbar = null;

view2131296303 .setOnClickListener( null ) ;
view2131296303 = null;
view2131296299 .setOnClickListener( null ) ;
view2131296299 = null;
view2131296479 .setOnClickListener( null ) ;
view2131296479 = null;
}
}


在Utils.findRequiredView中使用了findViewById方法,说明实际还是通过findViewById拿到View对象的

可借鉴学习的地方:(1)使用LinkedHashmap做缓存

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324371793&siteId=291194637