自己簡易打造的IOC注解框架:SteadyoungIOC

1 前言

前面的文章注解框架源碼分析(XUtils百侧、ButterKnife)ButterKnife編譯時生成代碼原理:butterknife-compiler源碼分析老赤,按照源碼的思路我們自己打造一款IOC注解框架唤崭,因為ButterKnife的源碼實現(xiàn)難度過大士聪,我先仿照xUtils的源碼方式某饰,反射注解實現(xiàn)。
雖說反射注解對性能有影響剃袍,但是影響是極小黄刚,相比I渲染和Bitmap以及Service和Handler上的內(nèi)存泄露不是一個量級的,編程一開始不糾結完美化民效,實現(xiàn)這個IOC框架是為了提升自己的編碼能力憔维,也是提高自己對項目整體代碼提高可控性涛救。

2 控件屬性注入

Annotation注解需要了解Java中Annotation用法Java Annotation 總結
屬性注入代碼:

/*
 * ElementType.FIELD 代表annotation的位置
 * FIELD:屬性注解
 * CONSTRUCTOR:構造器注解
 * METHOD:方法注解
 * TYPE:類上注解
 */
@Target(ElementType.FIELD)
/*
@Retention(RetentionPolicy.CLASS)什么時候生效
CLASS 編譯時
RUNTIME 運行時
SOURCR 源碼資源
 */
@Retention(RetentionPolicy.RUNTIME)
public @interface FindView {

    //@FindView(R.id.xxxx)
    @IdRes int value();
}
public class SteadyoungIOC {
    //activity使用
    public static void inject(Activity activity){
        inject(new ViewFinder(activity),activity);
    }

    //View使用
    public static void inject(View view){
        inject(new ViewFinder(view),view);
    }

    //Fragment使用
    public static void inject(View view,Object object){
        inject(new ViewFinder(view),object);
    }

    //兼容上述三種方式
    private static void inject(ViewFinder finder,Object object){
        injectField(finder,object);
        injectEvent(finder,object);
    }

