《撸代码 学习 IOC注入技术1 》—— 布局注入 与 控件注入

不诗意的女程序媛不是好厨师~
转载请注明出处,From李诗雨---https://blog.csdn.net/cjm2484836553/article/details/104539874

在这里插入图片描述

在前面的文章中我们已经学习了 依赖注入与控制反转的概念注解、和反射 ,有了这些知识做铺垫,我们就可以 更加深入的来学习一下 IOC注入技术了。

今天我们主要 来学习运行时注入,并亲自撸代码来一步一步的实现 布局注入控件注入

文章的逻辑思路讲的很细,也很好懂,没有什么难点,并且文章篇幅也不长,不妨一读哦~

1.概念再理解

温故而知新,上篇文章中跟大家提到了 控制反转(IOC) 和 依赖注入的概念,可能大家还是有点 花非花雾非雾的 感觉,今天经过亲自的撸代码之后,我有了新的体会。在此与大家分享~

【控制反转(IOC)】:是原来由程序代码中主动获取的资源,转变由第三方获取并使原来的代码被动接收的方式,以达到解耦的效果。

按照上篇文章的内容,我们把它看成是一种控制权的反转。

但其实,我们还可以把它看成是一种义务的转交,即 把我们自己应该做的事转交给别人来做,从而让自己变得更轻松。

再举个形象的栗子来说吧:

在一个月黑风高的寒冷的夜晚,你有事要出门,由于天气太冷你要披肩大棉袄才能出去,于是你就自己乖乖的拿了棉袄再乖乖的穿好出门,消失在寒冷的黑夜中。

IOC就是你有了一个女朋友,你只告诉她你要出门,于是贴心的女朋友便给你拿来棉衣,帮你穿上,才放心让你出门。于是你在爱的目光中出了门~

恩,女朋友就好比IOC,把你本来要拿衣服穿衣服的事情 转交给了女朋友来做。

画个图来帮助大家理解:


在这里插入图片描述

好的,现在我们就开始撸代码来学习 IOC注入技术吧~

2.布局注入

在这里插入图片描述

我们都知道在Activity中我们 通过自己的 setContentView(R.layout.activity_main)来加入、显示布局的。

那如果我现在采用ioc,不是自己来注入布局,而是让我的女朋友来注入布局,该怎么做呢?

  • ①首先,我得造一个女朋友出来!她里面有布局注入的方法。
  • ②其次,我们考虑到可能所有的Activity都要用到,所以,我们在BaseActivity的onCreat中完成注入。
  • ③MainActivity继承BaseActivity。并且把setContentView(R.layout.activity_main)这句代码去掉!
//①造了一个女朋友
public class InjectUtils {
    public static void inject(Object context) {
        //布局的注入
        injectLayout(context);
    }

    private static void injectLayout(Object context) {

    }
}
public class BaseActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        InjectUtils.inject(this);//②在这里注入
    }
}
//③继承BaseActivity ,并去掉setContentView(R.layout.activity_main)这句代码
public class MainActivity extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.activity_main);//把这行代码去掉!让女朋友来完成
    }
}

好的,到这里大家应该都没有什么问题吧。

现在大家想想,我们既然去掉了setContentView(R.layout.activity_main);这句代码,那此时我们的MainActivity是不知道需要哪个布局的。

这该怎么办?怎么才能知道MainActivity需要哪个布局文件呢?

那我们就要标识出来我们所需要的布局文件呀,那怎么标识呢?

对!用注解。就像这样:


在这里插入图片描述

那接下来我们就要来自定义这个注解啦~

  • ④自定义注解MyContentView。
//④自定义注解MyContentView
@Target(ElementType.TYPE)   //表明:注解将来是使用在类上面的
@Retention(RetentionPolicy.RUNTIME) //表明注解的存活周期,我们希望可以在运行时读取到它的信息
public @interface MyContentView {
    int value();
}

好了,到目前为止,我们的主要逻辑就完成了。但是,此时运行还是不能加载出布局的,因为这还是个假货,我们InjectUtils中的injectLayout()还是空的,里面什么都没有做。

所以,接下来我们的重点就是实现injectLayout()方法了。

⑤实现injectLayout()方法:

我们先来分析一下,在该方法中我们要做什么:

首先我们要明确的是,此处我们肯定要 运用反射 去获取所需信息和执行对应方法了。

  • 第一步 获取activity对应的Class
  • 第二步 拿到该Class上的MyContentView注解
  • 第三步 取到注解括号后面的内容,即布局id

再接下来 就要 反射在class上去执行setContentView了:

  • 第四步 利用反射获取setContentView()对应的method
  • 第五步 反射执行setContentView()方法。
