Jetpeck Hilt介绍——Android中依赖注入简单用法(1)

问题背景

我们之前可能都接触过java spring框架,了解和接触过控制反转(IOC)和依赖注入(DI)的概念,那么,什么是IOC和DI呢? IOC是Inversion of Control的缩写,是面向对象编程中的一种设计原则,可以用来降低代码之间的耦合度。 IOC是什么的控制被反转了呢?其实是获得依赖对象的过程被反转了,控制被反转之后,获得依赖对象的过程由自身管理变为了由IOC容器主动注入。 IOC中最常见的方式叫做依赖注入(Dependency Injection,简称DI),所谓依赖注入,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中。 在java spring框架的开发过程,类获得依赖对象不需要手动的new出来,只要利用@autoWired注解配合spring框架即可自动注入对象。很显然,这种获取依赖对象的方式比我们每次都手动new出来更简洁,同时代码之间的耦合度也大大降低了。Google给我们提供了hilt来实现类似的操作,我们一起看看。

问题分析

话不多说,直接上demo看看hilt的一般使用先。 (1)项目根目录的build.gradle文件中配置Hilt的插件路径

buildscript {
    ...
    dependencies {
        ...
        classpath 'com.google.dagger:hilt-android-gradle-plugin:2.44'
    }
}

(2)在app/build.gradle文件中,引入Hilt的插件并添加Hilt的依赖库:

plugins {
    ...
    id 'dagger.hilt.android.plugin'
}
dependencies {
    implementation "com.google.dagger:hilt-android:2.44"
    kapt "com.google.dagger:hilt-android-compiler:2.44"
    ...
}

(3)我们自定义的MyApplication类加上@HiltAndroidApp注解,代码如下所示:

@HiltAndroidApp
class MyApplication : Application() {
    companion object {
        var instance: Application by Delegates.notNull()
    }

    override fun onCreate() {
        super.onCreate()
        instance = this
        ...
    }
}

(4)新建需要依赖使用的对象加上@Inject注解,代码如下:

class Truck @Inject constructor() {
    companion object {
        const val TAG = "TestHilt"
    }

    fun deliver() {
        Log.d(TAG, "Truck is delivering cargo.");
    }
}

(5)新建activity文件,加上@AndroidEntryPoint注解,代码如下:

@AndroidEntryPoint
class TestHiltActivity : AppCompatActivity() {
    private lateinit var activityTestHiltBinding: ActivityTestHiltBinding

    @Inject
    lateinit var truck: Truck

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        activityTestHiltBinding = ActivityTestHiltBinding.inflate(LayoutInflater.from(this))
        setContentView(activityTestHiltBinding.root)

        doDeliver.setOnClickListener {
            truck.deliver()
        }
    }
}

(6)activity对应的layout布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".view.TestHiltActivity">

  <Button
      android:id="@+id/doDeliver"
      android:text="doDeliver"
      app:layout_constraintTop_toTopOf="parent"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"/>
  </androidx.constraintlayout.widget.ConstraintLayout>

运行项目代码,点击按钮,运行结果如下: image.png

问题解决

(1)hilt注入点有哪些? 我们刚才demo使用注解的时候可以看到,只有Application这个入口点是@HiltAndroidApp注解来声明的,这个我们刚才已经看过了。activity是用@AndroidEntryPoint注解来声明的。hilt呢一共6个入口点,分别是: Application Activity Fragment View Service BroadcastReceiver 其中,只有Application这个入口点是使用@HiltAndroidApp注解来声明的,这个我们刚才已经看过了。其他的所有入口点,都是用@AndroidEntryPoint注解来声明的。 (2)@AndroidEntryPoint注解了Activity之后Hilt帮我们做了什么? 生成对应的Hilt_TestHiltActivity.java文件,在inject方法中通过injectTestHiltActivity将我们的TestHiltActivity注入到了ActivityComponent组件中,代码如下: composer.view.Hilt_TestHiltActivity#inject

  protected void inject() {
    if (!injected) {
      injected = true;
      ((TestHiltActivity_GeneratedInjector) this.generatedComponent()).injectTestHiltActivity(UnsafeCasts.<TestHiltActivity>unsafeCast(this));
    }
  }

composer.view.TestHiltActivity_GeneratedInjector#injectTestHiltActivity

@OriginatingElement(
    topLevelClass = TestHiltActivity.class
)
@GeneratedEntryPoint
@InstallIn(ActivityComponent.class)
public interface TestHiltActivity_GeneratedInjector {
  void injectTestHiltActivity(TestHiltActivity testHiltActivity);
}

问题总结

本文主要是介绍了jetpeck库中hilt的简单使用,使用hilt,通过注解的方式依赖注入,可以有效的简化代码,极大降低安卓项目中代码之间的耦合性,有兴趣的同学可以进一步深入研究。