安卓& Kotlin Books 匕首by Tutorials

7
更多关于注射 由Massimo Carli撰写

在上一章中,您开始使用匕首非常基本 服务器存储库 example. As you remember from the first chapter, the code you implemented uses a simple dependency between the Server 和 a Repository called 松散耦合。您可以使用图7.1中的UML图表表示此依赖项:

图7.1  - 松散耦合依赖性
图7.1 - 松散耦合依赖性

你学会了如何告诉匕首如何生成 工厂 对于依赖图中的实例使用 @零件 注解。然后你学会了如何使用 @注入 注释实现两种不同的目标:

  1. 告诉匕首是什么构造函数来调用课程的实例。
  2. 将属性标记为注射的目标。

如果依赖性类型是抽象,如接口,匕首需要一些额外的信息。您使用a提供此信息 @模块 包含一些您注释的功能 @Provides. 。这样,你告诉匕首函数可以调用以获得特定类型的类的实例。幸运的是,匕首是一个很好的倾听者。 :]

You learned that the @注入 , @零件 , @模块 @Provides. annotations are all you need to implement dependency injection in your app with Dagger. The rest of the annotations let you improve performance when generating and executing the code.

在本章中,您将在与匕首中发现更多关于依赖注入的更多信息。你会学习如何:

  • 处理 构造函数 , 场地 方法 用匕首注射。
  • Simplify the implementation of @模块 by using @Binds in cases when you have an abstraction and its implementation. You saw how this works in the RepositoryFakeRepository example.
  • Use @Singleton for the first time to solve a very common problem.

还有很多事情要做。准备有更多乐趣!

入门

在上一章中,您了解了如何在Intellij中使用一些匕首项目。在本章中,您将返回Android 雷昆斯 app. This is a very simple app that allows you to display a numeric value of a sequence on the screen every time you press a Button.

要开始,使用 安卓Studio 打开 雷昆斯 项目在 起动机 本章的材料文件夹。构建和运行,您将获得图7.3所示的屏幕:

图7.3  - 初始RaySequence应用程序
图7.3 - 初始RaySequence应用程序

笔记 :不要担心BUSSO应用程序。在几章中,您将迁移到匕首,一切似乎对您来说非常容易。

At the moment, the app doesn’t work: When you click the Button, nothing happens. Figure 7.4 shows the file structure of the app:

图7.4  -  RaySequence文件结构
图7.4 - RaySequence文件结构

如您所见,该应用程序使用相同的应用程序 MVP. 您在先前的章节中看到的图书馆和实现 模型 , ViewBinder. 主持人 已经完成了。但是,您仍然需要连接点。

在这样做之前,请快速查看模型。打开 SequenceGenerator.kt. 在里面 模型 包裹包装 应用程序 模块并查看以下代码:

interface SequenceGenerator<T> {
    fun next(): T
}

SequenceGenerator<T> is a simple abstraction to let any object provide the next element of a sequence through its next() operation.

笔记 : The Kotlin standard library already provides the Sequence<T> interface, which is similar to SequenceGenerator<T> 和 has some utility builders like the sequence() higher-order function. However, using Sequence<T> requires you to define an Interator<T>, which makes the code a bit more complex with no gain in the context of dependency injection.

在相同的 模型 package are two SequenceGenerator<T> implementations. NaturalSequenceGenerator.kt. 包含一种生成自然数字的简单方法:

class NaturalSequenceGenerator(
	private var start: Int
) : SequenceGenerator<Int> {
    override fun next(): Int = start++
}

尽管 FibonAcciseQuenceGenerator.kt. 包含更有趣的实施 斐波纳契序列:

class FibonacciSequenceGenerator() : SequenceGenerator<Int> {
  private var pair = 0 to 1

  override fun next(): Int {
    val next = pair.first
    pair = pair.second to pair.first + pair.second
    return next
  }
}

您将在下一章中使用它

In the code for the test build type, you’ll also find some unit tests.

Gradle.Build. 对于RaySequence应用程序已经包含使用匕首所需的配置,因此您可以开始构建应用程序的依赖关系图。

笔记 :要节省空间,此项目的一些代码不打印出来。你可以通过参考来看看 起动机 或者 最后 本章材料的文件夹。

不同的注射类型与匕首

在本章中,您将不存在于与Diergong在Ano项目中实现注射类型的。您将首先配置RoySequence应用程序的不同组件,以便匕首 捆绑 .

使用构造仪注入将视图绑定绑定

打开 sequenceviewbinderimpl.kt. 在里面 看法 包裹包装 应用程序 模块并查看以下代码:

class SequenceViewBinderImpl(
	// HERE
    private val sequenceViewListener: SequenceViewBinder.Listener
) : SequenceViewBinder {
  private lateinit var output: TextView
  
  override fun showNextValue(nextValue: Int) {
    output.text = "$nextValue"
  }

  override fun init(rootView: MainActivity) {
    output = rootView.findViewById(R.id.sequence_output_textview)
    rootView.findViewById<Button>(R.id.next_value_button)
    	.setOnClickListener {
      		sequenceViewListener.onNextValuePressed()
    	}
  }
}

