使用依赖注入最大的好处就是你不需要知道一个对象是怎么来的了,你只管使用它,这可以让你的代码更加整洁.
并且如果后来它的构造函数或者是具体实现类发生了改变,那都与你现在所写的代码无关,它们的改变不会迫害你去更新现有的代码.
而在传统的软件开发过程中,我们通常要在一些控制器中去主动依赖一些对象,如果这些对象的依赖方式在未来频繁地发生改变,那我们的程序是无法经受住考验的.
这就是所谓控制反转,它将获得依赖对象的方式反转了.
2.常见的依赖注入框架
- 在服务器后端,一般使用
Spring
框架进行依赖注入。 - 在
Android
上,一般使用Dagger
系列进行依赖注入。
3.实现自己的依赖注入框架
有些同学可能知道Dagger
实现了Java的依赖注入标准(JSR-330
),这个标准使用的有些注解确实让人有点摸不着头脑,而且Dagger
使用的门槛也较高,估计应该有不少人看了许多《Dagger
完全入门》之类的文章,然而到最后还是没搞懂Dagger
到底是怎么一回事.
所以我就想,能不能搞一个稍微亲民一点的依赖注入框架让我直接先能用上.我不是大神,所以它不一定要实现JSR-330
,也不一定使用注解处理器来追求极致的效率,但它必须要好理解,里面的概念必须是常见的.
在参考了服务器上Spring
框架的依赖注入后,我决定使用xml
作为依赖注入的配置文件,本来想上Github
看看有没有现成的轮子可以让我"抄抄"之类的,谁知道逛了一圈下来之后才发现Android
开发者除了Dagger
和Dagger2
根本没得选,这更加坚定了我造轮子的信心.
使用xml
是有优势的,xml
是最常见的配置文件,它能更明确的表达依赖关系。所以就有了Liteproj
这个库与Dagger
不同,Liteproj
不使用Java
来描述对象间的依赖关系,而是像Spring
一样使用xml
.
Liteproj
目前的实现中也没有使用注解处理器而是使用了反射,因为Liteproj
追求的并非是极致的性能,而是便于理解和上手以及轻量化和易用性,它的诞生并不是为了取代Dagger2
或者其他的一些依赖注入工具,而是在它们所没有涉及的领域做一个补全。
客官请移步 : Liteproj
4.xml解析
既然选择了xml
,那么就要需要解决解析xml
的问题.
经过考虑之后最终选择了dom4j作为xml
解析依赖库.其实Android
本身自带了xml
的解析器,而且它的效率也不错,那我为什么还要使用dom4j
呢,那当然是因为它好用啊。Android
自带的xml
解析器是基于事件驱动的,而dom4j
提供了面向对象的xml
操作接口,我觉得这会给我的编码带来极大的便利,可以降低开发难度.
比如dom4j
中的Document->Element->Attribute
等抽象,非常好地描述了xml
的结构,你甚至无需看它的文档就能简单上手,这可比XmlPullParser
中定义的一堆常量和事件好理解多了.
而且dom4j
也是老牌的xml
解析库,大名鼎鼎的hibernate
也使用它来解析xml
配置文件.
解析xml
,首先要解决assets
文件夹下的xml
文件解析问题,这个还算比较好处理,使用AssetManager
获取Java
标准流,然后把他交给dom4j
解析就可以了。
但是想要解析res/xml
文件夹下的xml
就比较麻烦了,熟悉安卓的人应该都知道,打包后的APK
,res
文件夹下除了raw
文件夹会原样保留,其他文件夹里的内容都会被编译压缩,为了解析res/xml
下的xml
,我依赖AXML这个库编写了一个Axml
到dom4j
的转换层,这样一来解析结果就可以共用一套依赖图生成方案。
由此Liteproj
现在支持解析assets
、res/raw
、res/xml
三个位置的xml
文件,使用@Using
注解在你需要注入的组件中标注你要使用那些xml
@Retention(RUNTIME)
@Target({TYPE})
public @interface Using
{
@XmlRes
@RawRes
int[] value();//res/xml 或 res/raw 文件夹下的xml
String[] assets() default {};//assets 文件夹下的xml
}
//使用@Using注解
@Using({R.xml.all_test,R.xml.test2,R.raw.test2,assets = {“test3.xml”}})
public class MainActivity extends AppCompatActivity
{
//TODO
}
5.对象构造适配
Java
是一门灵活的程序设计语言,由此诞生了多种对象构造方式。如传统的使用构造函数构造对象,又或者是工厂模式,Builder
模式,JavaBean
模式等。Liteproj
必须从一开始就兼容这些现有方案,否则就是开倒车了。
在Liteproj
中你需要为你的依赖关系在xml
中编写一些配置.
第一行是惯例的<?xml version="1.0" encoding="utf-8"?>
,第二行是最外层是dependency
标签,这个标签必须要指定一个owner
的属性来指定此依赖配置文件所兼容的类型,下面的xml
中我指定了android.app.Application
作为此xml
所兼容的类型,那么所有从这个类型派生
的类型都可以使用这个配置文件(其他类型在满足一定条件时也可以使用,见下文标题"生命周期和对象所有权")
- 使用
new
生成对象
首先从最原始的对象生成方式开始,下面的代码将会使用new
来构造对象.
在配置文件中,你可以使用var
标签声明一个依赖,并用name
属性指定它在上下文中的唯一名字,使用type
属性指定它的类型,使用provider
属性指定它的提供模式,有两种模式可以选择,singleton
和factory
,singleton
保证每次返回的对象都是相同的,而factory
则是每次都会重新创建一个新的对象,factory
还是默认的行为,你可以不写provider
属性,那么它默认就是factory
的.
然后var
标签中包裹的new
标签表明此依赖使用构造函数创建,使用arg
标签填入构造函数的参数并用ref
属性引用一个上文中已经存在的另一个已经声明的var
的name
.
这里我引用了一个特殊的name
->owner
,这个依赖不是你使用var
声明的,而是默认导入的,也就是我们的android.app.Application
实例,除此之外还有另外一个特殊的var
,那就是null
,它永远提供Java
中的null
值.
Liteproj
会按照arg
标签ref
所引用的类型的顺序自动去查找类的public
构造函数.不过Liteproj
的对象生成是惰性
的,这意味这只有你真正使用到该对象它才会被创建,在xml
中配置的其实是依赖关系.
//xml配置文件
<?xml version="1.0" encoding="utf-8"?>//java bean
public class AppHolderTest
{
final Context context;
public AppHolderTest(Context context)
{
this.context = context;
}
@Override
public String toString()
{
return super.toString() + context;
}
}
- 使用
Builder
模式
Liteproj
也支持使用Builder
模式创建对象,这在xml
配置中都很直观.
使用builder
标签指定此依赖使用Builder
模式生成,指定builder
的type
为okhttp3.Request$Builder
,使用action
标签指定最后是调用build
方法生成所需要的对象(当然这也是默认行为,你可以不写出action
属性),并使用arg
标签给builder
赋值,不过要注意,这里的arg
标签是有name
的,它将会映射到Builder
对象的方法调用上去给Builder
赋值.
- 使用工厂模式
下面的代码模拟了工厂模式的使用场景.
使用factory
标签表明此依赖使用工厂函数生成,使用type
属性标明工厂类,并使用action
标明需要调用的工厂函数.
你可能注意到了下面出现了一个新的属性val
,它是用来引用字面值的,之前的ref
只能引用标注名字的var
但是无法引用字面值,所以我加入了一个新的属性val
,它可以在arg
标签中使用,与ref
属性不能同时出现,如果val
以一个@
开头,那么它的内容就是@
后面的的字符串,否则他会被转换成数字或布尔值.
//一个简单的工厂类,包含一个工厂方法test
public class Factory
{
public static Bean test(String text)
{
Log.d(“test”,text);
return new Bean();
}
}
public class Bean
{
public float field;
public String string;
Object object;
public void setObject(Object object)
{
this.object = object;
}
@Override
public String toString()
{
return super.toString() + “\n” + field + “\n” + object + “\n” + string;
}
}
- 使用
JavaBean
代码还是上面的代码,只不过这次加了点东西,factory
,builder
,new
定义了对象的构造方式,我们还可以用field
和property
标签在对象生成后为对象赋值,通过name
属性指定要赋值给哪个字段或属性,property
所指定的name
应该是一个方法,它的命名应该符合Java
的setter
标准,比如name="abc"
,对应void setAbc(YourType)
方法
- 将
val
转换为var
我知道每次重复写字面值很蠢,所以提供了val
转换为var
的方法,让字面值可以像var
一样被ref
使用
- 完整的
xml
最后在这里提一点无论是factory
还是builder
都不允许返回null
值,默认导入的null
只是为了兼容某些特殊情况而设计的,factory
和builder
返回null
是没有意义的.
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
建议
当我们出去找工作,或者准备找工作的时候,我们一定要想,我面试的目标是什么,我自己的技术栈有哪些,近期能掌握的有哪些,我的哪些短板 ,列出来,有计划的去完成,别看前两天掘金一些大佬在驳来驳去 ,他们的观点是他们的,不要因为他们的观点,膨胀了自己,影响自己的学习节奏。基础很大程度决定你自己技术层次的厚度,你再熟练框架也好,也会比你便宜的,性价比高的替代,很现实的问题但也要有危机意识,当我们年级大了,有哪些亮点,与比我们经历更旺盛的年轻小工程师,竞争。
-
无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,这四个字就是我的建议!!!!!!!!!
-
准备想说怎么样写简历,想象算了,我觉得,技术就是你最好的简历
-
我希望每一个努力生活的it工程师,都会得到自己想要的,因为我们很辛苦,我们应得的。
-
有什么问题想交流,欢迎给我私信,欢迎评论
【附】相关架构及资料
内含往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术
生活的it工程师,都会得到自己想要的,因为我们很辛苦,我们应得的。
- 有什么问题想交流,欢迎给我私信,欢迎评论
【附】相关架构及资料
[外链图片转存中…(img-Rb2T5XfQ-1711632974564)]
[外链图片转存中…(img-0jpE2wDr-1711632974564)]
内含往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术