透过sunflower看看Google最近在Android上有什么花活(一)--- Hilt

1,089 阅读4分钟

sunflower

sunflower是什么?太阳花?Google没出这个框架吧。

其实,sunflower是Google基于Google开发框架,加入大量Android JetPack组件的示例项目,涉及到kotlin,kotlin协程,新fragment,hilt,DataSource,paging3等等等等。

话不多说,先clone一下吧,地址如下:github.com/android/sun…

开始

clone完成之后,打开项目,项目本身并不复杂,也没有使用插件化,组件化之类的技术。

在编译项目的时候,发生了gradle版本错误,因为项目默认的gradle版本是4.1.0,我的android studio还是4.0,通过升级android studio或者将gradle版本改成4.0.0解决。

常规操作,打开AndroidManifest文件,很简单的配置文件,找到首页 GardenActivity 。 同样,极简的代码,一点点看。

image-20210311152642448.png

两个知识点出现了,DataBinding和Hit。一个个来,先Hilt

Hilt

Hilt?这是什么,遇到不认识的单词,我们一般做的是放到翻译网站翻译一下,主流翻译的结果是剑柄,刀柄。还是不明觉厉,此时,我们注意到hilt的包的路径,是不是很熟悉,dagger,依赖注入?dagger中文意思是匕首,而hilt是刀柄,是不是有点明确了,光有刀还不行,还需要一个可以更安全的使用刀,而且还能保护自己安全的刀把。

那么,先下个结论,hilt的职责是依赖注入,而且是更方便,更安全的依赖注入。

引入

  1. 在根目录的build.gradle添加hilt-android-gradle-plugin插件

    buildscript {
        ...
        dependencies {
           ...
            classpath 'com.google.dagger:hilt-android-gradle-plugin:2.28-alpha'
        }
    }
    
  2. 在项目的build.gradle中添加依赖

        apply plugin: 'kotlin-kapt'
        apply plugin: 'dagger.hilt.android.plugin'
    
    dependencies {
      ....
        implementation "com.google.dagger:hilt-android:2.28-alpha"
        kapt "com.google.dagger:hilt-android-compiler:2.28-alpha"
    
    }
    
    • 如果不用Java8,引入的过程到这就结束了

使用

  1. 需要新建application,加入@HiltAndroidApp注解,随后将其添加到配置文件中

    @HiltAndroidApp
    class BaseApplication : Application()
    
  2. 接下来就是刚刚提到的GardenActivity了,在代码中出现了@AndroidEntryPoint注解,Hilt总共支持六种Android类,除了刚刚出现过得Application和Activity,还有Fragment,View,Service,BroadcastService。

    我们以Activity为例,简单看一下具体的工作流程。新建一个Activity,添加@AndroidEntryPoint注解。

    @AndroidEntryPoint
    class HiltMainActivity : Activity() {
        
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
        }
    }
    
    • 此时,是不是很纳闷,然后呢,说好的依赖注入呢,注入的对象呢?
  3. 添加注入的对象,此处以运送货物为例,创建Truck类

    class Truck {
        fun deliver() {
            Log.d(Util.TAG,"delivering")
        }
    }
    
  4. 将truck注入到Activity中

    @AndroidEntryPoint
    class MainActivity : AppCompatActivity() {
    
        @Inject lateinit var truck: Truck
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            truck.deliver()
        }
    }
    
    • 出现了另一个注解@Inject,这个注解的作用就是将truck注入到Activity中,而且MainActivity并没有实例化Truck的代码,但是能够调用Truck的deliver方法,一个词---方便。

    • 如果就这样,尝试运行的话,就会报错了。还需要做一些改变,Truck类也是需要@Inject修饰的

      class Truck @Inject constructor() {
          fun deliver() {
              Log.d(Util.TAG,"delivering")
          }
      }
      
      • 在主构造函数之前加入@Inject注解,此举是告诉Hilt,Truck实例是如何产生的,在主构造函数之前加入@Inject,是告诉Hilt,Truck的实例是通过主构造函数产生的。
      • 修改之后运行,成功运行

    带参数注入

    在运输过程中,肯定是需要一个司机的,那么就把司机当做参数传入吧,稍作修改

    class Truck @Inject constructor(val driver: Driver) {
        fun deliver() {
            Log.d(Util.TAG,"delivering..., the driver is $driver")
        }
    }
    
    • 此时,hilt还不知道Driver是如何实例化的话,那么,我们就按照之前的格式在Driver类中加入@Inject

      class Driver @Inject constructor()
      

    运行代码之后,结果如下:

image-20210312140533767.png

可见,成功打印出了driver的身份。

Hilt和Retrofit

以上是Hilt的基本用法,如果是要将第三方库注入的话,使用@Inject注解就不能解决问题了,此时,就用到了另一个注解@Module。

下面,让我们试一试hilt今天Retrofit是如何联动的吧

引入Retrofit

首先,需要引入Retrofit

implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.google.code.gson:gson:2.8.6'

Retrofit Module

成功引入之后,需要添加一个文件进行Retrofit的初始化工作

@Module
@InstallIn(ApplicationComponent::class)
class Network {

    @Provides
    @Singleton
    fun provideOkHttpClient(): OkHttpClient {
        return OkHttpClient.Builder()
            .build()
    }

    @Provides
    @Singleton
    fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
        return Retrofit.Builder()
            .client(okHttpClient)
            .baseUrl("https://wanandroid.com")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }

    @Provides
    @Singleton
    fun provideService(retrofit: Retrofit): ChapterApi {
        return retrofit.create(ChapterApi::class.java)
    }
}
  • @InstallIn注解,意思是将这个模块安装到Application中去,这样,声明周期就和Application一致了
  • @Provides 常用于被 @Module 注解标记类的内部的方法,并提供依赖项对象
  • 因为每次加入注解,都会创建一个实例,而我们的想法自然是单例,提高效率,这也很简单,加上@Singleton就可以了。

尝试获取数据

既然准备工作都好了,就开始试着获取数据吧

创建Api

第一步自然是创建Retrofit获取数据的api,以wanAndroid的后台api为依托。

interface ChapterApi {
    @GET("/wxarticle/chapters/json")
    suspend fun getChapter()
}

创建Bean文件

新建Bean文件,借助于android studio插件kotlin data classes from Json自定生成数据类

MainActivity

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject lateinit var service: ChapterApi

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initChapter()
    }

    private fun initChapter() {
        GlobalScope.launch {
            val chapterData: ChapterData = service.getChapter()
            Log.d(Util.TAG,chapterData.errorMsg + chapterData.errorCode)
        }
    }
}
  • 这是主界面实现的代码,这里只是简单的获取了errorCode和errorMsg,但是能看到,代码已经非常非常简洁了
  • 借助于kotlin的协程,替代原本网络请求中的回调,代码行数减少了,效率自然就高了