匕首线切割图纸下载

重点 (Top highlight)

A pragmatic guide to dependency injection on Android

在Android上进行依赖注入的实用指南

Over the years many of us used different frameworks for dependency injection (DI). I remember writing an article about Transfuse a couple of years ago for a German magazine. Before that when I was working for eBay Kleinanzeigen we had a complete self-written DI approach. There was no magic and therefore it taught me a lot about the internals of those frameworks. Later at Groupon, I introduced Dagger1 in the Merchant app, which at some point was migrated to Toothpick aligning with the consumer app, who moved there from RoboGuice. At Sporttotal.tv I fell in love with Koin. When I had to touch a Groovy backend there, there was Spring. At Viacom my team introduced Dagger2, which is also what we use at SoundCloud.

多年来,我们许多人使用不同的框架进行依赖注入(DI)。 我记得几年前为一家德国杂志写过一篇有关Transfuse的文章。 在那之前,当我在eBay Kleinanzeigen工作时,我们采用了完整的自行编写的DI方法。 没有魔术,因此它教会了我很多关于这些框架的内部知识。 后来在Groupon的,我介绍Dagger1在商家应用程序,这在某些时候迁移到牙签对准与消费者应用程序,从谁搬到那里RoboGuice 。 在Sporttotal.tv我爱上了Koin 。 当我不得不接触那里的Groovy后端时,就有了Spring 。 在维亚康姆,我的团队介绍了Dagger2 ,这也是我们在SoundCloud中使用的。

One thing stayed a constant, I like Dependency Injection! It can be very powerful and became an important item in my toolbox. But saying this, it’s one out of many. Therefore I want to talk about the role that Dagger, the most common DI framework on Android, should play in your application!

一件事保持不变,我喜欢依赖注入! 它可能非常强大,并成为我工具箱中的重要项目。 但话虽如此,这是十分之一。 因此,我想谈谈Dagger(Android上最常见的DI框架)应在您的应用程序中扮演的角色!

DI的作用 (The role of DI)

One of the first things many developers do on a greenfield project is defining the libraries they want to use: Room as a database, Moshi and Retrofit for networking, and probably Dagger for dependency injection.

许多开发人员在未开发项目上要做的第一件事就是定义他们要使用的库:将Room作为数据库,将Moshi和Retrofit用于网络,并可能将Dagger用于依赖项注入。

I’ll be honest: If you are planning your app this way, you are doing it wrong. Libraries are implementation details. Decisions about those should be delayed as long as possible. That is one of the basic lessons in Clean Code and Clean Architecture:

老实说:如果您以这种方式计划您的应用程序,那就错了。 库是实现细节。 有关这些问题的决定应尽可能延迟。 这是“ 干净代码”和“ 干净架构 ”的基本课程之一:

A good system architecture is one in which decisions like these are rendered ancillary and deferrable. A good system architecture does not depend on those decisions. A good system architecture allows those decisions to be made at the latest possible moment, without significant impact.

好的系统架构是这样的决策的辅助和可延期决策。 好的系统架构不取决于那些决定。 良好的系统架构可让这些决定在最可能的时刻做出,而不会产生重大影响。

This is something many Android apps fail at. The approach to DI I’ve seen there can be often described as “Dagger driven architecture”. For someone caring about architecture and the long term success of a project, this is disturbing. Never should your codebase be driven by a tool. Don’t sell your soul or that of your project.

这是许多Android应用程序失败的地方。 我在那里看到的DI方法通常可以描述为“ Dagger驱动的体系结构”。 对于关心建筑和项目的长期成功的人来说,这是令人不安的。 您的代码库绝不应由工具驱动。 不要卖掉您的灵魂或项目灵魂。

But how do we avoid this, especially when many blog posts and “best practices” preach exactly the opposite? How to write an app, where your DI library is just an implementation detail that can be switched without rewriting the app?

但是我们如何避免这种情况发生,特别是当许多博客文章和“最佳实践”宣扬相反的消息时呢? 如何编写应用程序,而您的DI库只是一个实现细节,可以在不重写应用程序的情况下进行切换?

Well, we can start by following some simple rules:

好吧,我们可以从遵循一些简单的规则开始:

不要在基类中使用注入的字段 (Don’t use injected fields in base classes)

