IOC注解框架。(反射+注解)参考:xUtils,ButterKnife。
场景:findViewById, onClick, 网络监测,用户是否已登录,是否VIP用户...
1.1 源码分析:xUtils:
compile "org.xutils:xutils:3.3.40"
// ViewInjectImpl: handlerType--> class, 获取setContentView方法,然后反射执行方法。
Method setContentViewMethod = handlerType.getMethod("setContentView", int.class);
setContentViewMethod.invoke(activit, viewId);
ViewInject viewInject = field.getAnnotation(ViewInject.class);
View view = finder.findViewById(viewInject.value(), viewInject.parentId());
if(view != null) {
field.setAccessible(true); // 可操作private修饰的变量。
field.set(handler, view); // 反射注入属性
}
动态代理的应用:插件开发,hook技术,
属性注入:利用反射获取Annotation--> 获取value--> findViewById--> 调用反射注入属性。
事件注入:利用反射获取Annotation--> 获取value--> findViewById--> setOnClickListener-- 动态代理,反射执行方法。
1.2 源码分析:ButterKnife: 相对更加高效一点。但是影响编译速度,增大apk的代码体积。有相应的插件,减少写代码。
流程:
- 编译的时候,ButterKnifeProcessor 生成.java文件,然后生成.class文件。打包到apk中。
- 在运行时 viewBinder.bind(finder, target, source);
- MainActivity$$ViewBinder bind() , 找到对应的view,绑定事件。
compile 'con.jakewharton:butterknife:8.5.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1'
// 编译时注解。
@Retention(CLASS) @Target(FIELD)
public @interface Bind {
int[] value();
}
2. 代码实现
public class MainActivity extends AppCompatActivity {
@ViewById(R.id.tv_text)
private TextView mTvText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewUtils.inject(this);
mTvText.setText("welcome 1131 ");
}
// @OnClick(R.id.tv_text)
@CheckNet // 没网,不让执行该方法。而是直接打印。统一打印Toast。
@OnClick({R.id.tv_text, R.id.iv_image})
private void onClick() {
Toast.makeText(this, "点击事件:"/* + view.getClass()*/, Toast.LENGTH_LONG).show();
}
}
// 添加ViewById,OnClick,CheckNet相关的注解。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CheckNet {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface OnClick {
int[] value(); // 可以配置多个
}
// 什么时候是生效:class编译时,runtime运行时,source源码资源上。
@Retention(RetentionPolicy.RUNTIME)
// 代表Annotation的位置,Field属性,type类,Constructor构造方法上。
@Target(ElementType.FIELD)
public @interface ViewById {
int value();
}
/**
* View的辅助类。
*/
public class ViewFinder {
private Activity mActivity;
private View mView;
public ViewFinder(Activity activity) {
this.mActivity = activity;
}
public ViewFinder(View view) {
this.mView = view;
}
// 兼容方法
public View findViewById(int viewId) {
return mActivity != null ? mActivity.findViewById(viewId) : mView.findViewById(viewId);
}
}
/**
* Added by Tom on 2024/06/24.
* View的注入
*/
public class ViewUtils {
public static void inject(Activity activity) {
inject(new ViewFinder(activity), activity);
}
// 兼容View。
public static void inject(View view) {
inject(new ViewFinder(view), view);
}
// object 是 Activity。
public static void inject(View view, Object object) {
inject(new ViewFinder(view), object);
}
// 兼容上面3个方法。
// object 是反射需要执行的类
private static void inject(ViewFinder finder, Object object) {
injectField(finder, object);
injectEvent(finder, object);
}
// 注入事件
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.onClickListener,传入方法名 、
view.setOnClickListener(new DeclaredOnClickListener(method, object, isCheckNet));
}
}
}
}
}
private static class DeclaredOnClickListener implements View.OnClickListener {
private Method mMethod;
private Object mObject;
private boolean mIsCheckNet;
public DeclaredOnClickListener(Method method, Object object, boolean isCheckNet) {
this.mMethod = method;
this.mObject = object;
this.mIsCheckNet = isCheckNet;
}
@Override
public void onClick(View v) {
if (mIsCheckNet) { // 是否需要监测网络。
if (!networkAvailable(v.getContext())) {
Toast.makeText(v.getContext(), "您的网络不太给力", Toast.LENGTH_SHORT).show();
return;
}
}
// 点击最终调用的方法
try {
// 5.反射执行方法. 执行View的 点击事件
mMethod.setAccessible(true);
mMethod.invoke(mObject, v); // mObject 就是Context,执行v的点击事件。
} catch (Exception e) {
e.printStackTrace();
try {
mMethod.invoke(mObject, null); // 没有传入v对象。
} catch (Exception e1) {
e1.printStackTrace();
}
} // try-catch,能避免部分crash,不会闪退。
}
}
// 注入属性:遍历所有的属性
private static void injectField(ViewFinder finder, Object object) {
// 1.遍历类里面所有的属性。
Class<?> clazz = object.getClass();
// 获取所有属性(私有+共有的)
Field[] fields = clazz.getDeclaredFields();
// 2.获取viewById里面的value值
for (Field field : fields) {
ViewById viewById = field.getAnnotation(ViewById.class);
if (viewById != null) {
// 3. findViewById找到view
int viewId = viewById.value(); // 获取注解里面的id值。R.id.test_tv
View view = finder.findViewById(viewId);
if (view != null) {
// 4. 动态注入找到View
try {
field.setAccessible(true); // 可处理所有修饰符修饰的变量。
field.set(object, view);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
// 网络是否可用
private static boolean networkAvailable(Context context) {
try {
ConnectivityManager cManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = cManager.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isConnected()) {
return true;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}
网友评论