Custom Android IOC framework

Outline

What is the IOC

Inversion of Control, abbreviation of IOC, the control means is reversed.

Specifically what the meaning of it?

Suppose a class has a lot of member variables, if you need to use the inside of the member variable, traditional practices are carried out using a new, but in principle the IOC, we are not new, since such coupling is too high, we can add comments on the need to inject (new) member variables, waiting to load this class, then it is injected.

So how were injected into it?

Simply put, is reflected by the way, the path string class becomes class.

What is the reflection

JAVA not become a dynamic language, in order to make the language more flexible, it introduced JAVA reflection. JAVA reflection mechanism is in operation, for any class, can know all the properties and methods of this class; for any one property, are able to call any of its methods; this dynamic access to information and dynamic invocation of object methods become a reflection function JAVA language.

What is a comment

Annotations and after introduction of 1.5 reflecting the JAVA, annotations implementation relies on reflection. JAVA annotations in a succession of special interface from the interface java.lang.annotation.Annotation.
So how to set the interface properties it?
JAVA is simply a way to generate dynamic proxies for you to implement an interface Annotation instance, then the property assignment proxy instance, so that you can (if visible if the annotation is set to run) while the program runs through reflecting acquired configuration information to comment. He said plainly, is equivalent to a markup annotations, add a comment in the program is equivalent to the program marked some kind of marks. JAVA program can use reflection to understand whether what kind of mark on your class and various elements for different markers, just do the corresponding event. Mark may be applied to the package member variables and parameters, type, process, process.

achieve

Defined Note

  1. Layout comment
/**
 * Created by Keven on 2019/6/3.
 *
 * 布局注解
 */
//RUNTIME 运行时检测,CLASS 编译时检测  SOURCE 源码资源时检测
@Retention(RetentionPolicy.RUNTIME)
//TYPE 用在类上  FIELD 注解只能放在属性上  METHOD 用在方法上  CONSTRUCTOR 构造方法上
@Target(ElementType.TYPE)
public @interface KevenContentViewInject {
    int value();//代表可以 Int 类型,取注解里面的参数
}
复制代码
  1. Properties of the component notes
/**
 * Created by Keven on 2019/6/3.
 *
 * 组件注解
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)//用在属性字段上
public @interface KevenViewInject {
    int value();
}
复制代码
  1. Event Notes
/**
 * Created by zhengjian on 2019/6/3.
 *
 * 事件注解
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)//使用在方法上
public @interface KevenOnClickInject {
    //会有很多个点击事件,所以使用数组
    int[] value();
}
复制代码

Achieve injection tools

/**
 * Created by Keven on 2019/6/3.
 * <p>
 * InjectUtils 注入工具类
 */
public class InjectUtils {
    //注入方法 Activity
    public static void inject(Activity activity) {
        injectLayout(activity);
        injectViews(new ViewFinder(activity), activity);
        injectEvents(new ViewFinder(activity), activity);
    }

    //注入方法 View
    public static void inject(View view, Activity activity) {
        injectViews(new ViewFinder(view), activity);
        injectEvents(new ViewFinder(view), activity);

    }

    //注入方法 Fragment
    public static void inject(View view, Object object) {
        injectViews(new ViewFinder(view), object);
        injectEvents(new ViewFinder(view), object);
    }


    /**
     * 事件注入
     */
    private static void injectEvents(ViewFinder viewFinder, Object object) {
        // 1.获取所有方法
        Class<?> clazz = object.getClass();
        Method[] methods = clazz.getDeclaredMethods();
        // 2.获取方法上面的所有id
        for (Method method : methods) {
            KevenOnClickInject onClick = method.getAnnotation(KevenOnClickInject.class);
            if (onClick != null) {
                int[] viewIds = onClick.value();
                if (viewIds.length > 0) {
                    for (int viewId : viewIds) {
                        // 3.遍历所有的id 先findViewById然后 setOnClickListener
                        View view = viewFinder.findViewById(viewId);
                        if (view != null) {
                            view.setOnClickListener(new DeclaredOnClickListener(method, object));
                        }
                    }
                }
            }
        }
    }


