Android自我提升二 揭秘IOC注入框架,轻松实现布局、属性、事件注入

基础知识讲解

什么是DIP、IOC、DI、IOC容器

Android自我提升二 揭秘IOC注入框架,轻松实现布局、属性、事件注入_第1张图片
DIP、IOC、DI、IOC容器的含义

IOC容器的技术剖析

IOC中最基本的技术就是“反射(Reflection)”编程
我们可以把IOC容器的工作模式看做是工厂模式的升华,可以把IOC容器看作是一个工厂,这个工厂里要生产的对象都在配置文件中给出定义,然后利用编程语言的的反射编程,根据配置文件中给出的类名生成相应的对象。从实现来看,IOC是把以前在工厂方法里写死的对象生成代码,改变为由配置文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。

实际开发用法

Android自我提升二 揭秘IOC注入框架,轻松实现布局、属性、事件注入_第2张图片
依赖注入

技术解析
看到具体实现,有很多朋友一定会觉得很熟悉的,这不是butterknife么?那说明朋友们还是经常会用到这些技术,因为butterknife在3之前及3的部分都是运用的“反射”技术,还有我们常用的EventBus等等都用到了这项技术,但是一个三方框架,可能百分之八十的技术是我们用不到的。

目的与优缺点

  1. IOC 技术核心是解耦,降低模块之间的关联
  2. 优点:代码少,加速开发
  3. 缺点:反射产生性能损耗

具体开发实现

核心技术:反射

Android自我提升二 揭秘IOC注入框架,轻松实现布局、属性、事件注入_第3张图片
反射常用API
getMethod()与getDeclareMathods区别

  1. getMethod() 获取当前类和父类 public方法
  2. getDeclareMathods() 获取当前类所有方法

布局注解代码

package com.fly.newstart.ioc.annotetion;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 
 *           .----.
 *        _.'__    `.
 *    .--(Q)(OK)---/$\
 *  .' @          /$$$\
 *  :         ,   $$$$$
 *   `-..__.-' _.-\$/
 *         `;_:    `"'
 *       .'"""""`.
 *      /,  FLY  ,\
 *     //         \\
 *     `-._______.-'
 *     ___`. | .'___
 *    (______|______)
 * 
* 包 名 : com.fly.newstart.ioc * 作 者 : FLY * 创建时间 : 2019/4/24 * 描述: 布局注入注解 */ @Target(ElementType.TYPE) //该注解作用于什么之上 ,对应枚举标识 METHOD-标识为类之上 @Retention(RetentionPolicy.RUNTIME)//jvm在运行时通过反射获取注解的值 //RUNTIME-jvm在运行时通过反射来完成的过程 //CLASS-在编译时进行一些预操作,并且注解会在class存在 //SOURCE-源码级的,主要是做一些检查检测操作 public @interface ContentView { int value(); }

属性注解代码

package com.fly.newstart.ioc.annotetion;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 
 *           .----.
 *        _.'__    `.
 *    .--(Q)(OK)---/$\
 *  .' @          /$$$\
 *  :         ,   $$$$$
 *   `-..__.-' _.-\$/
 *         `;_:    `"'
 *       .'"""""`.
 *      /,  FLY  ,\
 *     //         \\
 *     `-._______.-'
 *     ___`. | .'___
 *    (______|______)
 * 
* 包 名 : com.fly.newstart.ioc.annotetion * 作 者 : FLY * 创建时间 : 2019/4/24 * 描述: 属性的注解 */ @Target(ElementType.FIELD) //该注解作用于什么之上 ,对应枚举标识 FIELD-标识为属性之上 @Retention(RetentionPolicy.RUNTIME)//jvm在运行时通过反射获取注解的值 public @interface InjectView { int value(); }

点击事件注解代码

package com.fly.newstart.ioc.annotetion;

import android.view.View;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 
 *           .----.
 *        _.'__    `.
 *    .--(Q)(OK)---/$\
 *  .' @          /$$$\
 *  :         ,   $$$$$
 *   `-..__.-' _.-\$/
 *         `;_:    `"'
 *       .'"""""`.
 *      /,  FLY  ,\
 *     //         \\
 *     `-._______.-'
 *     ___`. | .'___
 *    (______|______)
 * 