//⑤实现injectLayout()方法
private static void injectLayout(Object context) {
    // a.获取到Activity对应的Class
    Class<?> clazz = context.getClass();
    // b.拿到该Class上的MyContentView注解
    MyContentView myContentView = clazz.getAnnotation(MyContentView.class);
    if (myContentView != null) { //如果有MyContentView注解就执行以下操作
        // c.取到注解括号后面的内容,即布局id
        int layoutId = myContentView.value();
        //====== 接下来就要 反射去执行setContentView
        try {
            // d.利用反射获取setContentView()对应的method
            Method method = clazz.getMethod("setContentView", int.class);
            // e.反射执行setContentView()方法。即相当于context.method(layoutId);
            method.invoke(context, layoutId);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

好的,那现在我来运行程序,如果可以正常显示出来布局是不是就可以证明,我注入布局成功啦!

先给大家看一下我的布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    android:padding="10dp"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:text="ioc注入技术,哈哈哈~" />

    <Button
        android:id="@+id/button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="按钮1" />

    <Button
        android:id="@+id/button2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="按钮2" />

</LinearLayout>

好的,下面就是见证奇迹的时刻啦:

在这里插入图片描述

成功啦!我成功啦,啊哈哈哈哈~

完成了布局注入,那我们下面继续控件注入吧~

3.控件注入

上面我们的布局已经注入成功,并且可以正常显示了。

我们可以看到布局中有2个按钮,那如果我想把这两个按钮注入该怎么办呢?

即:我现在不想自己通过findViewById来注入按钮,而是想让我的【ioc女朋友】来帮我实现按钮的注入~

我们先来看一下我的预期想达到的效果:


在这里插入图片描述

那要达到这种效果我们该怎么实现呢?

有了布局注入的经验,相信对于 控件注入 大家还是会有大体的思路的:

我们还用之前的女朋友InjectUtils,还是在BaseActivity中进行注入。

那我们就要在InjectUtils里添加一个控件注入的方法injectView():


在这里插入图片描述

现在我们既然不想自己使用findViewById来获取控件,而是想用这种形式来注入控件:


在这里插入图片描述

那我们肯定还是要通过使用注解,并且在注解后面传入对应控件的id。

所以第①步,我们要自定义一个BindView注解:

//① 自定义一个BindView注解
@Target(ElementType.FIELD) //说明该注解是用在属性上的
@Retention(RetentionPolicy.RUNTIME)//该注解可以保留到程序运行的时候
public @interface BindView {
    int value();
}

第②步,具体实现injectView()方法。

实现injectView()方法是重点,让我们来仔细分析一下思路:

  • 首先,我们肯定还是要通过反射,所以要先拿到Activity对应的Class.
  • 拿到了clazz后,我们还要拿到clazz上的所有属性字段(Fields)。▲▲▲
  • 然后我们就要循环遍历属性,看属性上是否有BindView注解。
  • 如果属性上确实拿到了BindView注解,那我们就要继续拿到注解后面的viewId了。
  • 再接着就是反射执行findViewById方法,得到对应的view.
  • 最后要注意,对于私有属性,无论是对它进行读写,都要调用field.setAccessible(true)。▲
private static void injectView(Object context) {
    //获取clazz
    Class<?> clazz = context.getClass();
    //获取clazz上的所有属性
    Field[] fields = clazz.getDeclaredFields();
    //循环遍历每一个属性
    for (Field field : fields) {
        //获取属性上的BindView注解
        BindView bindView = field.getAnnotation(BindView.class);
        if (bindView != null) {//如果该属性上找到了BindView注解
            //拿到注解后面的viewId
            int viewId = bindView.value();
            //运行到这里,每个按钮的ID已经取到了
            //下面就是反射执行findViewById方法
            try {
                Method method = clazz.getMethod("findViewById", int.class);
                View view = (View) method.invoke(context, viewId);
                //对 field 做相关操作
                //注意:如果获取的字段是私有的,不管是读还是写,都要先 field.setAccessible(true);才可以。否则会报:IllegalAccessException。
                field.setAccessible(true);
                field.set(context, view);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

好的,现在我们控件注入的相关操作就完成了,那让我们来改个button的名称测试一下吧:

@MyContentView(R.layout.activity_main)
public class MainActivity extends BaseActivity {

    @BindView(R.id.button1)
    Button btn1;
    @BindView(R.id.button2)
    Button btn2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //检测 控件注入 是否成功
        btn1.setText("我是注入的按钮01");
        btn2.setText("我是注入的按钮02");
    }
}

下面还是见证奇迹的时刻:


在这里插入图片描述

怎么样是不是不撸不知道,一撸代码才知道原来这就是IOC技术啊,也蛮容易的嘛~


在这里插入图片描述

是的,布局注入和控件注入我们都轻松搞定啦。

还有一个事件注入我们没有实现,这个事件注入就会有点小难度了哟。

害怕文档太长,大家懒得看(PS:其实是因为我懒),

那我们就在下篇继续来撸代码一步一步实现 事件注入 吧~~~

积累点滴,做好自己~!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容

  • 什么是IOC注入框架 ButterKnife大家都应该使用过,对于view的注入减少了大量篇幅的findViewB...
    酷酷的Demo阅读 1,954评论 0 4
  • 前言 在讲IOC(依赖注入)前 先来看一段代码 或者下载demo[https://github.com/daiwe...
    明朗__阅读 1,020评论 0 9
  • 什么是IOC注入框架 IOC-控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面...
    Coder_Sven阅读 170评论 0 0
  • 在刚接触安卓的第二天 , 自己最熟悉的代码 , 就是那句findViewById. 记得当时特别舒服的啪啪啪敲完一...
    MinuitZ阅读 748评论 1 14
  • 半夜醒来,拿起床头柜上的手机,想看看一觉睡到了几点。手机屏幕上显示几条未读微信,遂打开却看到了好朋友的几条留言。 ...
    枣园草阅读 475评论 5 5