Android利用注解实现“注入”

基础概念

  1. 依赖倒置原则(DIP Dependency Inverse Principle)
    高层组件(抽象)不应该依赖于低层组件(细节),两者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。 其核心思想就是基于接口编程

  2. 控制反转(IOC Inverse of Control)
    将控制权交向上层组件转移。控制反转是DIP的具体实现方式。

  3. 依赖注入(DI Dependency Injection)
    组件通过构造函数或者setter的方式,将其依赖暴露给上层,上层要设法取得组件的依赖,并将其传递给组件。依赖注入是实现控制反转的的一种方式。

  4. AOP原理和拦截
    AOP(面向切面编程),AOP基于IOC基础
    AOP将应用系统分为两部分:核心业务逻辑及横向的通用逻辑。

1.Activity基类

public class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 在基类中统一执行注入的逻辑
        InjectManager.injectLayout(this);
        InjectManager.injectView(this);
        InjectManager.injectClick(this);
    }
}

2.MainActivity类

@ContentView(R.layout.activity_main) // 布局注入
public class MainActivity extends BaseActivity {

    @InjectView(R.id.tv) // 控件注入
    private TextView tv;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        tv.setText("lcj");
    }

    @OnClick({R.id.tv}) // 点击事件注入
    public void tvClick(View view){
        Toast.makeText(this, "lcj", Toast.LENGTH_LONG).show();
    }

}

3.自定义注解

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

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ContentView {
    int value();
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectView {
    int value();
}
import android.view.View;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@EventBase(listenerSetter = "setOnClickListener", listenerType = View.OnClickListener.class, callBackListener = "onClick")
public @interface OnClick {
    int[] value();
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.ANNOTATION_TYPE) // 作用在注解之上
@Retention(RetentionPolicy.RUNTIME)
public @interface EventBase {

    String listenerSetter(); // setXXXClickListener()

    Class<?> listenerType(); // View.OnXXXClickListener()

    String callBackListener(); // onXXXClick()

}

4.注解管理器(核心代码)

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

import com.lcj.annotationlibrary.annotation.ContentView;
import com.lcj.annotationlibrary.annotation.EventBase;
import com.lcj.annotationlibrary.annotation.InjectView;

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

public class InjectManager {

    public static final String TAG = InjectManager.class.getName();

    // 注入布局文件
    public static void injectLayout(Activity activity) {

        Class<? extends Activity> clz = activity.getClass();
        ContentView contentView = clz.getAnnotation(ContentView.class);
        if (contentView != null) {
            int layoutId = contentView.value();
            try {
                Method method = clz.getMethod("setContentView", int.class);
                method.invoke(activity, layoutId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    // 注入控件
    public static void injectView(Activity activity) {

        Class<? extends Activity> clz = activity.getClass();
        Field[] fields = clz.getDeclaredFields(); // 获取到所有字段
        for (Field field : fields) {
            InjectView injectView = field.getAnnotation(InjectView.class);
            if (injectView != null) {
                int viewId = injectView.value();
                try {
                    Method method = clz.getMethod("findViewById", int.class);
                    Object view = method.invoke(activity, viewId);
                    field.setAccessible(true); // 设置private属性的访问权限
                    field.set(activity, view);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    // 注入事件
    public static void injectClick(Activity activity) {

        Class<? extends Activity> clz = activity.getClass();
        Method[] methods = clz.getDeclaredMethods();
        for (Method method : methods) {
            // 获取到方法上的多个注解
            Annotation[] annotations = method.getAnnotations();
            for (Annotation annotation : annotations) {

                Class<? extends Annotation> annotationType = annotation.annotationType();
                if (annotationType != null) {
                    // 获取到注解的注解
                    EventBase eventBase = annotationType.getAnnotation(EventBase.class);

                    if (eventBase != null) {
                        String listenerSetter = eventBase.listenerSetter();
                        Class<?> listenerType = eventBase.listenerType();
                        String callBackListener = eventBase.callBackListener();

                        // 利用 代理 来拦截onClick()方法,转而执行自己的方法tvClick方法
                        ListenerInvocationHandler listenerInvocationHandler = new ListenerInvocationHandler(activity);
                        listenerInvocationHandler.addMethodMap(callBackListener, method);
                        Object listener = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, listenerInvocationHandler);

                        try {
                            // 获取到OnClick注解的方法
                            Method valueMethod = annotationType.getDeclaredMethod("value");
                            int[] viewIds = (int[]) valueMethod.invoke(annotation);
                            for (int viewId : viewIds) {
                                // 执行了setOnClickListener()方法
                                View view = activity.findViewById(viewId);
                                Method setter = view.getClass().getMethod(listenerSetter, listenerType);
                                setter.invoke(view, listener);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

}

5.使用代理实现onXXXClick方法拦截的代码

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

public class ListenerInvocationHandler implements InvocationHandler{

    // 需要拦截的对象
    private Object target;

    // 需要拦截的方法集合
    private HashMap<String, Method> mMethodMap = new HashMap<>();

    public ListenerInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 在此处执行的时候,将本来要执行的onClick方法替换成 自己的方法
        if (target != null) {
            String name = method.getName();
            method = mMethodMap.get(name);
            if (method != null) {
                return method.invoke(target, args);
            }
        }
        return null;
    }

    public void addMethodMap(String methodName, Method method) {
        mMethodMap.put(methodName, method);
    }
    
}

猜你喜欢

转载自blog.csdn.net/reuxfhc/article/details/89466877