打造一套自己的注解框架
1.Xutils源码阅读与使用
// handlerType --> class 获取setContentView()方法
Method setContentViewMethod=handlerType.getMethod("setContentView", int.class);
// 反射执行该方法
setContentViewMethod.invoke(activity, viewId);
View view = finder.findViewById(viewInject.value(),viewInject.parentId());
if (view != null) {
// 可以操作所有修饰
field.setAccessible(true);
// 反射注入属性
field.set(handler, view);
}
属性注入:利用反射去获取Annotation->value->findViewById->反射注入属性
事件注入:利用反射去获取Annotation->value->findViewById->setOnClickListener->动态代理注入事件
2.ButterKnife源码阅读与使用
@Retention(CLASS)
@Target(FIELD) //代表Annotation的位置
public @interface Bind {
/** View ID to which the field will be bound. */
int[] value();
}
@Retention(CLASS)编译时的注解
1.编译的时候ButterKnifeProcessor 生成 .java --> class文件
2.运行时viewBinder.bind(finder, target, source);
3.自己打造一套注解框架
3.1 findViewById注入
1.在使用注解绑定控件的時候,一定记得在onCreate中调用ViewUtils.inject(this);
2.与ButterKnife不同的是我们采用的是运行时生效,Retention是说什么时候生效,分为三种情况,编译时生效class,运行时生效Runtime,源码资源Source.
//ViewByID.class
//代表Annotation 的位置
@Target(ElementType.FIELD)
//什么时候生效, 编译时生效Class 运行时生效Runtime 源码资源Source
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewById {
int value();
}
3.ViewUtils中注入属性(因为考虑到了事件比如onClick等以及findViewById的绑定等,所以inject分成两步走,一部分是injectFiled,一部分是injectEvent)
//注入属性
private static void injectFiled(ViewFinder finder, Object object) {
//1.获取类里面的所有属性
Class<?> clazz=object.getClass();
//获取所有的属性,包括公有和私有
Field[] fields=clazz.getDeclaredFields();
//2.获取findById的里面的value 值
for(Field field :fields){
ViewById viewById=field.getAnnotation(ViewById.class);
if(viewById !=null){
//获取注解里面的id值 ---->R.id.test_iv
int viewId=viewById.value();
//3.findViewById 找到View
View view=finder.findViewById(viewId);
if(view !=null) {
//能够注入所有修饰符 例如private、public
field.setAccessible(true);
//4.动态的注入找到的View
try {
field.set(object, view);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
3.2 事件的注入
1.onClick.class
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick {
int[] value();
}
2.事件的注入
/**
*
* 事件注入
*/
@RequiresApi(api = Build.VERSION_CODES.N)
private static void injectEvent(ViewFinder finder, Object object) {
//1.获取类里面所有的方法
Class<?> clazz=object.getClass();
Method[] methods=clazz.getDeclaredMethods();
//2.获取OnClick里面的value值
for(Method method:methods){
OnClick onClick=method.getAnnotation(OnClick.class);
if(onClick !=null){
int[] viewIds=onClick.value();
for(int viewId :viewIds){
//3.findViewById找到View
View view=finder.findViewById(viewId);
//拓展功能 检测网络
boolean isCheckNet=method.getAnnotation(CheckNet.class) !=null;
if(view!=null){
//4.view.setOnClickListener
view.setOnClickListener(new DecalredOnclickListener(method,object,isCheckNet));
}
}
}
}
}
private static class DecalredOnclickListener implements View.OnClickListener {
private Object mObject;
private Method mMethod;
private boolean mIsCheckNet;
public DecalredOnclickListener(Method method, Object object,boolean isCheckNet) {
this.mObject=object;
this.mMethod=method;
this.mIsCheckNet=isCheckNet;
}
@Override
public void onClick(View v) {
//需不需要监测网络
if(mIsCheckNet){
if(!networkAvalible(v.getContext())){
Toast.makeText(v.getContext(),"请检查您的网络环境",Toast.LENGTH_LONG).show();
return;
}
}
//5.反射执行方法
//点击会调用该方法
try {
mMethod.setAccessible(true);
mMethod.invoke(mObject,v);
} catch (Exception e) {
e.printStackTrace();
try {
mMethod.invoke(mObject,null);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
private static boolean networkAvalible(Context context){
try {
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
if (activeNetworkInfo != null && activeNetworkInfo.isConnected()) {
return true;
}
}catch(Exception e){
e.printStackTrace();
}
return false;
}
injectEvent->DeclaredClickListener->netWorkAvalibe
4.APO和OOP
APO是面向切面的,通过注解、反射、动态代理(预编译方式和运行期动态代理)等方式实现,通俗的来说就是把跟性能有关的功能与同一个切面绑定。
OOP是面向对象的