I often see base classes that have fields marked for injection.

我经常看到基类具有标记为注入的字段。

abstract class BaseFragment: Fragment() {   @Inject lateinit var navigator: Navigator}

Don’t do this! This class is now limited to be implemented by classes using a JSR330 compatible DI framework. For example, this would not work with something like Koin. But why limited reusability? Reusability is probably what you aimed for in the first place with a base class.

不要这样! 现在,此类仅限于由使用JSR330兼容DI框架的类实现。 例如,这不适用于Koin之类的东西。 但是为什么限制了可重用性? 可重用性可能是您最初针对基类的目标。

How to fill a field is an implementation detail!

如何填写字段是一个实现细节

Lets the implementer decide how to set these fields. Maybe they don’t even want to use DI at all! Why force them?

让实施者决定如何设置这些字段。 也许他们甚至根本不想使用DI! 为什么要强迫他们?

A better approach is to make those fields abstract:

更好的方法是使这些字段抽象化:

abstract class BaseFragment: Fragment() {   abstract val navigator: Navigator}// ...class MyFragment: BaseFragment() {    @Inject override lateinit var navigator: Navigator}

This brings some benefits: the compiler forces you to declare the implementation detail of these abstract fields in concrete derived classes. Also: although it is defined as immutable in the base class, it can be declared as mutable in the concrete derived class — allowing it to work with Dagger injected fields without giving up encapsulation.It also becomes much clearer this way as now there is only one place to check which fields are injected, in contrast to jumping through a hierarchy of classes.Btw you should not use field injection anyway, always prefer the constructor! Therefore check out FragmentFactories if you haven’t already.

这带来了一些好处:编译器迫使您在具体的派生类中声明这些抽象字段的实现细节。 另外:尽管在基类中将其定义为不可变的,但可以在具体派生类中将其声明为可变的- 允许它与Dagger注入字段一起使用而无需放弃封装。这种方式也变得更加清晰,因为现在只有与跳过类的层次结构相反,这里是检查注入哪些字段的地方。顺便说一句,您无论如何都不应该使用字段注入,总是喜欢构造函数! 因此,如果还没有,请查看FragmentFactories 。

单身人士 (Singletons)

How do you define your singletons?

您如何定义单身人士?

We could look at patterns from Wikipedia but often it is simply solved by adding the @Singleton annotation to the class.

我们可以查看Wikipedia的模式,但是通常可以通过在类中添加@Singleton批注来解决。

@Singletonclass StatusPublisher

Probably all of us did this at some point, but don’t do this!

可能我们所有人都在某个时候做到了这一点,但是不要这样做!

Instead, ask yourself why do you need that Singleton? Based on the answer we could differentiate between two different approaches:

相反,问自己为什么需要那个Singleton? 根据答案,我们可以区分两种不同的方法:

Maybe your class needs to be a singleton under all circumstances, this is just how your class works. In that case, you should write it as one to prevent it from being used in any other way.

也许您的班级在所有情况下都必须是单身,这就是您班级的工作方式。 在这种情况下,应将其编写为一个,以防止其以任何其他方式使用。

Adding an annotation does not enforce anything!

添加注释不会强制执行任何操作!

The second case is where an instance of your class should be a shared resource in your business domain, where only one instance should exist at all times. In that case, an annotation sounds good but the right place for this information is in your module definition:

第二种情况是,类的实例应该是您的业务域中的共享资源,而在任何时候都应该只存在一个实例。 在这种情况下,注释听起来不错,但是此信息的正确位置在模块定义中:

@Provides@Singletonfun providesStatusPublisher(): StatusPublisher

That is what our module is for! And by adding the annotation there, you would not limit the re-usage of that class for other cases or modules.

那就是我们的模块的作用! 并且通过在其中添加注释,您将不会限制该类在其他情况或模块中的重用。

范围界定 (Scoping)

Scoped injections became a common pattern in the last few years. But as of that many of us also learned, scoping can be a very dangerous feature. It is easy to overuse and to create a big mess in your codebase. If you can, avoid it!

在过去几年中,有针对性的注射已成为一种常见的模式。 但是据我们许多人了解,范围界定可能是一个非常危险的功能。 容易过度使用并在代码库中造成很大的混乱。 如果可以,请避免!

