IOC實現ButterKnife(一)之注入佈局與注入控件

IOC是什麼?

是面向對象編程中的一種設計原則,可以用來減低計算機代碼之間的耦合度,其中最常見的便是依賴注入。簡單點說就是如果你在一個類A中實例化一個對象B,那麼久需要在A類中使用new來進行實例化對象B,這樣A和B的耦合度就很高,而IOC是通過註解讓其進行動態生成,而不需要自己去實例化,以此來降低耦合度。(依賴注入核心)

需要用到的知識:

反射
註解
	如果不熟悉上面兩個知識,可百度找一篇如果操作即可。

實現的最終結果:

注入佈局:即讓我們在代碼中不需要寫setContentView()方法,而是通過在類之上添加註解(MContentView)來動態生成:
在這裏插入圖片描述
注入控件:即在代碼中不需要寫findViewById方法,而是通過在屬性之上添加註解(MBIndView)來動態生成:
在這裏插入圖片描述

實現過程:

實現注入佈局過程:

思考1:要想實現在類上的註解,首先我們需要創建一個註解MContentView,而這個註解是作用在類之上,所以其作用域是類之上(ElementType.TYPE)並且我們的註解是在運行時使用的,所以聲明其是運行時使用(RetentionPolicy.RUNTIME)。因此創建一個註解如下:

@Target(ElementType.TYPE)//作用在類上
@Retention(RetentionPolicy.RUNTIME)//運行時
public @interface MContentView {
  
}

思考2:由最終效果可知,因爲我們需要代替setContentView(),而這個方法需要傳入layoutId(R.layout.activity_main),所以註解中還要添加一個int類型的方法,如下:

@Target(ElementType.TYPE)//作用在類上
@Retention(RetentionPolicy.RUNTIME)//運行時
public @interface MContentView {
    int value() ;
}

至此註解MContentView已完成,在MainActivity中即可引用該註解並附上layoutId,如下圖:
在這裏插入圖片描述

當寫好註解後,我們便需要通過這個註解來處理我們需要的邏輯,因此寫一個工具類(InjectTool)來實現,並在onCreate方法中將MainActivity引入,如圖:在這裏插入圖片描述

inject方法如圖:在這裏插入圖片描述

由上圖可知injectContentView()方法便是實現注入佈局,因此我們在該方法中實現注入佈局,而實現該方法的思路如下:

1、通過MainActivity的class信息獲取我們自定義的MContentView的註解
2、如果找到註解則獲取註解上的layoutId值
3、通過反射,反射除setContentView方法
4、執行setContentView方法
	具體代碼如下:
  private static void injectContentView(Object object) {
        //獲取對象的類
        Class<?> activity = object.getClass();
        //獲取類上的MContentView註解
        MContentView contentView = activity.getAnnotation(MContentView.class);
        //如果contentView等於空則代表沒有該註解,便直接返回
        if (contentView==null){
            Log.d(TAG, "injectContentView: 沒找到該註解");
            return;
        }
        //如果由該註解,則獲得該註解上的layoutId
        int value = contentView.value();
        try {
            //通過反射,反射除類activity裏的setContentView方法並執行該方法
            Method method = activity.getMethod("setContentView",//該參數是反射的方法名稱,因爲我們要執行setContentView(),所以反射setContentView
                    int.class);//反射方法中需要攜帶的參數的類型

            method.invoke(object,// 第一個參數是指該方法在哪個類中執行,因爲我們是在object對象中執行的setContentView方法,所以傳入object
                    value);//這個參數是指執行setContentView方法所需要的參數
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

實現注入控件:

1:和注入佈局一樣首先創建一個註解mBindView:

@Target(ElementType.FIELD)//作用在屬性之上
@Retention(RetentionPolicy.RUNTIME)
public @interface MBindView {
    int value() ;
}

2:在MainActivity中引用該註解:

在這裏插入圖片描述

3:在InjectTool中實現injectBindView(注入控件)方法,實現思路如下:

1、找到我們定義的MBindView註解:因爲其是作用在屬性之上的,所以我們需要過MainActivity的class信息先找到所有的屬性(getDeclaredFields)
2、獲取到屬性後,便獲取該屬性上的註解並判斷有無我們許喲啊的MBindView註解
3、獲取到MBindView註解後獲取其中的id值
4、通過反射反射出findViewById方法,並將id值傳入執行該方法
5、將執行後返回的對象複製給我們的屬性(即btn = findViewById(xxx)這個操作)

代碼如下:
   private static void injectBindView(Object object) {
        Class<?> activity = object.getClass();

        //遍歷所有字段
        Field[] declaredFields = activity.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            //獲取字段上的註解
            MBindView mBindView = declaredField.getAnnotation(MBindView.class);
            if (mBindView==null){
                Log.d(TAG, "injectBindView: 沒找到該註解");
                continue;
            }
            int value = mBindView.value();
            if (value==-1){
                Log.d(TAG, "injectBindView: 請設置綁定id");
                return;
            }
            try {
                Method findViewById = activity.getMethod("findViewById", int.class);
                Object invoke = findViewById.invoke(object, value);//執行findViewById方法(findViewById(xxx))
                declaredField.set(object,invoke);//將已經執行的findViewById複製給擁有註解的declareField對象,即(btn = findViewById(xxx)的複製操作)
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

MainActivity完整代碼如下:


@MContentView(R.layout.activity_main)
public class MainActivity extends AppCompatActivity {
    @MBindView(R.id.btn1)
    Button btn1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        InjectTool.inject(this);
        btn1.setText("HELLO YOYO");
    }
}

效果圖如下:
在這裏插入圖片描述

至此注入佈局和注入控件已完成,源碼將在下節IOC實現ButterKnife(二)之注入事件 中給出

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章