当前位置: 首页 > news >正文

网站建设私人合同范本wordpress兼容html5

网站建设私人合同范本,wordpress兼容html5,招聘网58同城求职信息,距离我最近的装修公司电话前言 之前接手的一个项目里有些代码看得云里雾里的#xff0c;找了半天没有找到对象创建的地方#xff0c;后来才发现原来使用了Hilt进行了依赖注入。Hilt相比Dagger虽然已经比较简洁#xff0c;但对初学者来说还是有些门槛#xff0c;并且网上的许多文章都是搬自官网找了半天没有找到对象创建的地方后来才发现原来使用了Hilt进行了依赖注入。Hilt相比Dagger虽然已经比较简洁但对初学者来说还是有些门槛并且网上的许多文章都是搬自官网入手容易深入难如果你对Hilt不了解或是想了解得更多那么接下来的内容将助力你玩转Hilt。 通过本篇文章你将了解到 什么是依赖注入Hilt 的引入与基本使用Hilt 的进阶使用Hilt 原理简单分析Android到底该不该使用DI框架? 1. 什么是依赖注入 什么是依赖 以手机为例要组装一台手机我们需要哪些部件呢 从宏观上分类软件硬件。 由此我们可以说手机依赖了软件和硬件。 而反映到代码的世界 class FishPhone(){val software Software()val hardware Hardware()fun call() {//打电话software.handle()hardware.handle()} } //软件 class Software() {fun handle(){} } //硬件 class Hardware() {fun handle(){} }FishPhone 依赖了两个对象分别是Software和Hardware。 Software和Hardware是FishPhone的依赖(项)。 什么是注入 上面的DemoFishPhone内部自主构造了依赖项的实例考虑到依赖的变化挺大的每次依赖项的改变都要改动到FishPhone容易出错也不是那么灵活因此考虑从外部将依赖传进来这种方式称之为依赖注入Dependency Injection 简称DI 有几种方式 构造函数传入SetXX函数传入从其它对象间接获取 构造函数依赖注入 class FishPhone(val software: Software, val hardware: Hardware){fun call() {//打电话software.handle()hardware.handle()} }FishPhone的功能比较纯粹就是打电话功能而依赖项都是外部传入提升了灵活性。 为什么需要依赖注入框架 手机制造出来后交给客户使用。 class Customer() {fun usePhone() {val software Software()val hardware Hardware()FishPhone(software, hardware).call()} }用户想使用手机打电话还得自己创建软件和硬件这个手机还能卖出去吗 而不想创建软件和硬件那得让FishPhone自己负责去创建那不是又回到上面的场景了吗 你可能会说FishPhone内部就依赖了两个对象而已自己负责创建又怎么了 解耦 再看看如下Demo interface ISoftware {fun handle() }//硬件 interface IHardware {fun handle() }//软件 class SoftwareImpl() : ISoftware {override fun handle() {} }//硬件 class HardwareImpl : IHardware {override fun handle() {} }class FishPhone() {val software: ISoftware SoftwareImpl()val hardware: IHardware HardwareImpl()fun call() {//打电话software.handle()hardware.handle()} }FishPhone 只关注软件和硬件的接口至于具体怎么实现它不关心这就达到了解耦的目的。 既然要解耦那么SoftwareImpl()、HardwareImpl()就不能出现在FishPhone里。 应该改为如下形式 class FishPhone(val software: ISoftware, val hardware: IHardware) {fun call() {//打电话software.handle()hardware.handle()} }消除模板代码 即使我们不考虑解耦假若HardwareImpl里又依赖了cpu、gpu、disk等模块 //硬件 class HardwareImpl : IHardware {val cpu CPU(Regisgter(), Cal(), Bus())val gpu GPU(Image(), Video())val disk Disk(Block(), Flash())//...其它模块override fun handle() {} }现在仅仅只是三个模块若是依赖更多的模块或者模块的本身也需要依赖其它子模块比如CPU需要依赖寄存器、运算单元等等那么我们就需要写更多的模板代码要是我们只需要声明一下想要使用的对象而不用管它的创建就好了。 class HardwareImpl(val cpu: CPU, val gpu: GPU, val disk: Disk) : IHardware {override fun handle() {} }可以看出下面的代码比上面的简洁多了。 从解耦和消除模板代码的角度看我们迫切需要一个能够自动创建依赖对象并且将依赖注入到目标代码的框架这就是依赖注入框架依赖注入框架能够管理依赖对象的创建依赖对象的注入依赖对象的生命周期使用者仅仅只需要表明自己需要什么类型的对象剩下的无需关心都由框架自动完成 先想想若是我们想要实现这样的框架需要怎么做呢 相信很多小伙伴最朴素的想法就是使用工厂模式你传参告诉我想要什么对象我给你构造出来。 这个想法是半自动注入因为我们还要调用工厂方法去获取而全自动的注入通常来说是使用注解标注实现的。 2. Hilt 的引入与基本使用 Hilt的引入 从Dagger到Dagger2再到HiltAndroid专用配置越来越简单也比较容易上手。 前面说了依赖注入框架的必要性我们就想迫不及待的上手但难度可想而知还好大神们早就造好了轮子。 以AGP 7.0 以上为例来看看Hilt框架是如何引入的。 一project级别的build.gradle 引入如下代码 plugins {//指定插件地址和版本id com.google.dagger.hilt.android version 2.48.1 apply false }二module级别的build.gradle引入如下代码 plugins {id com.android.applicationid org.jetbrains.kotlin.android//使用插件id com.google.dagger.hilt.android//kapt生成代码id kotlin-kapt } //引入库 implementation com.google.dagger:hilt-android:2.48.1 kapt com.google.dagger:hilt-compiler:2.48.1实时更新最新版本以及AGP7.0以下的引用请参考Hilt最新版本配置 Hilt的简单使用 前置步骤整好了接下来看看如何使用。 一表明该App可以使用Hilt来进行依赖注入添加如下代码 HiltAndroidApp class MyApp : Application() {override fun onCreate() {super.onCreate()} }HiltAndroidApp 添加到App的入口即表示依赖注入的环境已经搭建好。 二注入一个对象到MyApp里 有个类定义如下 class Software {val name fish }我们不想显示的构造它想借助Hilt注入它那得先告诉Hilt这个类你帮我注入一下改为如下代码 class Software Inject constructor() {val name fish }在构造函数前添加了Inject注解表示该类可以被注入。 而在MyApp里使用Software对象 HiltAndroidApp class MyApp : Application() {Injectlateinit var software: Softwareoverride fun onCreate() {super.onCreate()println(inject result:${software.name})} }对引用的对象使用Inject注解表示期望Hilt帮我将这个对象new出来。 最后查看打印输出正确说明Software对象被创建了。 这是最简单的Hilt应用可以看出 我们并没有显式地创建Software对象而Hilt在适当的时候就帮我们创建好了HiltAndroidApp 只用于修饰Application 如何注入接口 一错误示范 上面提到过使用DI的好处之一就是解耦而我们上面注入的是类现在我们将Software抽象为接口很容易就会想到如下写法 interface ISoftware {fun printName() }class SoftwareImpl Inject constructor(): ISoftware{override fun printName() {println(name is fish)} }HiltAndroidApp class MyApp : Application() {Injectlateinit var software: ISoftwareoverride fun onCreate() {super.onCreate()println(inject result:${software.printName()})} }不幸的是上述代码编译失败Hilt提示说不能对接口使用注解因为我们并没有告诉Hilt是谁实现了ISoftware而接口本身不能直接实例化因此我们需要为它指定具体的实现类。 二正确示范 再定义一个类如下 Module InstallIn(SingletonComponent::class) abstract class SoftwareModule {Bindsabstract fun bindSoftware(impl: SoftwareImpl):ISoftware }Module 表示该类是一个Hilt的Module固定写法InstallIn 表示模块在哪个组件生命周期内生效SingletonComponent::class指的是全局一个抽象类类名随意抽象方法方法名随意返回值是需要被注入的对象类型接口而参数是该接口的实现类使用Binds注解标记 如此一来我们就告诉了HiltSoftwareImpl是ISoftware的实现类于是Hilt注入ISoftware对象的时候就知道使用SoftwareImpl进行实例化。 其它不变运行一下 可以看出实际注入的是SoftwareImpl。 Binds 适用在我们能够修改类的构造函数的场景 如何注入第三方类 上面的SoftwareImpl是我们可以修改的因为使用了Inject修饰其构造函数所以可以在其它地方注入它。 在一些时候我们不想使用Inject修饰或者说这个类我们不能修改那该如何注入它们呢 一定义Provides模块 Module InstallIn(SingletonComponent::class) object HardwareModule {Providesfun provideHardware():Hardware {return Hardware()} }Module和InstallIn 注解是必须的定义object类定义函数方法名随意返回类型为我们需要注入的类型函数体里通过构造或是其它方式创建具体实例使用Provides注解函数 二依赖使用 而Hardware定义如下 class Hardware {fun printName() {println(Im fish)} }在MyApp里引用Hardware 虽然Hardware构造函数没有使用Inject注解但是我们依然能够使用依赖注入。 当然我们也可以注入接口 interface IHardware {fun printName() }class HardwareImpl : IHardware {override fun printName() {println(name is fish)} }想要注入IHardware接口需要定义provides模块 Module InstallIn(SingletonComponent::class) object HardwareModule {Providesfun provideHardware():IHardware {return HardwareImpl()} }Provides适用于无法修改类的构造函数的场景多用于注入第三方的对象 3. Hilt 的进阶使用 限定符 上述 ISoftware的实现类只有一个假设现在有两个实现类呢 比如说这些软件可以是美国提供也可以是中国提供的依据上面的经验我们很容易写出如下代码 class SoftwareChina Inject constructor() : ISoftware {override fun printName() {println(from china)} }class SoftwareUS Inject constructor() : ISoftware {override fun printName() {println(from US)} }Module InstallIn(SingletonComponent::class) abstract class SoftwareModule {Bindsabstract fun bindSoftwareCh(impl: SoftwareChina):ISoftwareBindsabstract fun bindSoftwareUs(impl: SoftwareUS):ISoftware }//依赖注入 Inject lateinit var software: ISoftware兴高采烈的进行编译然而却报错 也就是说Hilt想要注入ISoftware但不知道选择哪个实现类SoftwareChina还是SoftwareUS没人告诉它所以它迷茫了索性都绑定了。 这个时候我们需要借助注解Qualifier 限定符注解来对实现类进行限制。 改造一下 Module InstallIn(SingletonComponent::class) abstract class SoftwareModule {BindsChinaabstract fun bindSoftwareCh(impl: SoftwareChina):ISoftwareBindsUSabstract fun bindSoftwareUs(impl: SoftwareUS):ISoftware }Qualifier Retention(AnnotationRetention.BINARY) annotation class USQualifier Retention(AnnotationRetention.BINARY) annotation class China定义新的注解类使用Qualifier修饰。 而后在Module里分别使用注解类修饰返回的函数如bindSoftwareCh函数指定返回SoftwareChina来实现ISoftware接口。 最后在引用依赖注入的地方分别使用China US修饰。 InjectUSlateinit var software1: ISoftwareInjectChinalateinit var software2: ISoftware此时虽然software1、software2都是ISoftware类型但是由于我们指定了限定符US、China因此最后真正的实现类分别是SoftwareChina、SoftwareUS。 Qualifier 主要用在接口有多个实现类抽象类有多个子类的注入场景 预定义限定符 上面提及的限定符我们还可以扩展其使用方式。 你可能发现了上述提及的可注入的类构造函数都是无参的很多时候我们的构造函数是需要有参数的比如 class Software Inject constructor(val context: Context) {val name fishfun getWindowService(): WindowManager?{return context.getSystemService(Context.WINDOW_SERVICE) as? WindowManager} } //注入 Inject lateinit var software: Software这个时候编译会报错 意思是Software依赖的Context没有进行注入因此我们需要给它注入一个Context。 由上面的分析可知Context类不是我们可以修改的只能通过Provides方式提供其注入实例并且Context有很多子类我们需要使用Qualifier指定具体实现类因此很容易我们就想到如下对策。 先定义Module Module InstallIn(SingletonComponent::class) object MyContextModule {ProvidesGlobalContextfun provideContext(): Context? {return MyApp.myapp} }Qualifier Retention(AnnotationRetention.BINARY) annotation class GlobalContext再注入Context class Software Inject constructor(GlobalContext val context: Context?) {val name fishfun getWindowService(): WindowManager?{return context?.getSystemService(Context.WINDOW_SERVICE) as? WindowManager} }可以看出借助Provides和Qualifier可以实现全局的Context。 当然了实际上我们无需如此麻烦因为这部分工作Hilt已经预先帮我们弄了。 与我们提供的限定符注解GlobalContext类似Hilt预先提供了: Qualifier Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD}) public interface ApplicationContext {}因此我们只需要在需要的地方引用它即可 class Software Inject constructor(ApplicationContext val context: Context?) {val name fishfun getWindowService(): WindowManager?{return context?.getSystemService(Context.WINDOW_SERVICE) as? WindowManager} }如此一来我们无需重新定义Module。 除了提供Application级别的上下文ApplicationContextHilt还提供了Activity级别的上下文ActivityContext因为是Hilt内置的限定符因此称为预定义限定符。如果想自己提供限定符可以参照GlobalContext的做法。 组件作用域和生命周期 Hilt支持的注入点(类 以上的demo都是在MyApp里进行依赖MyApp里使用了注解HiltAndroidApp 修饰表示当前App支持Hilt依赖Application就是它支持的一个注入点现在想要在Activity里使用Hilt呢 AndroidEntryPoint class SecondActivity : AppCompatActivity() {除了Application和ActivityHilt内置支持的注入点如下 除了Application和ViewModel其它注入点都是通过使用AndroidEntryPoint修饰。 注入点其实就是依赖注入开始的点比如Activity里需要注入A依赖A里又需要注入B依赖B里又需要注入C依赖从Activity开始我们就能构建所有的依赖 Hilt组件的生命周期 什么是组件在Dagger时代我们需要自己写组件而在Hilt里组件都是自动生成的无需我们干预。 依赖注入的本质实际上就是在某个地方悄咪咪地创建对象这个地方的就是组件Hilt专为Android打造因此势必适配了Android的特性比如生命周期这个Android里的重中之重。 因此Hilt的组件有两个主要功能 创建、注入依赖的对象管理对象的生命周期 Hilt组件如下 可以看出这些组件的创建和销毁深度绑定了Android常见的生命周期。 你可能会说上面貌似没用到组件相关的东西看了这么久也没看懂啊。 继续看个例子 Module InstallIn(SingletonComponent::class) object HardwareModule {Providesfun provideHardware():IHardware {return HardwareImpl()} }InstallIn(SingletonComponent::class) 表示把模块安装到SingletonComponent组件里SingletonComponent组件顾名思义是全局的对应的是Application级别。因此安装的这个模块可在整个App里使用。 问题来了SingletonComponent是不是表示Provides修饰的函数返回的实例是同一个 答案是否定的。 这就涉及到组件的作用域。 组件的作用域 想要上一小结的代码提供全局唯一实例则可用组件作用域注解修饰函数 Module InstallIn(SingletonComponent::class) object HardwareModule {ProvidesSingletonfun provideHardware():IHardware {return HardwareImpl()} }当我们在任何地方注入IHardware时获取到的都是同一个实例。 除了Singleton表示组件的作用域还有其它对应组件的作用域 简单解释作用域 Singleton 被它修饰的构造函数或是函数返回的始终是同一个实例 ActivityRetainedScoped 被它修饰的构造函数或是函数在Activity的重建前后返回同一实例 ActivityScoped 被它修饰的构造函数或是函数在同一个Activity对象里返回的都是同一实例 ViewModelScoped 被它修饰的构造函数或是函数与ViewModel规则一致 Hilt默认不绑定任何作用域由此带来的结果是每一次注入都是全新的对象组件的作用域要么不指定要指定那必须和组件的生命周期一致 以下几种写法都不符合第二种限制 Module InstallIn(SingletonComponent::class) object HardwareModule {ProvidesActivityScoped//错误和组件的作用域不一致fun provideHardware():IHardware {return HardwareImpl()} }Module InstallIn(ActivityComponent::class) object HardwareModule {ProvidesSingleton//错误和组件的作用域不一致fun provideHardware():IHardware {return HardwareImpl()} }Module InstallIn(ActivityRetainedComponent::class) object HardwareModule {ProvidesActivityScoped//错误和组件的作用域不一致fun provideHardware():IHardware {return HardwareImpl()} }除了修饰Module作用域还可以用于修饰构造函数 ActivityScoped class Hardware Inject constructor(){fun printName() {println(Im fish)} }ActivityScoped表示不管注入几个Hardware在同一个Activity里注入的实例都是一致的。 构造函数里无法注入的字段 一个类的构造函数如果被Inject注入那么构造函数的其它参数都需要支持注入。 class Hardware Inject constructor(val context: Context) {fun printName() {println(Im fish)} }以上代码是无法编译通过的因为Context不支持注入而通过上面的分析可知我们可以使用限定符 class Hardware Inject constructor(ApplicationContext val context: Context) {fun printName() {println(Im fish)} }这就可以成功注入了。 再看看此种场景 class Hardware Inject constructor(ApplicationContext val context: Context,val version: String, ) {fun printName() {println(Im fish)} }很显然String不支持注入当然我们可以向ApplicationContext 一样也给String提供一个Provides和Qualifier注解但可想而知很麻烦关键是String是动态变化的我们确实需要Hardware构造的时候传入合适的String。 由此引入新的写法辅助注入 class Hardware AssistedInject constructor(ApplicationContext val context: Context,Assistedval version: String, ) {//辅助工厂类AssistedFactoryinterface Factory{//不支持注入的参数都可以放这返回值为待注入的类型fun create(version: String):Hardware}fun printName() {println(Im fish)} }在引用注入的地方不能直接使用Hardware而是需要通过辅助工厂进行创建 AndroidEntryPoint class SecondActivity : AppCompatActivity() {private lateinit var binding: ActivitySecondBindingInjectlateinit var hardwareFactory : Hardware.Factoryoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding ActivitySecondBinding.inflate(layoutInflater)setContentView(binding.root)val hardware hardwareFactory.create(3.3.2)println(${hardware.printName()})} }如此一来通过辅助注入我们还是可以使用Hilt值得一提的是辅助注入不是Hilt独有而是从Dagger继承来的功能。 自定义注入点 Hilt仅仅内置了常用的注入点Application、Activity、Fragment、ViewModel等。 思考一种场景小明同学写的模块都是需要注入 class Hardware Inject constructor(val gpu: GPU,val cpu: CPU, ) {fun printName() {println(Im fish)} }class GPU Inject constructor(val videoStorage: VideoStorage){}//显存 class VideoStorage Inject constructor() {}class CPU Inject constructor(val register: Register) {}//寄存器 class Register Inject() constructor() {}此时小刚需要引用Hardware他有两种选择 使用注入方式很容易就引用了Hardware可惜的是他没有注入点仅仅只是工具类。不选注入方式则需要构造Hardware实例而Hardware依赖GPU和CPU它们又分别依赖VideoStorage和Register想要成功构造Hardware实例需要将其它的依赖实例都手动构造出来可想而知很麻烦。 这个时候适合小刚的方案是 自定义注入点 方案实施步骤 一定义入口点 InstallIn(SingletonComponent::class) interface HardwarePoint {//该注入点负责返回Hardware实例fun getHardware(): Hardware }二通过入口点获取实例 class XiaoGangPhone {fun getHardware(context: Context):Hardware {val entryPoint EntryPointAccessors.fromApplication(context, HardwarePoint::class.java)return entryPoint.getHardware()} }三使用Hardware val hardware XiaoGangPhone().getHardware(this)println(${hardware.printName()})注入object类 定义了object类但在注入的时候也需要可以做如下处理 object MySystem {fun getSelf():MySystem {return this}fun printName() {println(Im fish)} }Module InstallIn(SingletonComponent::class) object MiddleModule {ProvidesSingletonfun provideSystem():MySystem {return MySystem.getSelf()} } //使用注入 class Middleware Inject constructor(val mySystem:MySystem ) { }4. Hilt 原理简单分析 AndroidEntryPoint class SecondActivity : AppCompatActivity() {}Hilt通过apt在编译时期生成代码 public abstract class Hilt_SecondActivity extends AppCompatActivity implements GeneratedComponentManagerHolder {private boolean injected false;Hilt_SecondActivity() {super();//初始化注入监听_initHiltInternal();}Hilt_SecondActivity(int contentLayoutId) {super(contentLayoutId);_initHiltInternal();}private void _initHiltInternal() {addOnContextAvailableListener(new OnContextAvailableListener() {Overridepublic void onContextAvailable(Context context) {//真正注入inject();}});}protected void inject() {if (!injected) {injected true;//通过manager获取组件再通过组件注入((SecondActivity_GeneratedInjector) this.generatedComponent()).injectSecondActivity(UnsafeCasts.SecondActivityunsafeCast(this));}} }在编译期SecondActivity的父类由AppCompatActivity变为Hilt_SecondActivity因此当SecondActivity构造时就会调用父类的构造器监听create()的回调回调调用时进行注入。 由此可见Activity.onCreate()执行后Hilt依赖注入的字段才会有值 真正注入的过程涉及到不少的类都是自动生成的类有兴趣可以对着源码查找流程此处就不展开说了。 5. Android到底该不该使用DI框架? 有人说DI比较复杂还不如我直接构造呢 又有人说那是你项目不复杂用不到在后端流行的Spring全家桶依赖注入大行其道Android复杂的项目也需要DI来解耦。 从个人的实践经验看Android MVVM/MVI 模式还是比较适合引入Hilt的。 摘抄官网的现代Android 应用架构 通常来说我们这么设计UI层到数据层的架构 class MyViewModel Inject constructor(val repository: LoginRepository ) :ViewModel() {}class LoginRepository Inject constructor(val rds : RemoteDataSource,val lds : LocalDataSource ) {}//远程来源 class RemoteDataSource Inject constructor(val myRetrofit: MyRetrofit ) {}class MyRetrofit Inject constructor( ) {}//本地来源 class LocalDataSource Inject constructor(val myDataStore: MyDataStore ) {}class MyDataStore Inject constructor() {}可以看出层次比较深使用了Hilt简洁了许多。 本文基于 Hilt 2.48.1 参考文档 https://dagger.dev/hilt/gradle-setup https://developer.android.com/topic/architecture/recommendations?hlzh-cn https://repo.maven.apache.org/maven2/com/google/dagger/hilt/android/com.google.dagger.hilt.android.gradle.plugin/
http://www.w-s-a.com/news/593569/