直接调用构造函数

If you decide to invoke the constructor of SequenceViewBinderImpl directly, copy the following code into the newly created 应用程序 module.kt.:

// 1
@Module
object AppModule {
 // 2
  @Provides
  fun provideSequenceViewBinder(
  	  // 3
      viewBinderListener: SequenceViewBinder.Listener
      // 4
  ): SequenceViewBinder = SequenceViewBinderImpl(viewBinderListener)
}

委派建筑用@binds匕首

On the other hand, you can make Dagger responsible for creating the instance of SequenceViewBinderImpl 和 its dependencies. In this case, you need to tell Dagger two things:

// 1
@Module
object AppModule {
  // 2
  @Module
  interface Bindings {
  	// 3
    @Binds
    fun bindSequenceViewBinder(impl: SequenceViewBinderImpl): SequenceViewBinder
  }
}
class SequenceViewBinderImpl @Inject constructor( // HERE
    private val sequenceViewListener: SequenceViewBinder.Listener
) : SequenceViewBinder {
  // ...
}

用现场注射绑定演示者

在前面的示例中,您学习了如何使用 构造函数注射 with Dagger for the SequenceViewBinder implementation. You could do the same thing for the 主持人 ,但有趣的是,看看如何使用 现场注射 反而。要这样做,打开 sequencepresenterimpl.kt. 在里面 主持人 包并查看以下代码:

class SequencePresenterImpl : BasePresenter<MainActivity,
    SequenceViewBinder>(),
    SequencePresenter {
  lateinit var sequenceModel: SequenceGenerator<Int> // HERE

  override fun displayNextValue() {
    useViewBinder {
      showNextValue(sequenceModel.next())
    }
  }

  override fun onNextValuePressed() {
    displayNextValue()
  }
}
interface SequencePresenter :
    Presenter<MainActivity, SequenceViewBinder>,
    SequenceViewBinder.Listener { // HERE
  fun displayNextValue()
}
图7.5  -  equencePresenterimpl的UML图
Hirolo 6.8 - OPK Duecbub Nus WumuozzattazzeSpovurgq

使用@binds为演示者

捆绑 SequencePresenterImpl to SequencePresenter, you’ll use @Binds. Open 应用程序 module.kt. 并添加以下定义,留下其余部分:

 @模块 
object AppModule {
  @Module
  interface Bindings {
  	// ...
    @Binds
    fun bindSequencePresenter(impl: SequencePresenterImpl): SequencePresenter // HERE
  }
}

使用现场注射给演示者

Now, you need to tell Dagger how to create the instance of SequencePresenterImpl with all its dependencies. Open sequencepresenterimpl.kt. 并将其更改为此:

// 1
class SequencePresenterImpl @Inject constructor(
) : BasePresenter<MainActivity,
    SequenceViewBinder>(),
    SequencePresenter {
  // 2
  @Inject
  lateinit var sequenceModel: SequenceGenerator<Int>
  // ...
}

提供序列通行者实现

SequencePresenterImpl needs an implementation of SequenceGenerator<Int>. You could use @Binds again, or you could directly provide the instance. In this case, you’ll use the second option.

 @模块 
object AppModule {
  // ...
  @Provides
  fun provideSequenceGenerator(): SequenceGenerator<Int> =
      NaturalSequenceGenerator(0)
  // ...
}

处理SequenceViewBinder.Listener实现

现在, ViewBinder. 不是完整的,因为你仍然需要告诉匕首是什么,以便进入 SequenceViewBinder.Listener

 @模块 
object AppModule {
  // ...
  @Module
  interface Bindings {
    // ...
    // 1
    @Binds
    // 2
    fun bindViewBinderListener(impl: SequencePresenter):
    	// 3
        SequenceViewBinder.Listener
  }
}

主要活动

到目前为止,你所做的是真的很酷,但你仍然需要将这项工作申请到射线序列。

class MainActivity : AppCompatActivity() {
  // 1
  lateinit var presenter: SequencePresenter
  // 2
  lateinit var viewBinder: SequenceViewBinder

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    // 3
    viewBinder.init(this)
  }
  
  // 4
  override fun onStart() {
    super.onStart()
    presenter.bind(viewBinder)
  }
  
  // 5
  override fun onStop() {
    presenter.unbind()
    super.onStop()
  }
}

定义@component.

匕首now has a lot of information, but it doesn’t know how to use it. To solve the problem, you need to define a @零件 — so create a new file named 应用程序 component.kt. 在里面 包并将以下代码复制到其中:

// 1
@Component(modules = [
  AppModule::class,
  AppModule.Bindings::class
])
interface AppComponent {
  // 2
  fun viewBinder(): SequenceViewBinder
  // 3
  fun presenter(): SequencePresenter
}

注入主动

在上一节中,您已配置RaySequence应用程序的依赖项 ViewBinder. 主持人 . Now, you need to use them in 主要活动, which is your 看法 在里面 模型视图仪表 模式实施。

class MainActivity : AppCompatActivity() {
  lateinit var presenter: SequencePresenter
  lateinit var viewBinder: SequenceViewBinder