    private static class DeclaredOnClickListener implements View.OnClickListener {
        private Method mMethod;
        private Object mHandlerType;

        public DeclaredOnClickListener(Method method, Object handlerType) {
            mMethod = method;
            mHandlerType = handlerType;
        }

        @Override
        public void onClick(View v) {
            // 4.反射执行方法
            mMethod.setAccessible(true);
            try {
                mMethod.invoke(mHandlerType, v);
            } catch (Exception e) {
                e.printStackTrace();
                try {
                    mMethod.invoke(mHandlerType, null);
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
            }
        }
    }

    //控件注入
    private static void injectViews(ViewFinder viewFinder, Object object) {
        //获取每一个属性上的注解
        Class<?> myClass = object.getClass();
        Field[] myFields = myClass.getDeclaredFields();//先拿到所有的成员变量
        for (Field field : myFields) {
            KevenViewInject myView = field.getAnnotation(KevenViewInject.class);
            if (myView != null) {
                int value = myView.value();//拿到属性id
                View view = viewFinder.findViewById(value);
                //将view 赋值给类里面的属性
                try {
                    field.setAccessible(true);//为了防止其是私有的,设置允许访问
                    field.set(object, view);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static void injectLayout(Activity activity) {
        //获取我们自定义类KevenContentViewInject 上面的注解
        Class<?> myClass = activity.getClass();
        KevenContentViewInject myContentView = myClass.getAnnotation(KevenContentViewInject.class);
        if (myContentView!=null){
            int myLayoutResId = myContentView.value();
            activity.setContentView(myLayoutResId);
        }

    }

}
复制代码

Class definitions ViewFinder

Tools for implanting in findViewById

/**
 * Created by Keven on 2019/6/3.
 */
final class ViewFinder {

    private View view;
    private Activity activity;

    public ViewFinder(View view) {
        this.view = view;
    }

    public ViewFinder(Activity activity) {
        this.activity = activity;
    }

    public View findViewById(int id) {
        if (view != null) return view.findViewById(id);
        if (activity != null) return activity.findViewById(id);
        return null;
    }


    public View findViewById(int id, int pid) {
        View pView = null;
        if (pid > 0) {
            pView = this.findViewById(pid);
        }

        View view = null;
        if (pView != null) {
            view = pView.findViewById(id);
        } else {
            view = this.findViewById(id);
        }
        return view;
    }

    /*public Context getContext() {
        if (view != null) return view.getContext();
        if (activity != null) return activity;
        return null;
    }*/
}
复制代码

Using an IOC Framework

Layout file

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ioc.IocActivity">
    <TextView
        android:id="@+id/tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/dimen_35dp"
        android:text="你好,IOC"
        android:textSize="25sp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>
    <Button
        android:id="@+id/bt_pop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/dimen_35dp"
        android:text="弹窗"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tv_title"/>

</android.support.constraint.ConstraintLayout>
复制代码

Activity Code

//布局文件注入
@KevenContentViewInject(R.layout.activity_ioc)
public class IocActivity extends AppCompatActivity {

    //属性控件注入
    @KevenViewInject(R.id.tv_title)
    private TextView tv_title;
    @KevenViewInject(R.id.bt_pop)
    private Button bt_pop;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //注入工具绑定
        InjectUtils.inject(this);
    }
    
    //点击事件注入
    @KevenOnClickInject(R.id.bt_pop)
    public void change(){
        tv_title.setText("hello IOC");
        Toast.makeText(this,"Hello IOC",Toast.LENGTH_SHORT).show();
    }
}
复制代码

When we click the button to pop above the TextView content will change, and there is Toast pop.

Reproduced in: https: //juejin.im/post/5cf4e6336fb9a07ecc446fdc

Guess you like

Origin blog.csdn.net/weixin_33695082/article/details/91412384