Let’s look at some use cases:

让我们看一些用例:

  • Maybe you need a scoped Singleton to communicate between various classes, like a local event bus. Fortunately, we have ViewModels now and communication between Fragments of the same Activity is one of its core use cases. You don’t need two tools for the job. Decide on one.也许您需要一个作用域单一的人才能在各个类之间进行通信,例如本地事件总线。 幸运的是,我们现在有了ViewModels,并且同一Activity的Fragment之间的通信是其核心用例之一。 您不需要两种工具来完成这项工作。 决定一个。
  • Maybe you don’t want a Singleton to stick around the whole lifetime of your app to limit its footprint and therefore think of scoping?

    也许您不希望Singleton在您的应用程序的整个生命周期内停留以限制其占用空间,并因此考虑范围界定?

    Be careful not to do premature optimizations here. Measure first, if this is an actual problem before adding complexity as a solution!

    注意不要在此进行过早的优化。 如果这是一个实际问题,请先进行测量,然后再添加复杂性作为解决方案!

  • Maybe you want to use scoping because of the need to inject the current Activity? There are better ways to do this!

    也许由于需要注入当前Activity而要使用作用域? 有更好的方法可以做到这一点!

    Look at the life cycle callbacks from Jetpack to see if they fit your problem.

    查看来自Jetpack的生命周期回调,看看它们是否适合您的问题。

    Or check out the section about assisted injection here.

    或在此处查看有关辅助注射的部分。

    But maybe just do it old school: just pass the

    但也许只是在老派上做:只要通过

    Activity as an argument when you actually need it!

    在实际需要时将Activity作为参数!

    Android developers have this fear of memory leaks when passing around a

    Android开发人员担心传递一个

    Context. This is good, it’s good to be aware. But its only a problem if we store a reference. That’s exactly why scoping is so dangerous, it is all it does, storing the context!

    Context 。 这很好,要意识到这一点很好。 但是如果我们存储一个引用,这只是一个问题。 这就是范围界定如此危险的原因,它就是它所做的一切,存储上下文!

    Instead, just pass it the moment you need it! You can use some Kotlin niceties here: If I need to pass an

    相反,只需在需要时通过它即可! 您可以在此处使用Kotlin的一些技巧:如果我需要通过

    Activity I build that method as an extension function.

    Activity我将该方法构建为扩展功能。

fun Activity.onEmailTaken(event: AuthenticationEvent) {    AlertDialog.Builder(this)       .setTitle(event.title).show()}// call via sth like:with(getActivity()) {    onEmailTaken(EmailError.Taken))}

This way you make it very explicit, so you are staying alert. And no Dagger complexity at all needed.

这样,您可以使其变得非常明确,从而保持警惕。 完全不需要Dagger复杂性。

让我们来谈谈辅助注射 (Let’s talk about assisted injection)

A while ago in one of the countless Dagger discussions, someone mentioned, that a DI library that doesn’t offer Assisted Injection can’t be taken seriously.

不久前,在无数Dagger讨论中,有人提到,不能认真对待不提供Assisted Injection的DI库。

Therefore, let’s look at what Assisted Injection is:The idea is that on the module definition level you don’t have all the information to construct a class. Some arguments are dynamic and need to be passed at runtime.

因此,让我们看看什么是辅助注入:想法是,在模块定义级别上,您没有所有信息来构造一个类。 一些参数是动态的,需要在运行时传递。

Let’s assume we need some string that isn’t known at compile time. This is how you could “inject” that:

假设我们需要一些编译时不知道的字符串。 这是您可以“注入”的方式:

class MyClass@AutoFactoryconstructor(    private val parameter: String,    @Provided private val myRepository: myRepository)

What this does under the hood is generating a Factory that you can inject and pass in the missing String argument, while all the @Provided arguments are injected normally. Unfortunately to add this functionality to Dagger you need another dependency.

这实际上是在生成一个Factory,您可以注入该工厂并传递缺少的String参数,而所有@Provided参数都可以正常注入。 不幸的是,要将此功能添加到Dagger中,您需要另一个依赖项。

But to be honest many of us doing this exactly the same without even hearing of Assisted Injection. We would create these factories manually.It’s always good to not need to write a few lines of code more, but that’s all that it is. Do we really need to bound ourselves to another library? I’d say, no.

