You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Back when I started writing Android apps in 2009 things were a little different. Apps were a whole new world of software development and everything was evolving, no one took apps too seriously and they were just a bit of fun.
16
-
17
15
09年我刚开发 App 那会,情况和现在不太一样。App 作为新生的 IT 领域,一切事物都处于从低级向高级演化的阶段。那会儿哪有人会把开发 App 当成吃饭的家伙呀,大家都只是想打发打发时间,找点乐子。
18
16
19
-
Fast forward to the present day and the mobile app landscape has totally changed. Apps are now big business and are becoming cornerstones of companies strategies. At JUST EAT this is very much the case. We know our customers love using our apps and so we see it as really important that we create apps that are robust and give a fantastic user experience.
20
-
21
17
而今天,移动 App 已经发生了翻天覆地的变化。App 不但在当今互联网时代和 PC 分庭抗礼,甚至在许多公司中成为战略发展的核心,而 JUST EAT 公司就是其中之一。我们知道用户都很喜欢我们的 App,所以我们一直很重视我们的 App,这也促使我们的 App 在保证性能表现的同时提供了绝佳的用户体验。
22
18
23
-
So as you may be able to tell, we’re serious about our apps, and serious developers use serious software tools and techniques. One of those software techniques we use in JUST EAT is Dependency Injection and the associated frameworks that go along with it.
Dependency Injection (DI) is a design pattern which has been around for while, but recently it has become more commonly used in the development of Android applications, due mainly to the implementation of some rather nifty DI frameworks. DI allows developers to write code that has low coupling and which can therefore be easily tested. The more complex and longer lived your Android software the more important it becomes to be able to test it effectively. At JUST EAT we see DI as key to allowing our code to be configurable and therefore testable, so creating a codebase we can have confidence in. Even though we have a fairly large and complicated codebase, we can make releases regularly and quickly because we have robust testing which is achieved in part by using DI. With that in mind hopefully I’ve convinced you that DI is worth a look, but first let’s have a quick recap of what DI is.
31
-
32
23
虽然依赖注入早已作为一种设计模式为人所知,但到了近段时间它才被广泛应用到 Android 应用的开发中,主要是因为最近才有了各种各样依赖注入框架的优秀实现。依赖注入让开发者的代码低耦合,且能够容易地进行测试。开发者开发的 Android 应用存活的时间越长,应用易于被高效地进行测试就显得越重要。在开发 JUST EAT 的时候,我们认为:让代码能够被配置因而能被测试的关键就是依赖注入,这样才能在应用变得越来越庞大的时候代码依然可靠。正因为我们通过依赖注入使我们的测试变得有效,鲁棒性高,才能定期更新应用。JUST EAT 的成功让我们确信依赖注入是一个有用的工具,但在继续吹B之前,我还是介绍一下以来注入把。
33
24
34
-
##Basics of Dependency Injection
35
25
##依赖注入的基础
36
26
37
-
When we write code we will often find that our classes will have dependencies on other classes. So class A might need to have a reference, or dependency to class B. To make things a little clearer let’s look at the case where we have a Car class that needs to use an Engine class.
38
-
39
27
我们写代码的时候,我们的类总会不可避免地拥有其他类的依赖。这样类 A 就可能需要一个类 B 的引用或依赖。为了简化介绍的难度,我下面就举个车的例子来解释吧:
40
28
41
29
```java
@@ -49,8 +37,6 @@ public class Car {
49
37
}
50
38
```
51
39
52
-
This code works fine, but the downside is that the coupling between the Car and the Engine is high. The Car class creates the new Engine object itself and so it has to know exactly what Engine it needs, in this case a PetrolEngine. Maybe we could do a little better and reduce the coupling, so let’s look at a different way of creating this Car class.
53
-
54
40
这段代码毫无疑问能正常地运作,但我们会发现 Car 类和 Engine 类高度耦合。Car 类一旦被实例化,就会伴随着 Engine 类的实例化,也就使得 Car 类在实例化时必须知道 Engine 类的实例化需要哪些条件,在我们的例子中就是 PetrolEngine 类实例化需要的条件。为了降低耦合,我们可以把代码改成下面这样:
55
41
56
42
```java
@@ -64,87 +50,46 @@ public class Car {
64
50
}
65
51
```
66
52
67
-
Here we have passed the Engine into the Car via the car’s constructor method. This means that the coupling between the two objects is now lower. The car doesn’t need to know what concrete class the Engine is, it could be any type of Engine as long as it extends the original Engine class. In this example since we have passed, or injected, the dependency via the Car classes constructor we have performed a type of injection known as constructor injection, we can also perform injection via methods and with the use of DI frameworks directly into fields. So that’s really all there is to DI. At its most basic it is just passing dependencies into a class rather than instantiating them directly in the class.
68
-
69
53
现在我们只需要在实例化 Car 对象的时候传入一个 Engine 对象的引用,这就意味着 Car 类和 Engine 类的耦合度降低了。Car 类不再需要知道 Engine 类的实例化条件是什么,Car 类能调用的任何类型的 Engine 类。在这个例子中,因为我们通过 Car 类的构造方法把 Engine 类传递(或者称为注入)给 Car 实例,使得我们完成了构造器注入的实现,我们当然还可以通过使用依赖注入框架的方法直接注入到类的域里。上面就是依赖注入的概念了。它的思想就是将依赖直接传递给类,而不是由类来初始化依赖。
70
54
71
-
##If DI is simple, why do we need Frameworks?
72
55
##如果依赖注入这么简单,为什么需要专门开发一个框架?
73
56
74
-
Now that we understand what DI is, it’s quite straightforward to start using it in our code. We simply look at what dependencies are needed and pass them via a constructor or a method call. This is fine for simple dependencies, but you’ll soon find that for more complex dependencies things can start getting a little messy.
Let’s return to our example of a Car that has a dependency on an Engine. Now imagine that the engine also has it’s own set of dependencies. Let’s say it needs a crank shaft, pistons, block and head. If we follow DI principles we will pass these dependencies into the Engine class, that’s not so bad, we just need to create these objects first and pass them into the Engine object when we create that. Finally we pass the Engine to the Car.
Next let’s make our example a little more complicated. If we imagine trying to create classes for each part of an engine we can see that we would soon end up with possibly hundreds of classes with a complicated tree (more accurately it is a graph) structure of dependencies.
A simplified graph of dependencies for our example. Here the leaf dependencies have to be created first then passed to the objects that depends on them. All objects have to be created in the correct order.
To create our dependencies we would then have to carefully create all our objects in the correct order, starting with the leaf node dependencies and passing those in turn to each of their parent dependencies and so on until we reach the top most or root dependency.
93
-
94
67
换句话说,为了创建依赖,我们如履薄冰,一旦顺序搞错了就会代码爆炸。
95
68
96
-
Things are starting to get quite complicated, if we also used factories and builders to create our classes we can soon see that we have to start creating quite a lot of complicated code just to create and pass our dependencies, this type of code is commonly know as boilerplate code and generally it’s something we want to avoid writing and maintaining.
From our example we can see that implementing DI on our own can lead to creating a lot of boilerplate code and the more complex your dependencies the more boilerplate you will have to write. DI has been around for a while and so has this problem, so to solve it Frameworks for using DI have been create. These frameworks make it simple to configure dependencies and in some cases generate factory and builder classes for creating objects, making it very straightforward to create complex dependencies that are easily managed.
Since DI has been around for a while there are unsurprisingly quite a few DI frameworks that we can choose from. In the Java world we have Spring, Guice and more recently Dagger. So which framework should we use and why?
Spring is a DI framework that’s been around for sometime. It’s aim was to solve the problem of declaring dependencies and instantiating objects. It’s approach was to use XML to do this. The downside to this was that the XML was almost as verbose as writing the code by hand and also validation of it was done at runtime. Spring introduced a number of problems while trying to solve the initial problems of using DI.
112
-
113
77
Spring 已经有一段时日了,为了解决声明依赖和初始化对象,Spring 运用了 XML。但这样做有一个问题,就是我们必须写下冗长的 XML 代码来完成这部分工作,而且要在运行时确保它完成了这些工作。所以 Spring 在尝试解决因此衍生的种种问题时也提出了使用依赖注入存在的问题。
114
78
115
-
In the history of Java DI frameworks, Guice was really the next evolution after Spring. Guice got rid of the XML configuration files and did all of its configuration in Java using annotations such as @Inject and @Provides. Things were starting to look a whole lot better, but there were still some problems. Debugging and tracking down errors with applications built using Guice could be somewhat difficult. Additionally it still did runtime validation of the dependency graphs and also made heavy use of reflection, both of which are fine for server side applications, but can be quite expensive for mobile applications that are launched much more often on devices with much lower performance.
116
-
117
79
在 Java 依赖注入框架的发展历程中,Guice 确实能算作对 Spring 的革新。Guice 不需要通过 XML 来配置类的成员域,而是直接通过注解完成了配置的工作,例如 @Inject 和 @Provides。这样看起来依赖注入框架确实要变得更好用了,但 Guice 还是存在一些问题:Debug 和追踪错误有些困难。另外,Guice 使用了大量的反射,虽说对服务器来说这些都不是太大的问题,但在追求用户体验的客户端就会造成很大的开销,影响应用的性能表现从而降低用户体验。
118
80
119
-
While Guice was a big step forward it really didn’t solve all the problems and its design was also not ideally suited for use on mobile devices. With that in mind a team of developers at a company called Square developed Dagger.
Dagger takes its name from our tree structure of dependencies. Remember more accurately it is a graph of dependencies and in this case the graph is actually a Directed Acyclic Graph or DAG hence the name DAGger. Dagger’s aim was to address some of the concerns of using Guice and especially using Guice on mobile devices.
Dagger took the approach of moving a lot of its workload to compile time rather than runtime and also tried to remove as much reflection from the process as possible, both of which really helped performance when running on mobile applications. This was done at the slight expense of reducing the feature set offered by the likes of Guice, but for Android apps Dagger was still a step in the right direction. With Dagger we are nearly at a good solution for a DI framework that is suitable for mobile devices, but a team at Google decided that things could still be done a little better and so they created Dagger 2.
128
-
129
85
Dagger 将大部分工作负担投到编译时完成而不是运行时,而且尝试尽可能不使用反射,这两部分工作能完成的话就能极大提高依赖注入框架的性能表现。最后 Dagger 完成了这样的工作,但为此也牺牲了一些 Guice 中的特性,但总体来说还是值得的。但 Google 认为这部分工作还能完成地更好,所以他们在尝试开发 Dagger 2。
130
86
131
-
Dagger 2 does even more of its work at compile time and also does a better job of removing reflection, finally it also generates code that is even easier to debug than the original version of Dagger. In my opinion there really isn’t a better solution for DI on Android, so if you’re going to use a DI framework, I believe that Dagger 2 really is the easiest to use and debug with, while also having the best performance.
So where do you go from here? Luckily Dagger and Dagger 2 already have a strong following so there are plenty of tutorials and presentation to help you get up to speed.
139
-
140
91
那么要怎么用它们呢?Dagger 和 Dagger 2 都有丰富的教程能帮助你快速入门。
141
92
142
-
The main Dagger 2 website can be found here and to get a good overview of Dagger 2 and it features there’s a great presentation by Jake Wharton that you can find here. It covers the basics and then goes on to discuss how Modules and Components work, while also covering the subject of scopes in Dagger 2. Finally to get you firmly on the road to using DI in Android here’s a list of handy tutorials:
143
-
144
-
Good overview of Dagger 2: http://fernandocejas.com/2015/04/11/tasting-dagger-2-on-android
0 commit comments