Android use annotations to achieve "injected"

Basic concepts

  1. Dependency Inversion Principle (DIP Dependency Inverse Principle)
    high-level components (abstract) should not depend on low-level components (detail), both of which should depend on the abstract. Abstract should not depend on the details, the details should depend on the abstract. Its core idea is based on the programming interface

  2. Inversion of Control (IOC Inverse of Control)
    control to the transfer assembly to the upper. Inversion control of a particular implementation DIP.

  3. Dependency injection (DI Dependency Injection)
    assembly by way of a constructor or setter, which depend exposed to upper layers, to try to achieve the dependent components, and passes it to the assembly. Dependency injection is a way to achieve control of the inversion.

  4. AOP interception principles and
    AOP (Aspect Oriented Programming), AOP IOC based on
    AOP the application into two parts: the core business logic and horizontal universal logic.

1.Activity base class

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 Deliverable

@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. custom annotation

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. Annotation Manager (core code)

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. The method of interception proxy code implemented 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);
    }
    
}

Guess you like

Origin blog.csdn.net/reuxfhc/article/details/89466877