sunflower
sunflower是什么?太阳花?Google没出这个框架吧。
其实,sunflower是Google基于Google开发框架,加入大量Android JetPack组件的示例项目,涉及到kotlin,kotlin协程,新fragment,hilt,DataSource,paging3等等等等。
话不多说,先clone一下吧,地址如下:https://github.com/android/sunflower.git
开始
clone完成之后,打开项目,项目本身并不复杂,也没有使用插件化,组件化之类的技术。
在编译项目的时候,发生了gradle版本错误,因为项目默认的gradle版本是4.1.0,我的android studio还是4.0,通过升级android studio或者将gradle版本改成4.0.0解决。
常规操作,打开AndroidManifest文件,很简单的配置文件,找到首页 GardenActivity 。 同样,极简的代码,一点点看。
两个知识点出现了,DataBinding和Hit。一个个来,先Hilt
Hilt
Hilt?这是什么,遇到不认识的单词,我们一般做的是放到翻译网站翻译一下,主流翻译的结果是剑柄,刀柄。还是不明觉厉,此时,我们注意到hilt的包的路径,是不是很熟悉,dagger,依赖注入?dagger中文意思是匕首,而hilt是刀柄,是不是有点明确了,光有刀还不行,还需要一个可以更安全的使用刀,而且还能保护自己安全的刀把。
那么,先下个结论,hilt的职责是依赖注入,而且是更方便,更安全的依赖注入。
引入
-
在根目录的build.gradle添加hilt-android-gradle-plugin插件
buildscript { ... dependencies { ... classpath 'com.google.dagger:hilt-android-gradle-plugin:2.28-alpha' } }
-
在项目的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,引入的过程到这就结束了
使用
-
需要新建application,加入@HiltAndroidApp注解,随后将其添加到配置文件中
@HiltAndroidApp class BaseApplication : Application()
-
接下来就是刚刚提到的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) } }
- 此时,是不是很纳闷,然后呢,说好的依赖注入呢,注入的对象呢?
-
添加注入的对象,此处以运送货物为例,创建Truck类
class Truck { fun deliver() { Log.d(Util.TAG,"delivering") } }
-
将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()
运行代码之后,结果如下:
可见,成功打印出了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的协程,替代原本网络请求中的回调,代码行数减少了,效率自然就高了