* 包 名 : com.fly.newstart.ioc.annotetion * 作 者 : FLY * 创建时间 : 2019/4/24 * 描述: 点击事件的注解 */ @Target(ElementType.METHOD) //该注解作用于什么之上 ,对应枚举标识 METHOD-标识为方法之上 @Retention(RetentionPolicy.RUNTIME)//jvm在运行时通过反射获取注解的值 @EventBase(listenerSetter = "setOnClickListener", listenerType = View.OnClickListener.class, listenerCallBack = "onClick") public @interface OnClick { int[] value(); }

长按事件注解代码

package com.fly.newstart.ioc.annotetion;

        import android.view.View;

        import java.lang.annotation.ElementType;
        import java.lang.annotation.Retention;
        import java.lang.annotation.RetentionPolicy;
        import java.lang.annotation.Target;

/**
 * 
 *           .----.
 *        _.'__    `.
 *    .--(Q)(OK)---/$\
 *  .' @          /$$$\
 *  :         ,   $$$$$
 *   `-..__.-' _.-\$/
 *         `;_:    `"'
 *       .'"""""`.
 *      /,  FLY  ,\
 *     //         \\
 *     `-._______.-'
 *     ___`. | .'___
 *    (______|______)
 * 
* 包 名 : com.fly.newstart.ioc.annotetion * 作 者 : FLY * 创建时间 : 2019/4/24 * 描述: 长按事件的注解 */ @Target(ElementType.METHOD) //该注解作用于什么之上 ,对应枚举标识 METHOD-标识为方法之上 @Retention(RetentionPolicy.RUNTIME)//jvm在运行时通过反射获取注解的值 @EventBase(listenerSetter = "setOnLongClickListener", listenerType = View.OnLongClickListener.class, listenerCallBack = "onLongClick") public @interface OnLongClick { //注解对应方法需要boolean返回值 int[] value(); }

事件注解的注解代码

package com.fly.newstart.ioc.annotetion;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 
 *           .----.
 *        _.'__    `.
 *    .--(Q)(OK)---/$\
 *  .' @          /$$$\
 *  :         ,   $$$$$
 *   `-..__.-' _.-\$/
 *         `;_:    `"'
 *       .'"""""`.
 *      /,  FLY  ,\
 *     //         \\
 *     `-._______.-'
 *     ___`. | .'___
 *    (______|______)
 * 
* 包 名 : com.fly.newstart.ioc.annotetion * 作 者 : FLY * 创建时间 : 2019/4/24 * 描述: 事件注解的注解 用于封装点击事件规律的对象 */ @Target(ElementType.ANNOTATION_TYPE) //该注解作用于什么之上 ,对应枚举标识 ANNOTATION_TYPE-标识为注解之上 @Retention(RetentionPolicy.RUNTIME)//jvm在运行时通过反射获取注解的值 public @interface EventBase { //setxxxListener String listenerSetter(); //new View.xxxListener Class listenerType(); //回调执行方法:onxxx() String listenerCallBack(); }

注入管理类代码

package com.fly.newstart.ioc;

import android.app.Activity;
import android.util.Log;
import android.view.View;

import com.fly.newstart.ioc.annotetion.ContentView;
import com.fly.newstart.ioc.annotetion.EventBase;
import com.fly.newstart.ioc.annotetion.InjectView;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


