上一节我们已经将自动生成注解代码部分介绍完毕,今天这篇文章是自定义Android注解系列的最后一篇文章。希望大家这一路走来有所收获。
经过前面的了解,我们三大部分:butterknife-annotations、butterknife-compiler与butterknife-bind。现在就剩下最后一部分butterknife-bind。该部分是对我们前面定义的注解变量与自动生成的代码进行绑定,即调用我们自动生成的代码。
那么我们还是来看下butterknife-bind模板库的结构:
只有Butterknife一个类,在这之前我们还需将前面我们已经定义好的module引入
dependencies { ... compile project(path: ':butterknife-annotations') }
有了之前的基础,我们 Make Project 项目工程,之后就可以找到MainActivity$Binding类,或者直接在 /app/build/generated/source/kapt/debug/ 目录下查找。
MainActivity$Binding在构造方法中就已经调用了我们的需要的bindView与setOnClickListener方法。所以我们需要使用的话只需实例化即可。但由于我们是该类是通过注解处理器自动生成的,所以我们并不知道它的类名全称(这里我们相当于查看了源码,才知道是以$Binding结尾)。这样我们是不能通过 new 关键字来实例化。如此,我们又该如何实例化它呢?这时我们再来看butterknife-bind中的唯一的类Butterknife
public class Butterknife { private Butterknife() { } private static <T extends Activity> void initialization(T target, String suffix) { Class<?> tClass = target.getClass(); String className = tClass.getName(); try { Class<?> bindingClass = tClass.getClassLoader().loadClass(className + suffix); Constructor<?> constructor = bindingClass.getConstructor(tClass); constructor.newInstance(target); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } public static void bind(Activity activity) { initialization(activity, ConstantUtils.BINDING_BUTTERKNIFE_SUFFIX); } }
在initialization方法中,我们通过 java反射 来实例化我们需要的MainActivity$Binding。既然我们已经知道自动生成的类是由原始类(MainActivity)+后缀($Binding)组成。所以可以很好的使用java反射来实例化所需的类。对于外界的调用只需使用bind方法,传入需要绑定的类即可。
到这里,所以的准备工作已经完成。接下来我们可以开始在MainActivity中使用。首先将定义的库进行依赖
dependencies { ... implementation project(':butterknife-bind') kapt project(':butterknife-compiler') }
然后在MainActivity中使用
class MainActivity : AppCompatActivity() { @BindView(R.id.public_service, R.string.public_service) lateinit var sName: TextView @BindView(R.id.personal_wx, R.string.personal_wx) lateinit var sPhone: TextView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) Butterknife.bind(this) } @OnClick(R.id.public_service) fun nameClick(view: View) { Toast.makeText(this, getString(R.string.public_service_click_toast), Toast.LENGTH_LONG).show() } @OnClick(R.id.personal_wx) fun phoneClick(view: View) { Toast.makeText(this, getString(R.string.personal_wx_click_toast), Toast.LENGTH_LONG).show() } }
我们使用@BindView绑定View的Id与默认值;使用@OnClick绑定点击事件;使用Butterknife.bind(this)绑定自定义的注解代码。这样我们已经完成了与开源库Butterknife相似的功能。
现在你在模拟器或者真机上跑着非常完美,然后你不小心切换到 release 版本并且开启了 混淆 功能。这时你会发现mmp居然没有效果。为什么呢?debug与release的区别,绝大数情况下都是混淆惹的祸。我们在实例化自动生成的类时使用的是java反射机制,所以一旦混淆了我们的java反射就找不到我们指定的类名,这样自然也就没有效果了。
那么我们现在又该如何解决呢?别急,是否还记得在系列的 Part1 我们自定义注解变量中定义了 @Keep 。
在MainActivity$Binding类上我们使用了@Keep来标识该类,通过该标识告诉proguard不去混淆使用@Keep标记的类。要达到这种效果,我们还需经过以下两个步骤:
1.在butterknife-bind的proguard-rules.pro文件中添加如下代码
-keep class com.idisfkj.butterknife.annotations.Keep** -keep @com.idisfkj.butterknife.annotations.Keep public class * -keepclassmembers @com.idisfkj.butterknife.annotations.Keep class ** {*;}
2.为了是依赖库的混淆生效,我们还需使用consumerProguardFiles声明
defaultConfig { ... consumerProguardFiles 'proguard-rules.pro' //依赖库混淆生效 }
完成这两步后我们在重写构建release版本,这时程序完美运行。终于可以轻松的休息会了!
自定义Android注解系列完美收工,希望通过这三部曲能够帮助大家学会如何实现注解库。最后希望大家点赞支持一下,谢谢!
文章中的代码都可以在 Github中 获取到。使用时请将分支切换到 feat_annotation_processing