但老实说,我们许多人甚至没有听到辅助注射的情况下完全一样。 我们将手动创建这些工厂,不需要再写几行代码总是很好,但这就是全部。 我们真的需要将自己绑定到另一个库吗? 我会说,不。

自动接线 (Auto wiring)

You want your DI to be a thin layer only. It just sits on the outer layer gluing things together.

您只希望DI是薄薄的一层。 它只是位于外层,将东西粘合在一起。

But if you agree here, isn’t it counterproductive spreading annotations all-around your codebase? As those are specific to your DI approach!

但是,如果您在这里同意,是不是在代码库中产生了适得其反的扩展注释? 由于这些特定于您的DI方法!

We tend to do this a lot with Inject annotations to every constructor. Developers are lazy by nature and the alternative would be calling the constructor manually in a provide method:

我们倾向于对每个构造函数使用Inject注释来做很多事情。 开发人员本质上是懒惰的,替代方法是在Provide方法中手动调用构造函数:

@Provides @JvmStaticfun providesCatalogRepository(resources: Resources) =      CatalogRepository(resources)

But believe it or not, some of us prefer it this way!Why would we want to do that?

但不管您信不信,我们当中有些人更喜欢这种方式!为什么要这么做呢?

This question became more imminent with Service Locators like Koin, as they don’t have these kinds of code generation via annotations and therefore you would always write these calls manually, but in a much nicer way:

对于像Koin这样的服务定位器 ,这个问题变得更加迫切,因为它们没有通过注释生成这类代码,因此您总是可以手动编写这些调用,但使用的方式更好:

factory { CatalogRepository(get()) }

Some say this doesn't scale. This argument is nonsense, not only from my personal experience. Large apps can be written with Koin and have been written with DI by using providers. But most important, large apps have been written outside the JVM world. There manual wiring is the default, not the exception.

有人说这没有规模。 这种说法是胡说八道,不仅是根据我的个人经验。 大型应用程序可以使用Koin编写,也可以使用提供商使用DI编写。 但是最重​​要的是,大型应用程序是在JVM世界之外编写默认情况下手动接线 ,也不例外

Actually Uncle Bob himself suggests the same in Clean Architecture:

实际上,鲍伯叔叔本人在“清洁建筑”中也提出了同样的建议:

Maybe you use Spring to auto-wire your dependencies. That’s fine, but you should not sprinkle @autowired annotations all throughout your business objects. Your business objects should not know about Spring. Instead, you can use Spring to inject dependencies into your Main component. It’s OK for Main to know about Spring since Main is the dirtiest, lowest-level component in the architecture.

也许您使用Spring自动关联您的依赖项。 很好,但是您不应在整个业务对象中都使用@autowired注释。 您的业​​务对象不应该了解Spring。 相反,您可以使用Spring将依赖项注入到Main组件中。 因为Main是体系结构中最脏,最底层的组件,所以Main知道Spring是可以的。

In the end, it’s not so different from the Singleton annotation we talked about. If you have all the definitions in a module you know where to find them, if you need to see what’s going on. That’s what the modules are for! Nothing is worse than having half the definitions in a module, and half of them having annotation on the class itself.

最后,它与我们讨论的Singleton注释没有太大不同。 如果您在模块中拥有所有定义,那么您将知道在哪里可以找到它们,以及是否需要查看最新情况。 这就是模块的作用! 没有什么比在模块中拥有一半的定义更糟了,其中一半的定义对类本身具有注释。

仅公开您需要的内容 (Expose only what you need)

This brings us to the next thing. Looking back at the example we used above:

这将我们带入下一件事。 回顾上面我们使用的示例:

@Provides @JvmStaticfun providesCatalogRepository(resources: Resources) = CatalogRepository(resources)

Let’s assume we now have multiple variations of this repository. I would write change that code to something like this:

假设我们现在有这个存储库的多个变体。 我会将该代码编写为如下所示:

@Provides @JvmStaticfun providesCatalogRepository(resources: Resources): CatalogRepository = StaticCatalogRepository(resources)

But many developers would write:

但是许多开发人员会写:

@Provides @JvmStaticfun providesCatalogRepository(repository: StaticCatalogRepository): CatalogRepository = repository