/**
 * 
 *           .----.
 *        _.'__    `.
 *    .--(Q)(OK)---/$\
 *  .' @          /$$$\
 *  :         ,   $$$$$
 *   `-..__.-' _.-\$/
 *         `;_:    `"'
 *       .'"""""`.
 *      /,  FLY  ,\
 *     //         \\
 *     `-._______.-'
 *     ___`. | .'___
 *    (______|______)
 * 
* 包 名 : com.fly.newstart.ioc * 作 者 : FLY * 创建时间 : 2019/4/24 * 描述: 注入管理类 */ public class InjectManager { /** * 注解的初始注入 * * @param activity */ public static void inject(Activity activity) { //布局的注入 injectLayout(activity); //控件的注入 injectView(activity); //事件的注入 injectEvents(activity); } /** * 布局的注入 * * @param activity */ private static void injectLayout(Activity activity) { //获取类 Class cla = activity.getClass(); //获取类上的注解 ContentView contentView = cla.getAnnotation(ContentView.class); //获取注解的值 if (contentView != null) { int layoutId = contentView.value(); // 执行方法:setContentView(R.layout.activity_ioc); // 方法一:activity.setContentView(layoutId); // 方法二:反射获取,高大上 try { //setContentView 是父类的方法不能使用getDeclareMathods(),需要使用getMethod() Method method = cla.getMethod("setContentView", int.class); //执行方法 method.invoke(activity, layoutId); } catch (Exception e) { e.printStackTrace(); } } } /** * 控件的注入 * * @param activity */ private static void injectView(Activity activity) { //获取类 Class cla = activity.getClass(); //获取类的所有属性 Field[] fields = cla.getDeclaredFields(); // 循环 拿到每个属性 if (fields == null || fields.length < 1) return; for (Field field : fields) { // 获取每个属性的注解 InjectView injectView = field.getAnnotation(InjectView.class); if (injectView != null) { //并不是所有属性都有注解 //获取注解的值 int viewId = injectView.value(); //执行方法:mBtn01 = findViewById(R.id.btnO1) try { Method method = cla.getMethod("findViewById", int.class); //findViewById 需要赋值 //执行方法 获取返回值 Object view = method.invoke(activity, viewId); //设置私有属性的访问权限,默认为false field.setAccessible(true); //给属性赋值 field.set(activity, view); } catch (Exception e) { e.printStackTrace(); } } } } /** * 事件的注入 * * @param activity */ private static void injectEvents(Activity activity) { //获取类 Class cla = activity.getClass(); //获取类的所有方法,事件注解肯定是当前类 Method[] methods = cla.getDeclaredMethods(); if (methods == null || methods.length < 1) return; for (Method method : methods) { //获取每个方法的注解 Annotation[] annotations = method.getAnnotations(); //一个可能有多个注解 if (annotations == null || annotations.length < 1) continue; for (Annotation annotation : annotations) { //获取注解上的注解类型 Class annotationType = annotation.annotationType(); if (annotationType != null) { //通过EventBase注解,获取3个重要的规律 EventBase eventBase = annotationType.getAnnotation(EventBase.class); //事件的3个规律 String listenerSetter = eventBase.listenerSetter(); Class listenerType = eventBase.listenerType(); String listenerCallBack = eventBase.listenerCallBack(); //获取注解的值 try { // 通过运行annotationType获取OnClick注解的value值 Method valueMethod = annotationType.getDeclaredMethod("value"); //运行OnClick注解的value方法获取value值 int[] viewIds = (int[]) valueMethod.invoke(annotation); //打包之后,代理处理后续工作,可以兼容多种事件方式,不需要写接口 //创建拦截器 ListenerInvocationHandler handler = new ListenerInvocationHandler(activity); handler.addMethod(listenerCallBack, method); //创建代理 Object listene = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, handler); if (viewIds == null || viewIds.length < 1) continue; for (int viewId : viewIds) { //控件的赋值,保证控件没有赋值也可使用 View view = activity.findViewById(viewId); //获取set方法 Method setter = view.getClass().getMethod(listenerSetter, listenerType); //执行方法,传入代理 setter.invoke(view, listene); } } catch (Exception e) { e.printStackTrace(); } } } } } }

事件处理拦截器代码

package com.fly.newstart.ioc;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;

/**
 * 
 *           .----.
 *        _.'__    `.
 *    .--(Q)(OK)---/$\
 *  .' @          /$$$\
 *  :         ,   $$$$$
 *   `-..__.-' _.-\$/
 *         `;_:    `"'
 *       .'"""""`.
 *      /,  FLY  ,\
 *     //         \\
 *     `-._______.-'
 *     ___`. | .'___
 *    (______|______)
 * 
* 包 名 : com.fly.newstart.ioc * 作 者 : FLY * 创建时间 : 2019/4/24 * 描述: 事件处理拦截器 */ public class ListenerInvocationHandler implements InvocationHandler { //需要拦截的对象 private Object target; //需要拦截的方法集合 private HashMap methodhMap = new HashMap<>(); public ListenerInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (target != null) { //获取需要拦截的方法名 String methodName = method.getName(); //重新赋值,将拦截的方法换为了自定义的方法 method = methodhMap.get(methodName);//从集合中判断是否需要拦截 if (method != null) {//确定找到了需要拦截的的方法,才执行自定义方法 if (method.getParameterTypes().length == 0) { //判断有无参数 return method.invoke(target); } else return method.invoke(target, args); } } return null; } /** * 将需要拦截的方法加入集合 * * @param methodName 需要拦截的方法名 * @param method 执行自定义的方法 */ public void addMethod(String methodName, Method method) { methodhMap.put(methodName, method); } }

测试Activity代码

package com.fly.newstart.ioc.text;

import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import com.fly.newstart.R;
import com.fly.newstart.common.base.BaseActivity;
import com.fly.newstart.ioc.annotetion.ContentView;
import com.fly.newstart.ioc.annotetion.InjectView;
import com.fly.newstart.ioc.annotetion.OnClick;
import com.fly.newstart.ioc.annotetion.OnLongClick;

// setContentView(R.layout.activity_ioc);
@ContentView(R.layout.activity_ioc)
public class IOCActivity extends BaseActivity {

    @InjectView(R.id.btnO1) //mBtn01 = findViewById(R.id.btnO1)
    private Button mBtn01;

    @InjectView(R.id.btnO2)
    private Button mBtn02;

    @Override
    protected void onResume() {
        super.onResume();
        Toast.makeText(this, mBtn01.getText().toString(), Toast.LENGTH_SHORT).show();
    }

    // mBtn01.setOnClickListener(new View.OnClickListener() {@Override public void onClick(View v) { //  }});
    @OnClick({R.id.btnO1, R.id.btnO2})
    public void show(View view) {
        Toast.makeText(this, "shwo(View view)", Toast.LENGTH_SHORT).show();
    }

    // mBtn01.setOnLongClickListener(new View.OnLongClickListener() {@Override public boolean onLongClick(View v) {   return false;  }});
    @OnLongClick({R.id.btnO1, R.id.btnO2})
    public boolean showLong(View view) {
        Toast.makeText(this, "shwoLong(View view)", Toast.LENGTH_SHORT).show();
        return false;
    }


//    @OnClick({R.id.btnO1,R.id.btnO2}) //proxy 代理
//    public void show(){
//        Toast.makeText(this, mBtn01.getText().toString(), Toast.LENGTH_LONG).show();
//    }

    public void initView() {
        //用注解实现,需要代理完成,监听回调的过程
        //Android监听事件是有规律的
        //setxxxListener
        //new View.xxxListener
        //回调执行方法:onxxx()
        //将规律打包成一个对象,用代理去完成这个事件
        //还需要用到AOP  面向切面的技术
        mBtn01.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });

        mBtn02.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });


        mBtn01.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                return false;
            }
        });
    }
}

