Dependency Injection with Dagger 2
原文:Dependency Injection with Dagger 2
创建单例
下面展示一个简单的Dagger来管理所有单例创建的例子:
原文:Dependency Injection with Dagger 2
下面展示一个简单的Dagger来管理所有单例创建的例子:
Dagger 是一款依赖注入框架。
项目地址:dagger
官方文档:Dagger
依赖注入(Dependency Injection),简称DI,又叫控制反转(Inversion of Control),简称IOC。
当一个类的实例需要另另一个类的实例进行协助时,在传统的设计中,通常由调用者来创建被调用者的实例,然而依赖注入的方式,创建被调用者不再由调用者创建实例,创建被调用者的实例的工作由IOC容器来完成,然后注入到调用者。因此也被称为依赖注入。
作用:将各层的对象以松耦合的方式组织在一起,解耦,各层对象的调用完全面向接口。当系统重构或者修改的时候,代码的改写量将大大减少。
1 | implementation 'com.google.dagger:dagger-android:2.17' |
转眼间 2018 已经结束了。还是很丰富的一年。
我是一个仪式感很强的人,临近新年时,并没有别人那种期待欢喜的感觉,却总是有一种离别的失落感。
5 月底的时候终于离职了,在经过一个月的项目交接之后我离开了毕业后加入的第一家公司,对老东家谈不上感激,大家也就是好聚好散。这算是我第一份正式的工作,还是学到了很多的东西的,包括技术、项目、合作等等,因为对未来的规划以及薪资上的问题 3 月底就确定要离职,之后一直在准备面试。因为准备的时间很长,准备的也比较充分,两天就拿到两个 offer,虽然都不是大厂,其中选择了一家比较有前景的公司就直接入职了。后来觉得太着急了,其实该多面试找找机会的。在公司的这半年时间里,经历了还是很多的,封闭开发,两个月的调休期,拖欠工资,公司倒闭…..好充实的。
Python 还是一直在学的,但是效率太低,发现问题在于只是看书或者教程,自己很少写代码,而且完全没有在工作中用到。后来自己试着去写 wallhaven 的爬虫,学着去操作数据库,尝试 web开发。未来学习的方向想放在商业化爬虫以及数据可视化处理上。
Android 还是一直在发展的,今年的主要研究方向在项目框架、Kotlin、RxJava 还有 Dagger,“纸上得来终觉浅 ” 在学习的过程中有时候感觉这些知识点自己已经掌握了,但是到了自己实际使用的时候还是会出现很多的问题,还有就是学完之后一定要在项目中去使用,去试着思考使用场景、bug、设计模式等等,否则遗忘的时间是很快的。在学习 Kotlin,Python 以及阅读 Github 优秀开源项目的时候,逐渐理解了所谓的编程思想,总之学无止境,还有很多需要进步的地方。
Android 行业今年有些疲软,工作岗位已经少了很多了,但我相信“高级程序员”的数量还是很少的,暂时还是打算继续走这条路。今年有一点小小的学习感触,一段时间内最好是只学习一个知识点,大量的寻找相关的博客、教程等等,关键的地方一定要做笔记,有一段时间是很浮躁的,因为发现有特别多需要学习的,又没有好的学习方式,所以导致心态有些问题。后来看到胡适先生的一句话“怕什么真理无穷,进一寸有一寸的欢喜”,豁然开朗。
由于Android系统的开放性,任何用户、开发者、硬件厂商、运营商都可以对Android系统和硬件进行定制,修改成他们想要的样子。 那么这种“碎片化”到达什么程度呢?
以上每一个矩形都代表一种机型,且它们屏幕尺寸、屏幕分辨率大相径庭。随着Android设备的增多,设备碎片化、系统碎片化、屏幕尺寸碎片化、屏幕碎片化的程度也在不断加深。
当 Android 系统、屏幕尺寸、屏幕密度出现碎片化的时候,就很容易出现同一元素在不同手机上显示不同的问题。试想一下这么一个场景: 为 4.3 寸屏幕准备的 UI 设计图,运行在 5.0 寸的屏幕上,很可能在右侧和下侧存在大量的空白;而 5.0 寸的 UI 设计图运行到 4.3 寸的设备上,很可能显示不下。
为了保证用户获得一致的用户体验效果,使得某一元素在 Android 不同尺寸、不同分辨率的、不同系统的手机上具备相同的显示效果,能够保持界面上的效果一致,我们需要对各种手机屏幕进行适配!
单位:px(pixel),像素就是手机屏幕的最小构成单元
px(pixel),1px = 1像素点,手机在横向、纵向上的像素点数总和 一般描述成 宽高 ,即横向像素点个数 纵向像素点个数(如1080 x 1920)。
单位 英寸(inch),手机对角线的物理尺寸
单位:dpi,每英寸的像素点数。 例如每英寸内有 160 个像素点,则其像素密度为 160dpi。计算公式:像素密度 = 像素 / 尺寸 (dpi = px / in)
density-independent pixel,叫dp或dip,与终端上的实际物理像素点无关。单位:dp,可以保证在不同屏幕像素密度的设备上显示相同的效果,是安卓特有的长度单位。dp 与 px 的转换:1dp = (dpi / 160 ) * 1px
scale-independent pixel,叫sp或sip。单位:sp,字体大小专用单位 Android开发时用此单位设置文字大小。
DPI 的存在,不就是为了让大屏能显示更多的内容
参考项目地址:https://github.com/JessYanCoding/AndroidAutoSize
今日头条适配方案默认项目中只能以高或宽中的一个作为基准。
density 是 DisplayMetrics 中的成员变量,而 DisplayMetrics 实例通过 Resources#getDisplayMetrics 可以获得,而Resouces 通过 Activity 或者 Application 的 Context 获得。
如果每个 View 的 dp 值是固定不变的,那我们只要保证每个设备的屏幕总 dp 宽度不变,就能保证每个 View 在所有分辨率的屏幕上与屏幕的比例都保持不变,从而完成等比例适配,并且这个屏幕总 dp 宽度如果还能保证和设计图的宽度一致的话,那我们在布局时就可以直接按照设计图上的尺寸填写 dp 值
屏幕的总 px 宽度 / density = 屏幕的总 dp 宽度
在这个公式中我们要保证 屏幕的总 dp 宽度 和 设计图总宽度 一致,并且在所有分辨率的屏幕上都保持不变,我们需要怎么做呢?屏幕的总 px 宽度 每个设备都不一致,这个值是肯定会变化的,这时今日头条的公式就派上用场了
当前设备屏幕总宽度(单位为像素)/ 设计图总宽度(单位为 dp) = density
这个公式就是把上面公式中的 屏幕的总 dp 宽度 换成 设计图总宽度,原理都是一样的,只要 density 根据不同的设备进行实时计算并作出改变,就能保证 设计图总宽度 不变,也就完成了适配。
布局文件中 dp 的转换,最终都是调用 TypedValue#applyDimension(int unit, float value, DisplayMetrics metrics)
来进行转换:
1 | public static float applyDimension(int unit, float value, DisplayMetrics metrics){ |
这里用到的 DisplayMetrics 正是从 Resources 中获得的。图片的decode,也是通过 DisplayMetrics 中的值来计算的。
当然还有些其他 dp 转换的场景,基本都是通过 DisplayMetrics 来计算的,这里不再详述。因此,想要满足上述需求,我们只需要修改 DisplayMetrics 中和 dp 转换相关的变量即可。
适配后的 density = 设备真实宽(单位px) / 360,接下来只需要把我们计算好的 density 在系统中修改下即可,代码实现如下:
1 | private static float sNonCompatDensity; |
会影响第三方控件
解决方案:
这个方案的的使用方式和我们平时在布局中引用 dimens 无异,核心点在于生成 dimens.xml 文件,但是已经有大神帮我们做了这 一步。
1 | ├── src/main |
如果有人还记得上面这种 宽高限定符屏幕适配方案 的话,就可以把 smallestWidth 限定符屏幕适配方案 当成这种方案的升级版,smallestWidth 限定符屏幕适配方案 只是把 dimens.xml 文件中的值从 px 换成了 dp,原理和使用方式都是没变的,这些在上面的文章中都有介绍,下面就直接开始剖析原理,smallestWidth 限定符屏幕适配方案 长这样
1 | ├── src/main |
其实 smallestWidth 限定符屏幕适配方案 的原理也很简单,开发者先在项目中根据主流屏幕的 最小宽度 (smallestWidth) 生成一系列 values-sw
如果系统根据当前设备屏幕的 最小宽度 (smallestWidth) 没找到对应的 values-sw
根据 今日头条屏幕适配方案 优化的屏幕适配框架。
例如 Android 屏幕适配方案,不过已经停止维护
1 | ├── src/main |
Android 屏幕适配终结者 ,也是基于头条的原理,不过是操作 pt
,所以不是改 DisplayMetrics#density
,而是 DisplayMetrics#xdpi
,由于适配不会失效
下面是摘自 ReactiveX 官网 的一段话。
ReactiveX is a library for composing asynchronous and event-based programs by using observable sequences.(一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库)
其中提到了几个概念,观察者模式、响应式编程。
概念:响应式编程是一种通过异步和数据流来构建事物关系的编程模型。
一般的编码模式中,“人”在其中扮演了过重的角色,关心程序中的每一部分。某种意义上这是一种顺序性思维的编程,我要做什么,然后做什么,最后做什么,按部就班编写就好了。具体如下图:
而响应式编程,全都是事物与事物之间的关系,解脱了”人”,之后一个事物发生变化另一个事物就自动响应。如下:
个人感觉响应式编程就是用异步数据流进行编程。流是响应式的核心,可以基于任何东西创建数据流,响应式编程就是根据数据流的流向进行一系列的操作。
观察者模式(Observer Pattern):定义了对象间的一种一对多的依赖关系,当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。
观察者模式面向的需求是:A 对象(观察者)对 B 对象(被观察者)的某种变化高度敏感,需要在 B 变化的一瞬间做出反应。举个例子,新闻里喜闻乐见的警察抓小偷,警察需要在小偷伸手作案的时候实施抓捕。在这个例子里,警察是观察者,小偷是被观察者,警察需要时刻盯着小偷的一举一动,才能保证不会漏过任何瞬间。程序的观察者模式和这种真正的『观察』略有不同,观察者不需要时刻盯着被观察者(例如 A 不需要每过 2ms 就检查一次 B 的状态),而是采用 注册(Register) 或者称为 订阅(Subscribe) 的方式,告诉被观察者:我需要你的某某状态,你要在它变化的时候通知我。 Android 开发中一个比较典型的例子是点击监听器 OnClickListener
。对设置 OnClickListener
来说, View
是被观察者, OnClickListener
是观察者,二者通过 setOnClickListener()
方法达成订阅关系。订阅之后用户点击按钮的瞬间,Android Framework 就会将点击事件发送给已经注册的 OnClickListener
。采取这样被动的观察方式,既省去了反复检索状态的资源消耗,也能够得到最高的反馈速度。当然,这也得益于我们可以随意定制自己程序中的观察者和被观察者,而警察叔叔明显无法要求小偷『你在作案的时候务必通知我』。
我所理解的RxJava的核心优势应该是它可以对复杂逻辑进行拆分成为一个一个的Observable后,RxJava的各种操作符予这些解耦的Observable能够合理的进行再组织的能力,并且它给予了你足够丰富的再组织能力。这种分拆再组织的能力是十分强大的,只有运用好RxJava这种强大的能力,才能真正意义上使你原来非常复杂的揉在一团的逻辑代码变得清晰、简洁,本质上是因为RxJava给你提供了这种强大方便的组织能力,我觉得有点像一种编程模式,你可以放心的将复杂的逻辑拆块,最后RxJava给你提供了丰富的组织、变换、串联、控制这些块的能力,只有这个时候你才会真正觉得这是个好东西,而不应该是跟风使用,但是心里也说不清楚为什么要使用。
RxJava 有三个基本概念: Observable
(被观察者),Observer
(观察者),subscribe
(订阅)。Observable
和 Observer
通过 subscribe()
方法实现订阅关系,从而 Observable
可以在需要的时候发出事件来通知 Observer
。
Github 链接:
RxJava https://github.com/ReactiveX/RxJava
RxAndroid https://github.com/ReactiveX/RxAndroid
gradle 依赖:
implementation 'io.reactivex.rxjava2:rxjava:2.x.y'
implementation 'io.reactivex.rxjava2:rxandroid:2.x.y'
1 | Observable<String> observable = Observable |
可以看到,这里传入了一个 ObservableOnSubscribe
对象作为参数。ObservableOnSubscribe
会被存储在返回的 Observable
对象中,当 Observable
被订阅的时候,subscribe
方法就会自动被调用,事件序列就会依照设定依次触发(对于上面的代码,就是观察者 observer
将会被调用三次 onNext()
和一次 onComplete()
)。
此外,还可以通过其他方法来创建被观察者。
just(T...)
: 将传入的参数依次发送出来。
1 | Observable observable = Observable.just("Hello", "World"); |
fromArray(T...items)
: 将传入的数组或 Iterable 拆分成具体对象后,依次发送出来。
1 | String[] words = {"Hello", "Hi", "Aloha"}; |
除 Observable
外还有 Flowable
等被观察者类型。
1 | Observer<String> observer = new Observer<String>() { |
在观察者中进行响应事件对应的相关操作。
1 | observable.subscribe(observer); |
这里的写法是被观察者订阅了观察者,而不是观察者订阅被观察者,是为了保证流式API调用风格。
1 | observable |
上面就是一个非常简易的RxJava流式API的调用:同一个调用主体一路调用下来,一气呵成。
RxJava 的这个实现,是一条从上到下的链式调用,没有任何嵌套,这在逻辑的简洁性上是具有优势的。
整个流程如下图所示:
结合流程图的相应代码实例如下:
1 | //创建被观察者,是事件传递的起点 |
注意:当调用订阅操作(即调用Observable.subscribe()方法)的时候,被观察者才真正开始发出事件。
至此,在 RxJava 的默认规则中,事件的发出和消费都是在同一个线程的。
在 RxJava 中,通过 Scheduler
来指定每一段代码应该运行在什么样的线程。下表展示了RxJava中可用的调度器种类:
调度器类型 | 效果 |
---|---|
Schedulers.computation( ) | 用于计算任务,如事件循环或和回调处理,不要用于IO操作(IO操作请使用Schedulers.io());默认线程数等于处理器的数量 |
Schedulers.from(executor) | 使用指定的Executor作为调度器 |
Schedulers.immediate( ) | 在当前线程立即开始执行任务 |
Schedulers.io( ) | 用于IO密集型任务,如异步阻塞IO操作,这个调度器的线程池会根据需要增长;对于普通的计算任务,请使用Schedulers.computation();Schedulers.io( )默认是一个CachedThreadScheduler,很像一个有线程缓存的新线程调度器 |
Schedulers.newThread( ) | 为每个任务创建一个新线程 |
Schedulers.trampoline( ) | 当其它排队的任务完成后,在当前线程排队开始执行 |
AndroidSchedulers.mainThread() | Android 主线程 |
subscribeOn()
: 指定 subscribe()
所发生的线程,或者叫做事件产生的线程。
observeOn()
: 指定 Observer
所运行在的线程。或者叫做事件消费的线程。
1 | //new Observable.just()执行在新线程 |
注意:
subscribeOn()
它指示 Observable
在一个指定的调度器上创建(只作用于被观察者创建阶段)。只能指定一次,如果指定多次则以第一次为准observeOn()
指定在事件传递(加工变换)和最终被处理(观察者)的发生在哪一个调度器。可指定多次,每次指定完都在下一步生效。=======================================================
操作符 | 说明 |
---|---|
Create | 使用一个函数从头开始创建一个Observable |
Defer | 直到有观察者订阅时才创建Observable,并且为每个观察者创建一个新的Observable |
Empty | 创建一个不发射任何数据但是正常终止的Observable |
Never | 创建一个不发射数据也不终止的Observable |
Throw | 创建一个不发射数据以一个错误终止的Observable |
From | 将其它种类的对象和数据类型转换为Observable |
Interval | 创建一个按固定时间间隔发射整数序列的Observable |
Just | 创建一个发射指定值的Observable |
Range | 创建一个发射特定整数序列的Observable |
Repeat | 创建一个发射特定数据重复多次的Observable |
Start | 返回一个Observable,它发射一个类似于函数声明的值 |
Timer | 创建一个Observable,它在一个给定的延迟后发射一个特殊的值 |
操作符 | 说明 |
---|---|
操作符 | 说明 |
---|---|
=======================================================
1 | private void testRxJava() { |
1 |
|
参考资料:
列一下目标清单上的一些计划。
1 | def room_version = "1.1.1" |
1 | @Entity(tableName = "user") |
1 | @Dao |
1 | @Database(entities = {User.class}, version = 1) |
1 | private fun testRoom() { |
1 | // product |
1 | class BaseApplication : Application() { |
1 | // 新建一个对象,并进行存储 |
1 | // product |
1 | class BaseApplication : Application() { |
1 | @Database(version = VERSION, name = NAME) |
1 | @Table(database = DBFlowDataBase.class) |
编译之后
1 | private fun testDBFlow() { |
下载地址:http://ormlite.com/releases/
需要下载最新的 core.jar 和 android.jar 并添加依赖
1 | @DatabaseTable(tableName = "user") |
1 | public class DatabaseHelper extends OrmLiteSqliteOpenHelper { |
观念大概是从 PGONE 被封杀开始转变的。当时也跟微博上那些吃瓜群众一样,拍手叫好,喜闻乐见。是对嘻哈没有什么兴趣,倒不是说讨厌他,只是讨厌她的粉丝,就像之前的薛之谦,吹的太过,捧得太高,但更多的是有人的一种习性吧,见不得这些,或许是有些嫉妒?更多人是选择去落井下石,补上一脚。当然也有我。清楚的记得当时有人为这种 “处罚” 鸣不平,当然在那个时候,我是不会在意这些腔调的。直到后来,参加了一期《歌手》的 GAI ,突然被退赛,一夜之间,嘻哈全面被封杀,这是才真正明白,为什么当时有些人会鸣不平,不只是 PGONE 的粉丝,还有些人看到了未来,被ZF干预。封杀这件事,如果你说他是违了法,好,说出来具体违反了什么法律法规,然后按相关规章制度办事,大家都就没有异议的。但是只是凭上层人士的个人喜好来处理,这算不算滥用职权。不存在的,在中国,没人敢说的。
又记起了上个赛季发生在中超的那些处罚,罚那么重,好,罚那么重也算了,只要大家都这么罚,也没什么,但是为什么,同样的犯规,最后处罚会差那么多。
马克思主义告诉我们,事情总是相对的。社会主义国家在面对重大灾难的时候,国家就是好,集中全国干大事。但是在上述问题下,国家的干预就不那么招人喜欢了。最近一直在想一个词,饱暖思淫欲。之前,大家都在为填饱肚子在忙,没时间去了解其他事。现在不一样,“闲人”太多,消息又这么发达,也都在接触一些国外的那种“开放”、“民主”的精神,所以会出现一些问题。
《我的团长我的团》中龙文章说:英国人死于傲慢与偏见,中国人死于听天由命和漫不经心。
我只是想要宪法赋予我的那个世界。
1 | public abstract class BaseLazyFragment extends Fragment { |
1 | import android.os.Bundle; |
对于可见状态的生命周期调用顺序,父 Fragment总是优先于子 Fragment,而对于不可见事件,内部的 Fragment 生命周期总是先于外层 Fragment。
标识符命名法最要有四种:
<scope_> + <prefix_> + <qualifier>
范围前缀,类型前缀,限定词。尽可能的用最少的字符而又能完整的表达标识符的含义。
英文缩写原则:
下面为常见的英文单词缩写:
名称 | 缩写 |
---|---|
icon | ic |
color | cl |
divider | di |
selector | sl |
background | bg |
image | img |
password | pwd |
position | pos |
TextView | tv |
ImageView | iv |
EditText | et |
注意: 单词缩写原则:不要用缩写,除非该缩写是约定俗成的。
采用反域名命名规则,全部使用小写字母。一级包名为com,二级包名为xx(可以是公司或则个人的随便),三级包名根据应用进行命名,四级包名为模块名或层级名。
包名 | 此包中包含 |
---|---|
包名.activities | Activity类 |
包名.base | 自定义基类 |
包名.adapter | Adapter类 |
包名.tools | 公共工具方法类 |
包名.bean | 包中包含:实体类 |
包名.view (或 widget) | 自定义的View类等 |
包名.service | Service服务 |
… | … |
采用大驼峰命名法,尽量避免缩写,除非该缩写是众所周知的,比如 HTML , URL,如果类名称中包含单词缩写,则单词缩写的每个字母均应大写。
类 | 描述 | 例如 |
---|---|---|
activity | Aty或者Activity为后缀标识 | WelcomeAty.或者WelcomeActivity |
Adapter | Adapte 为后缀标识 | NewsAdp或NewsAdapter |
基础类 | 以Base开头 | BaseActivity,BaseFragment |
公共方法类 | Tools或Manager为后缀标识 | ThreadPoolManager, LogTools |
Service | 以Service为后缀标识 | TimeService |
… | … | … |
动词或动名词,采用小驼峰命名法例如: onCreate(), run()
采用小驼峰命名法。类中控件名称必须与xml布局id保持一致。
用统一的量词通过在结尾处放置一个量词,就可创建更加统一的变量,它们更容易理解,也更容易搜索。例如,请使用 strCustomerFirst
和 strCustomerLast
,而不要使用 strFirstCustomer
和 strLastCustomer
。
全部大写,采用下划线命名法.例如:MIN_WIDTH
全部小写,采用下划线命名法,加前缀区分
名称 | 功能 |
---|---|
btn_xx |
按钮图片使用btn_整体效果(selector) |
btn_xx_normal |
按钮图片使用btn_正常情况效果 |
btn_xx_press |
按钮图片使用btn_点击时候效果 |
bg_head |
背景图片使用bg_功能_说明 |
ic_more_help |
图标图片使用icon_功能_说明 |
… | … |
大小写规范与方法名一致,采用小驼峰命名法。命名规范为“资源控件的缩写 名”+“变量名”。注意:页面控件名称应该和控件id名保持一致
命名模式为:view缩写_模块名称_view的逻辑名称
控件 | 缩写 |
---|---|
RelativeView | rv |
TextView | tv |
Button | btn |
ImageView | iv |
ProgressBar | pb |
… | … |
注意: 如果layout文件很复杂,建议将layout分成多个模块,每个模块定义一个moduleViewHolder,其成员变量包含所属view
将layout中不断重现的style提炼出通用的style通用组件,放到styles.xml中
Update your browser to view this website correctly. Update my browser now