Don’t do this! This StaticCatalogRepository is one possible implementation of our CatalogRepository interface. That means its an implementation detail! Therefore it should not be possible to get injected directly, ever! We want every client of our app or our module to use the interface and get a single source of truth. This is good old encapsulation.

不要这样! 此StaticCatalogRepositoryCatalogRepository一种可能的实现 接口。 这意味着其实现细节! 因此, 永远不可能直接注射! 我们希望我们的应用程序或模块的每个客户端都使用该界面并获取真实的唯一来源。 这是很好的旧封装。

Therefore never inject something like a RoomDatabase, instead, have a Database and glue the Room version to that interface in a module. This is the purpose of the DI layer, gluing these things together!

因此,切勿注入诸如RoomDatabase类的RoomDatabase ,而是拥有一个Database并将Room版本粘贴到模块中的该接口上。 DI层目的就是将这些东西粘合在一起

避免多重绑定 (Avoid Multi-binding)

A few years ago I gave a talk at Droidcon Berlin comparing Koin and Kodein to Dagger and Toothpick. After the presentation, someone asked me how to inject a collection of objects. In the beginning, I was stunned a bit, on what use case would need that? Since then I learned that it's more widely used than I thought. Let’s look at this.

几年前,我在柏林Droidcon上做了一次演讲,比较了Koin和Kodein与Dagger和Toothpick。 演讲后,有人问我如何注入对象集合。 一开始,我有些惊讶,需要什么用例? 从那时起,我了解到它的使用范围比我想像的要广泛。 让我们看看这个。

@Binds@IntoSetabstract fun bindAnalytics(FirebaseDelegate delegate): AnalyticsDelegate

The idea here is that there are multiple implementations of AnalyticsDelegate and we want to inject all of them.

这里的想法是有多种AnalyticsDelegate实现,我们想注入所有这些实现。

@Inject lateinit var delegates: Set<AnalyticsDelegate>

Don’t do this! This has a smell and it even has a name: leaky abstraction:

不要这样! 它有一种气味,甚至有一个名字: 泄漏抽象

In software development, a leaky abstraction is an abstraction that leaks details that it is supposed to abstract away. [Wikipedia]

在软件开发中,泄漏抽象是泄漏应该被抽象的细节的抽象。 [ 维基百科 ]

Why would any higher layer need to know that we have multiple Analytics classes? This is a low-level detail that should be hidden under any circumstances! Don’t throw away polymorphism like this!

为什么更高层需要知道我们有多个Analytics类? 这是任何情况下都应隐藏的低级细节! 不要扔掉这样的多态!

And the alternative is not far off. Good old design patterns already provide you with solutions like a Facade.Deliver one implementation of AnalyticsDelegate to your clients, that can internally forward to all the variations. No need to expose all of those!

替代方案并不遥远。 好的旧设计模式已经为您提供了Facade之类的解决方案。为您的客户提供一个AnalyticsDelegate实施,可以在内部转发所有变体。 无需暴露所有这些!

And it’s even easier to use as you call one method and not loop over every single one. With a facade you are more flexible as well: you could easily make the methods exposed thread-safe, or make them transactional…

而且,调用一个方法而不遍历每个方法更容易使用。 使用立面,您也可以更加灵活:您可以轻松地使方法公开为线程安全,或使其具有事务性。

One argument I heard for injected Sets are multi-modular projects, where each module contributes to the whole by itself.To be honest I don't think doing this is very wise. Your app should have a single source of truth in your app. It is what Clean Architecture calls Main:

我听到的关于注入Sets一个论点是多模块项目,其中每个模块都自己对整体做出贡献。说实话,我认为这样做不是很明智。 您的应用程序中应该有一个真实的来源。 这就是Clean Architecture所说的Main

Main is the dirtiest, lowest-level component in the architecture.

最主要的是架构中最脏,最底层的组件。

This is where we put things together :

这是我们将所有内容放在一起的地方:

Think of Main as a plugin to the application — a plugin that sets up the initial conditions and configurations, gathers all the outside resources, and then hands control over to the high-level policy of the application. Since it is a plugin, it is possible to have many Main components, one for each configuration of your application.

将Main视为应用程序的插件-一种可设置初始条件和配置,收集所有外部资源,然后将控制权移交给应用程序高级策略的插件。 由于它是一个插件,因此可能有许多主要组件,每个应用程序配置一个。