baseActivity代码

package com.fly.newstart.common.base;

import android.app.Activity;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.AppCompatDelegate;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

import com.fly.newstart.R;
import com.fly.newstart.ioc.InjectManager;

/**
 *           .----.
 *        _.'__    `.
 *    .--(Q)(OK)---/$\
 *  .' @          /$$$\
 *  :         ,   $$$$$
 *   `-..__.-' _.-\$/
 *         `;_:    `"'
 *       .'"""""`.
 *      /,  FLY  ,\
 *     //         \\
 *     `-._______.-'
 *     ___`. | .'___
 *    (______|______)
 * 
* 包 名 : com.fly.newstart.common.base * 作 者 : FLY * 创建时间 : 2017/8/7 *

* 描述: Activity的基类 */ public class BaseActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 帮助所有子类完成注入工作 InjectManager.inject(this); } }

难点剖析

布局注入与属性注入都比较简单,只是运用反射的基本知识,但是方法的注入就比较难以理解,其中还运用了Prixy(代理)和InvocationHandler(拦截)的技术。

代理的原理与用法

Android自我提升二 揭秘IOC注入框架,轻松实现布局、属性、事件注入_第4张图片
代理的原理与用法

Android监听事件规律-三部曲
Android自我提升二 揭秘IOC注入框架,轻松实现布局、属性、事件注入_第5张图片
Android监听事件的三部曲

将规律打包成一个对象,用代理去完成这个事件,但是这样任然不能完成我们需要的功能,还需要用到AOP 面向切面的技术。

AOP原理和拦截

Android自我提升二 揭秘IOC注入框架,轻松实现布局、属性、事件注入_第6张图片
AOP原理和拦截

小结

  1. 主要了解IOC注入框架,含义及基础使用
  2. 注解的基本使用
  3. “反射”的常规用法,七种常用且基础的API理解
  4. 代理的思路与原理
  5. AOP原理与拦截

你可能感兴趣的:(Android自我提升二 揭秘IOC注入框架,轻松实现布局、属性、事件注入)