    /**
     * 注入屬性
      * @param finder
     * @param object
     */
    private static void injectField(ViewFinder finder, Object object) {
        //1.獲取類里面的所有屬性
        Class<?> clazz = object.getClass();
        //獲取所有屬性包括私有和公有
        Field[] fields = clazz.getDeclaredFields();
        //2.獲取ViewById的里面的value值
        for(Field field : fields){
            FindView viewById = field.getAnnotation(FindView.class);
            if(viewById != null){
                int viewId = viewById.value();
                //3.findViewById找到View
                View view = finder.findViewById(viewId);
                if (view != null) {
                    // 4. 反射注入View屬性
                    // 設置所有屬性都能注入包括私有和公有
                    field.setAccessible(true);
                    try {
                        field.set(object, view);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                } else {
                    throw new RuntimeException("Invalid @ViewInject for "
                            + clazz.getSimpleName() + "." + field.getName());
                }
            }
        }

    }

    /**
     * 注入事件
     * @param finder
     * @param object
     */
    private static void injectEvent(ViewFinder finder, Object object) {
      //TODO
    }
public class ViewFinder {
    private Activity mActivity;
    private View mView;

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

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

    /**
     * 判斷容器然后 根據(jù)容器和控件ID獲取控件View
     * @param viewId
     * @return
     */
    public View findViewById(@IdRes int viewId){
        return mActivity!= null ? mActivity.findViewById(viewId) : mView.findViewById(viewId);
    }
}

3 點擊事件注入

我先實現(xiàn)setOnclickListener點擊事件业扒,其他事件后期實現(xiàn)检吆。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick {
    @IdRes int[] value();
}
    /**
     * 注入事件
     * @param finder
     * @param object
     */
    private static void injectEvent(ViewFinder finder, Object object) {
        //TODO
        //1.獲取類里面的所有方法
        Class<?> clazz = object.getClass();
        Method[] methods = clazz.getDeclaredMethods();
        //2.獲取OnClick的里面的Value值
        for ( Method method: methods ) {
            OnClick onClick = method.getAnnotation(OnClick.class);
            if(onClick != null){
                int[] viewIds = onClick.value();
                for( int viewId : viewIds){
                    //3.findViewById找到View
                    View view = finder.findViewById(viewId);
                    //4.view.setOnClickListener
                    view.setOnClickListener(new DeclaredOnClickListener(method,object));
                }
            }
        }
    }

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

        public DeclaredOnClickListener(Method mMethod, Object mObject) {
            this.mMethod = mMethod;
            this.mObject = mObject;
        }

        @Override
        public void onClick(View v) {
            try {
                //打開權限
                mMethod.setAccessible(true);
                mMethod.invoke(mObject,v);
            } catch (Exception e) {
                e.printStackTrace();
                try {
                    mMethod.invoke(mObject,null);
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
            }
        }
    }

4 擴展動態(tài)檢測網(wǎng)絡注解

最后擴展動態(tài)檢測網(wǎng)絡注解,可以在點擊某些按鈕或者圖片是需要訪問網(wǎng)絡前檢測網(wǎng)絡狀態(tài)程储,避免打不開網(wǎng)頁或者獲取不到數(shù)據(jù)等網(wǎng)絡問題的情況蹭沛!

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckNet {
    String value();
}
    /**
     * 注入事件
     * @param finder
     * @param object
     */
    private static void injectEvent(ViewFinder finder, Object object) {
        //TODO
        //1.獲取類里面的所有方法
        Class<?> clazz = object.getClass();
        Method[] methods = clazz.getDeclaredMethods();
        //2.獲取OnClick的里面的Value值
        for ( Method method: methods ) {
            OnClick onClick = method.getAnnotation(OnClick.class);
            if(onClick != null){
                int[] viewIds = onClick.value();
                for( int viewId : viewIds){
                    //3.findViewById找到View
                    View view = finder.findViewById(viewId);
                    //判斷是否有檢測網(wǎng)絡需求
                    boolean isCheckNet = method.getAnnotation(CheckNet.class) != null;
                    String message = null;
                    if(isCheckNet){
                        //獲取無網(wǎng)絡提示信息
                        message = method.getAnnotation(CheckNet.class).value();
                    }
                    //4.view.setOnClickListener
                    view.setOnClickListener(new DeclaredOnClickListener(method,object,isCheckNet,message));
                }
            }
        }
    }

    private static class DeclaredOnClickListener implements View.OnClickListener {
        private Method mMethod;
        private Object mObject;
        private boolean isCheckNet;
        private String message;

        public DeclaredOnClickListener(Method mMethod, Object mObject, boolean isCheckNet, String message) {
            this.mMethod = mMethod;
            this.mObject = mObject;
            this.isCheckNet = isCheckNet;
            this.message = message;
        }

        @Override
        public void onClick(View v) {
            if(isCheckNet){
                Log.d("isCheckNet", "onClick:111111111111111111 ");
                if(!isNetworkConnected(v.getContext())){
                    Toast.makeText(v.getContext(), TextUtils.isEmpty(message) ? "網(wǎng)絡不給力,請檢查網(wǎng)絡連接章鲤!" : message ,Toast.LENGTH_SHORT).show();
                    return;
                }
            }
            try {
                //打開權限
                mMethod.setAccessible(true);
                mMethod.invoke(mObject,v);
            } catch (Exception e) {
                e.printStackTrace();
                try {
                    mMethod.invoke(mObject,null);
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
            }
        }
    }

    /**
     *獲取網(wǎng)絡連接狀態(tài)
     * @param context
     * @return true網(wǎng)絡連接正常 false無網(wǎng)絡
     */
    private static boolean isNetworkConnected(Context context){
        if(context != null){
            ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
            if(networkInfo != null){
                return networkInfo.isAvailable();
            }
        }
        return false;
    }

下面放上測試代碼和測試結果:

public class MainActivity extends AppCompatActivity {

    /****Hello World!****/
    @FindView(R.id.test_tv)
    private TextView mTestTv;
    @FindView(R.id.test_iv)
    private ImageView mTestIv;
    /****TestButton****/
    @FindView(R.id.test_btn)
    private Button mTestBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        SteadyoungIOC.inject(this);
        mTestTv.setText("測試文本");
        mTestIv.setBackgroundColor(Color.RED);
        mTestBtn.setText("測試按鈕");
    }

    @OnClick(R.id.test_tv)
    private void testTvClick(TextView testTv) {
        Toast.makeText(this,"點擊了文字",Toast.LENGTH_SHORT).show();
    }

    @OnClick(R.id.test_iv)
    private void testIvClick(ImageView testIv) {
        mTestIv.setBackgroundColor(Color.BLUE);
        Toast.makeText(this,"點擊了圖片",Toast.LENGTH_SHORT).show();
    }

    @CheckNet("親摊灭!網(wǎng)絡不給力哦!")
    @OnClick(R.id.test_btn)
    private void testBtnClick(Button testBtn) {
        Toast.makeText(this,"點擊了按鈕",Toast.LENGTH_SHORT).show();
    }
}

steadyoungioctest.gif

steadyoungioc.gif

上圖中生成代碼的插件是我自己編寫的咏窿,后面的文章將講解插件代碼斟或。
附上源碼:https://github.com/Steadyoung/SteadyoungIOC
插件下載地址:SteadyoungIOC-CodePlug.jar

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末素征,一起剝皮案震驚了整個濱河市集嵌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌御毅,老刑警劉巖根欧,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異端蛆,居然都是意外死亡凤粗,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門今豆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嫌拣,“玉大人,你說我怎么就攤上這事呆躲∫熘穑” “怎么了?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵插掂,是天一觀的道長灰瞻。 經(jīng)常有香客問我,道長辅甥,這世上最難降的妖魔是什么酝润? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮璃弄,結果婚禮上要销,老公的妹妹穿的比我還像新娘。我一直安慰自己夏块,他們只是感情好蕉陋,可當我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布捐凭。 她就那樣靜靜地躺著,像睡著了一般凳鬓。 火紅的嫁衣襯著肌膚如雪茁肠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天缩举,我揣著相機與錄音垦梆,去河邊找鬼。 笑死仅孩,一個胖子當著我的面吹牛托猩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播辽慕,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼京腥,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了溅蛉?” 一聲冷哼從身側響起公浪,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎船侧,沒想到半個月后欠气,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡镜撩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年预柒,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片袁梗。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡宜鸯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出遮怜,到底是詐尸還是另有隱情淋袖,我是刑警寧澤,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布奈泪,位于F島的核電站适贸,受9級特大地震影響,放射性物質發(fā)生泄漏涝桅。R本人自食惡果不足惜拜姿,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望冯遂。 院中可真熱鬧蕊肥,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至展东,卻和暖如春赔硫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背盐肃。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工爪膊, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人砸王。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓推盛,卻偏偏與公主長得像,于是被迫代替她去往敵國和親谦铃。 傳聞我的和親對象是個殘疾皇子耘成,可洞房花燭夜當晚...
    茶點故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容