美文网首页
IOC 注解框架的使用

IOC 注解框架的使用

作者: xqiiitan | 来源:发表于2024-06-23 17:09 被阅读0次

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

相关文章

  • Spring AOP编程实战

    Spring框架的IOC功能之注解的方式 Spring框架的IOC之注解方式的快速入门 Spring框架中Bean...

  • Spring框架的IOC功能之注解

    Spring框架的IOC功能之注解的方式 Spring框架的IOC之注解方式的快速入门 步骤一:导入注解开发所有需...

  • 九、使用注解实现IOC的配置

    一、本课目标 掌握使用注解实现IOC的方法 二、使用注解实现IOC 2.1准备工作 注解方式将Bean的定义信息和...

  • IOC注解框架

    最近利用空闲时间学习了一点东西IOC属性注解、事件注解、网络注解,其实很感谢那些无私分享的作者,通过看他们...

  • spring基于注解的ioc

    spring基于注解的ioc 教学内容 Spring中的ioc常用注解 案例使用xml和注解方式实现单标的CRUD...

  • Spring入门篇

    主要介绍 什么是框架? Spring简介? IOC(配置、注解) ——控制反转 Bean(配置、注解) AOP(配...

  • 注解框架源码分析(XUtils、ButterKnife)

    1.前言 频繁的findViewById是一件挺烦人的事,IOC注解框架应运而生,Android中IOC框架就是注...

  • spring-ioc 注解

    spring-ioc 注解使用 配置扫描路径 添加注解 @Component("user") 使用 Spring ...

  • 使用注解打造自己的IOC框架

    一、简述 IoC和AOP可谓是后台开发入门必学的知识(Spring相关),但这两者都仅仅只是概念而已,并非具体技术...

  • 使用注解打造自己的IOC框架

    一、简述 IoC和AOP可谓是后台开发入门必学的知识(Spring相关),但这两者都仅仅只是概念而已,并非具体技术...

网友评论

      本文标题:IOC 注解框架的使用

      本文链接:https://www.haomeiwen.com/subject/pjsxcjtx.html