What if you have multiple configurations of your application, some need fewer items but you might still want other things from a module? This is why that single source of truth is important. Don’t give this control to other layers.

如果您有多个应用程序配置,有些配置需要更少的项目,但是您可能仍希望模块提供其他配置,该怎么办? 这就是为什么唯一的真理来源很重要的原因。 不要将此控件交给其他层。

摘要 (Summary)

Without a doubt, Dagger has a lot of features. Its API might sometimes feel a bit clunky but keep in mind it was designed for larger codebases, which most of us will not work on.But then the question is, do you really need all of those features? Ask yourself: What is it that you actually need?

毫无疑问,Dagger具有许多功能。 它的API有时可能有点笨拙,但请记住,它是为较大的代码库设计的,我们大多数人都不会使用它,但是问题是,您真的需要所有这些功能吗? 问问自己:您实际需要什么?

As I tried to show with the tips above: try to keep Dagger (and any other DI library) very shallow.

正如我试图用上面的技巧展示的那样:尽量使Dagger(和其他DI库)非常浅。

DI is the glue of your application components; it’s not its skeleton!

DI是您的应用程序组件的粘合剂; 这不是它的骨架!

You might wonder why would you want to hide Dagger? As it is the opinionated suggestion by Google for Android. It is here to stay, right? Actually, it is getting bound to Android closer as we speak.

您可能想知道为什么要隐藏匕首? 正如Google对于Android的建议。 它在这里停留,对不对? 实际上, 正如我们所说的 ,它与Android的联系越来越紧密 。

But things change rapidly in IT, remember all the frameworks I mentioned in the beginning? Some of them you might never been heard of but it’s not so long ago they where important. With Kotlin multi-platform getting real the place for Dagger will change and questioned.

但是,IT领域瞬息万变,还记得我一开始提到的所有框架吗? 其中一些您可能从未听说过,但不久前它们就显得很重要。 随着Kotlin多平台的实现,Dagger的地位将发生变化并受到质疑。

I often get asked by Junior developers what they should learn. Let’s say I would not want to hire someone because they know Dagger, I want to hire someone who understands DI.

初级开发人员经常问我应该学习什么。 假设我不想雇用某个人,因为他们认识Dagger,我想要雇用一个了解DI的人。

翻译自: https://proandroiddev.com/clean-dagger-f248eda5790b

匕首线切割图纸下载


http://www.taodudu.cc/news/show-3376389.html

相关文章:

  • 匕首线切割图纸下载_匕首2-利用范围和子组件
  • IOError: [Errno 2] No such file or directory的解决方法
  • ArcEngine代码 打开文件地理库GDB中的ITable表
  • 数据对齐 Tab 键
  • ArcEngine代码 GP区域分析之面积制表(统计各行政区内的各土地利用类型面积)
  • matlab图
  • 在Python中如何优雅地创建表格
  • 平均成绩和等级python_使用python3.6中的函数和列表计算平均成绩
  • google velvet_LG Velvet 5G值得推出新的设计方向
  • 昆明计算机学校有个爱什么,云南6个爱情传说,总有一个感动你
  • 勇士屠熊,绿军射鹿,夕阳西下,人群散尽,唯有烈火燎原势不可挡
  • Spring之IOC(一)
  • java程序连接2个数据库,Java连接数据库(2)
  • 第十一PickerView
  • 一只眼睛的鹿
  • php发送短信发送失败如何处理,php – 从我的网站发送短信(仅发送不接收)
  • HarmonyOS(一) 快速开始学习鸿蒙开发,官方文档学习路线解析
  • 数据库垂直拆分 水平拆分
  • 用户行为分析大数据系统(实时统计每个分类被点击的次数,实时计算商品销售额,统计网站PV、UV )
  • JavaScript大作业——美食餐饮网站设计与实现(HTML+CSS+JavaScript)
  • 如何评估研发人员效能?软件工程师报告帮你看见每个人的贡献
  • FreeBSD网站平台建设全过程
  • wordpress 大网站_加快您的WordPress网站
  • 使用Varnish为网站加速
  • Mysql学习(一)架构原理
  • php页面设置后缩放变形,网站网页缩放后变形
  • 技术人如何利用 github+Jekyll ,搭建一个独立免费的技术博客
  • 构建自己的网站(一)——uWSGI+Django实现显示图片点击更新
  • 一种用于COVID-19检测的轻量级深度学习模型实现
  • 大数据下一个十年将如何演进?