相关文章:

  • 桂林网站制作培训学校外包seo公司
  • 莱州网站建设方案北京装修公司口碑
  • 大型网站建设济南兴田德润团队怎么样韩国女足出线了吗
  • 南通做网站找谁重庆网络推广网站推广
  • ps网站主页按钮怎么做怎样做网站的用户分析
  • 哪个网站做黑色星期五订酒店活动公司网络营销推广软件
  • 岳阳新网网站建设有限公司网页设计基础考试题目
  • 辽宁响应式网站费用海外平台有哪些
  • 杨凌规划建设局网站网站后台建设怎么进入
  • 有赞商城网站建设企业管理咨询是做什么的
  • 提供衡水网站建设中国石化工程建设有限公司邮政编码
  • 大芬地铁站附近做网站工业设计公司报价
  • 建设网站最强永年网站建设
  • 网站分站代理加盟wordpress国内工作室主题
  • 东营远见网站建设公司服装网站建设内容
  • 互助平台网站建设费用百度seo优化怎么做
  • lol英雄介绍网站模板工商局网上注册
  • 电商网站运营策划什么样的网站容易做seo
  • 网站备案需要什么流程怎么创建小程序卖东西
  • 陇西网站建设 室内设计持啊传媒企业推广
  • 连云港做网站制作首选公司如何让单位网站做防护
  • wordpress企业网站源码开发网站用什么工具做设计
  • 网站负责人不是法人seo神马网站推广器
  • 网站建设绩效考核方案wordpress支付宝付款
  • 高要区住房和城乡建设局网站如何网上注销自己的公司
  • 哪种技术做网站容易论文答辩图片做记录片的是哪个网站
  • 怎样在微信中做网站网站的备案号在哪
  • 返利淘网站怎么做wordpress htnl短代码
  • 网站 手机 appwordpress管理账户
  • 徐州网站建设 网站制作做招商网站的前景怎么样