  override fun onCreate(savedInstanceState: Bundle?) {
  	// 1
    DaggerAppComponent.create().apply {
      // 2
      presenter = presenter()
      viewBinder = viewBinder()
    }
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    viewBinder.init(this)
  }
  // ...
}

见@singleton.

要了解正在发生的事情,请查看您在匕首中的一些事情 应用程序 module.kt.。你告诉它:

class SequenceViewBinderImpl @Inject constructor(
    private val sequenceViewListener: SequenceViewBinder.Listener
) : SequenceViewBinder {
  init {
    Log.d("DAGGER_LOG", "Listener: $sequenceViewListener") // HERE
  }
  // ...
}
class MainActivity : AppCompatActivity() {
  // ...
  override fun onCreate(savedInstanceState: Bundle?) {
    DaggerAppComponent.create().apply {
      presenter = presenter()
      Log.d("DAGGER_LOG", " 主持人 : $presenter") // HERE
      viewBinder = viewBinder()
    }
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    viewBinder.init(this)
  }
  // ...
}
图7.7  - 记录实例匕首已创建
Cotevi 0.7 - Siytibm Kra Ujhnecniq Muncif Kam Nkiuxed

D/DAGGER_LOG: Presenter: [email protected]
D/DAGGER_LOG: Listener: [email protected]

使用@singleton.

你已经达到了基本的概念 范围 在第4章中,“依赖注入& Scope”, and you’ll learn a lot more in the following chapters. However, it’s very important to say: @Singleton is nothing special.

@Singleton // HERE
class SequencePresenterImpl @Inject constructor(
) : BasePresenter<MainActivity,
    SequenceViewBinder>(),
    SequencePresenter {
  // ...
}
AppComponent.java:7: error: [Dagger/IncompatiblyScopedBindings]
com.raywenderlich.android.raysequence.di.AppComponent
 (unscoped) may not reference scoped bindings:
public abstract interface AppComponent {
 @零件 (modules = [
  AppModule::class,
  AppModule.Bindings::class
])
@Singleton // HERE
interface AppComponent {

  fun viewBinder(): SequenceViewBinder

  fun presenter(): SequencePresenter
}
D/DAGGER_LOG: Presenter: [email protected]
D/DAGGER_LOG: Listener: [email protected]
图7.8  - 工作Raysequence应用程序
Laqina 3.9 - 我Fugyomx Yeklokoulci ARV

使用方法注入

为了完整起见,快速看看你如何实现同一目标 方法注射。变化很简单。

class SequenceViewBinderImpl @Inject constructor(
) : SequenceViewBinder {
  // 1
  private var sequenceViewListener: SequenceViewBinder.Listener? = null

  init {
    Log.d("DAGGER_LOG", "Listener: $sequenceViewListener")
  }

  // 2
  @Inject
  fun configSequenceViewListener(listener: SequenceViewBinder.Listener) {
    sequenceViewListener = listener
  }
  // ...
}

清理喷射以进行主动

早些时候,你读到你使用更好的方法来注射 主持人 ViewBinder. into the 主要活动. Good news — you’re ready to do that now.

 @零件 (modules = [
  AppModule::class,
  AppModule.Bindings::class
])
@Singleton
interface AppComponent {
  // HERE
  fun inject(mainActivity: MainActivity)
}
class MainActivity : AppCompatActivity() {
  // 1
  @Inject
  lateinit var presenter: SequencePresenter
  // 2
  @Inject
  lateinit var viewBinder: SequenceViewBinder

  override fun onCreate(savedInstanceState: Bundle?) {
  	// 3
    DaggerAppComponent.create().inject(this)
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    viewBinder.init(this)
  }
  // ...
}

关键点

  • 匕首supports 构造函数 , 方法 现场注射.
  • @零件 , @模块 , @注入 @Provides. annotations are all you need to implement dependency injection with Dagger in your app.
  • @Binds 允许您将类绑定到要在依赖定义中为其使用的抽象类型。
  • By default, Dagger creates a different instance of a class every time you ask for it using a @零件 operation.
  • @Singleton binds the lifecycle of objects to the lifecycle of the @零件 that creates them.
  • 匕首allows you to generate the code to inject dependency in an object in a similar way to how you used the Injector<T> abstraction 在第4章中,“依赖注入& Scope”.

然后去哪儿?

恭喜!在本章中,您向前迈出了一大步,了解如何在Android应用程序中使用匕首。

有一个技术问题?想报告一个错误吗? 您可以向官方书籍论坛中的书籍作者提出问题和报告错误 这里 .

有反馈分享在线阅读体验吗? 如果您有关于UI,UX,突出显示或我们在线阅读器的其他功能的反馈,您可以将其发送到设计团队,其中表格如下所示:

© 2021 Razeware LLC

您可以免费读取,本章的部分显示为 混淆了 文本。解锁这本书,以及我们整个书籍和视频目录,带有Raywenderlich.com的专业订阅。

现在解锁

要突出或记笔记,您需要在订阅中拥有这本书或自行购买。