匕首线切割图纸下载_干净匕首相关推荐

  1. 匕首线切割图纸下载_匕首击剑简介

    匕首线切割图纸下载 Dependency injection (DI), in a nutshell, is a technique whereby one object provides or su ...

  2. 匕首线切割图纸下载_匕首击剑:更短更轻松!

    匕首线切割图纸下载 学习Android开发 (Learning Android Development) About more than 2 years ago, I was on a quest t ...

  3. 匕首线切割图纸下载_使用Robolectric测试带有匕首注入依赖性的类

    匕首线切割图纸下载 It is common for Android code to use dependency injection (DI). And one of the tenets of D ...

  4. 匕首线切割图纸下载_匕首2-利用范围和子组件

    匕首线切割图纸下载 范围的含义是什么? (What is the meaning of scope?) Scope refers to the lifetime of an object. Consi ...

  5. 匕首线切割图纸下载_真正的单身与匕首2

    匕首线切割图纸下载 我之前写过有关Dagger 2的文章.但是,我仍然不了解每个角落. 尤其是@Singleton注释可能会引起误解,因为用户Zhuiden十分友善地指出 : 如果您每次注入都创建一个 ...

  6. 匕首线切割图纸下载_我们从匕首到科恩的旅程

    匕首线切割图纸下载 When you think about Dependency Injection (DI) on Android, the first library you probably ...

  7. 钢铁侠头盔制作图纸下载_如何在10分钟内制作头盔图

    钢铁侠头盔制作图纸下载 我每天的大部分时间都涉及创建,修改和部署Helm图表以管理应用程序的部署. Helm是Kubernetes的应用程序包管理器,负责协调应用程序的下载,安装和部署. Helm图表 ...

  8. 魔兽怀旧网站模块下载_一个人的网站重新设计和怀旧

    魔兽怀旧网站模块下载 Despite how I look, I'm the kind kind of person that loves to play old video games. (Full ...

  9. figma下载_在Figma上进行原型制作的各种触发选项

    figma下载 Prototypes are model versions of digital products. They're used to measure usability by test ...

最新文章

  1. 王道考研 计算机网络笔记 第三章:数据链路层
  2. java注解@remote,Dwr3.0纯注解(纯Java Code配置)配置与应用浅析二之前端调用后端
  3. 笛卡尔集基本原理,等值连接,不等值连接,外连接,自连接
  4. 产品解读 | 敏捷版数据库场景 一站式快速构建企业全场景数据库管理平台
  5. CentOS 安装过程中格式化 SATA 硬盘巨慢的问题
  6. python dataframe取一列_python DataFrame列运算
  7. matlab数据游标不能使用,启用数据游标模式
  8. centos redis验证_centos7中安装、配置、验证、卸载redis
  9. win8系统软件不兼容怎么办
  10. Java常用设计模式————桥接模式
  11. 【PAT乙】1005 继续(3n+1)猜想 (25分)
  12. python元组排序_python元组怎么排序
  13. 查看APP用到的图片方法
  14. Python SQLite3 教程
  15. Lisp语言中的print函数
  16. Flask入门(三)~补充及虚拟环境
  17. 加密所有事物,将数据安全存储在任何地方
  18. C# dotnet 获取某个字符所在 Unicode 字符平面映射
  19. 不用PS,小白也能轻松搞定抠图
  20. Salesforce收购Slack背后的原因,你知道多少?

热门文章

  1. Transformers 发展一览
  2. 江苏大学计算机学院国家奖学金,江苏大学京江学院 学生素质综合测评办法
  3. 多版本office兼容办法
  4. 介绍一个学习编程的捷径
  5. 使用idea如何把代码推送到两个git仓库
  6. 可怕的Shadon 网络搜索引擎
  7. PCB中电流与线宽 电流与过孔的关系
  8. 火狐的旺旺和支付宝控件总是需要反复点击确认?
  9. 医院病房管理信息系统PHP版本
  10. sqlserver if exists 用法