在上一篇文章 Jetpack 新成员 Hilt 实践(一)起程过坑记 别离介绍了 Hilt 的常用注解、以及在实践过程中遇到的一些坑,Hilt 怎么 Android 结构类进行绑定,以及他们的生命周期z m 1 + &,这篇文章持续解说 Hilt 的用法,代码现已全部上传到 GitHub:G 8 o f !HiltWi4 S : a J I ^ O lthAppStartupSimple 假如对你有E b : E _ / Q I协助,请在库房右上角帮我点个赞。

Hilt 涉及的知识点有点多而且比较难了解,在看本篇文章之前必定要先看一下之前的文章 Jetpack 新成员 Hilt 实践(一),为了节省篇幅,这篇文章将会疏忽 Hilt 环境装备的过程等等之前文章现已介绍过的内容。

别的假如想了i k p y E解 Google 新推出的别的两个 Jetpack 新成员 App StartupPaging3 的实践与原理,能够点击下方链接前去检查。

  • Jetpack 最新成员 AndroidX App Startup 实践以及原理剖析
  • Jetpack 新成员 PY D h ] E 4 Q #aging3 数据库实践以及源码剖析(一)
  • Jetpack 新成员 Paging3 网络实践及原理剖析(二)
  • 代码地址:https://github.com/hi-dhl/AndroidX-Jetpack-Practice

经过这篇文章你将学习到以下内容:

  • 什么是注解?

  • @assist 注解和 SavedStateHans : j ] s f Zdle 怎么运用?

  • 怎么运用 @Binds 注解完成接口注入?

  • @Binds@Provides 的差异?

  • 限制符 @Qj E y } v y n { iualifierO F 4 ? 的运用?

    • 自界说限q g c { f制符 @qualifers
    • 预界说的限制符 @qualiferZ k ` P p Y }s
  • 组件效果T e 6 F Z y (@scopes 怎么运用?

  • 怎么在 Hilt 不支撑的类中履行依靠注入?

    • Hilt 怎么和 ContentProvider 一同运用?
    • H@ M 4ilt 怎么和 App Sta# g n 3rtup! d S V $ 一同运用?

Hilt 是根据 Dagger 基础上进行开发的,假如了m & b + ! A解 DagG U ) J 7ger 朋友们,应该会感觉它们很像,可是与 Dagger 不同的是, Hilt 集成了 Jetpack 库和 Android 结构类,并删除p b B X [了大部分模板代码,让开发者只需求重视怎么进行绑定,而不需求管理一切 Dagger 装备的问题。

在上篇文章现已介绍过, Hilt 怎么 Android 结构类进行绑定,以及他们的生命周期,这篇文: ) 3 v } ? : c i章将介绍 Hilt 怎么和 Jetpack 组件(ViewModel、App Startup)一同绑定,在开始介_ ; 3 y z w P绍之前{ W u W ] W N咱们先来了解一下什么是注解。S R z l }

什么是注解

之前有小伙伴在 WX 上问过我,对注解不太了解,所以想在这儿想简略w h ,的提一下。

注解是放在 Java 源码的类、办法、字段、参数前的一种特别“注释”,注解则能够被编译器打包进入 class 文件,能够在编译,类加载,运转时被读取。

常见的三个注解 @Override@Deprecated@SuppressWarnings

  • @Override: 确保子类重写了父类的办法,编译器会检查该办法是否正确地完成。
  • @Deprecated:表明某个类j A 2 6 / l T *、办法现已过期,编o % l a 0 q + M译器会检C n d查,假如运用了过期的办法,会给出提示。
  • @SuppressWarnings:编译器会疏忽产生的警告。

Hilt 怎么和 ViewModel 一同运用?

在上一篇文章仅仅简略的介绍了 Hilt 怎么和 ViewModel 一同运用,咱们持续介绍 ViewModel 的别的一个重要的参数 SavedStateHandle,首先需求增加依靠。

在 App 模块中的 builS $ 1 . +d.gradle 文件中增加以c 5 4 : ~ b下代码。

implementation 'androi$ s A y H Xdx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha01'
kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha01'

koltin 运用 kapt, Java 运用v G . B X annotationProcessor。

留意: 这个是在 Goog1 B z `le 文档上没有说到的,假如运用的是 kotlin 的话需求额外在 ApU _ a :p 模块中的 build.gradle 文件中增加以下代码,否则调用 by viewModels() 会编译不过。

// For Kotlin projects
kotlinOptions {
jvmTarge* N K ? O T Y St = "1.8"
}

在 ViewModel 的结构函数中运用 @ViewMode7 E - & ` WlInject 注解供给一个 ViewModel,假如需求用到 SavedStateHandle,需求运用 @assist 注解增加 SavedStateHandle 依靠T E F项,代码如下所示。

class HiltViewModel @ViewModelInject constructor(
private val tasksRepository: Repository,@ g U L # B
//Saveb  rdStateHandle 用于进程被停止时,保存和康复数据
@Assisted private vaP D + & G 3 o l :l savedStateHandle: SavedStat~  y :eHandle
) : ViewModel() {
// getLiveData 办法会取得一个与 key 相关联的 MutableLiveData
// 当与 key 相对应的 value 改动时 MutableLiveData 也会更新。
private val _userId: MutableLiveDatao r c -<String> = savedStateHandle.gc v o N % / p 2 uetLiveData(U7 f ? ! e u J i ?SER_KEY)
// 对外露出不行变- 8 T a ] 0 /的 LiveData
val userId: LiveData<String> = _userId
companion o) 9 jbject {
private val USER_KEY = "userId"
}
}

将用户的 u~ * v W X tserId 存储在 SavedStateHandle 中,当进程被停止时保存和康复对应的数据。

SavedStateS S 9 yHandle 是什么?SavedStateHandle 为了处理什么问题?

ActivityFragmeg | A G Anj h H A d & . ; .t 一般会在下面三种状况下被毁掉(以下内容来自 Google):

  • 从当前界面永久离开: 用户导航至其他界面或直接封闭 Activity (经过点击回来按钮或履行的操作调6 – ? :用了 finish() 办法)。对应 Activity 实例被永久封闭。
  • Activity 装备 (configuratiox f c Q En) 被改动: 例如旋转屏幕等操作,会使 Activity 需求立即重建。
  • 应用在后台时,其进程被体系杀死: 这种状况发生在设备剩下运转内存不足,体系又需求开释一些内存的时分,当进程在后台被杀身后,用户又回来该应用时 Activity 需求被重建。

ViewModel 会帮您处理第二种状况,由于在这种状况下 ViewModel 没有被毁掉,而在第三种状况下,ViewModel 被毁掉了, 当m O x N V Q }进程在后台被杀身后,则需求运用 onSaveInstanceState() 作为备用保E ^ A 6 ? ( T存数据的办法。

SavedStateHandle 的呈现便是为了处理f ) Q l F p L w { App 进程停止保存和康复数据问题,ViewModel 不需求向 A; : k F K * 8 Kctivity 发送和接纳状态。相反的,现在能够在 ViewModel 中处理保存和康复数据。

SavedStateHandle 类似于一个 Bundle,它是数据的键-值映射,这个 SavedStateHandle 包含在 ViewModel 中,它在后台进程停止时依然存在,曾经保存在 onSaveIns9 V N P { VtanceState() 中的任何数据现在都能够保存在 SavedStaU r : ~ s $ y s RteHan# d J 6 Vdle 中。

运用 @BinM & ,ds 注解完成接口注入?

注入R * 2 / + 5 Q 8接口实例有两种办法别离运用注解 @Binds@ProvideG ` d r ! 3 Gs@Provides 的办法在上一篇文章 Jetpack 新成员 Hilt 实践(一)起程过坑H N I o )Hilt 怎么和 RoB v 6 R V * W nom 一同运用Hilt 怎么和第三q q H g j P j I方组件一同运用 都有介绍,这儿咱们来介绍怎么运用注解 @Binds

interface WorkService {
fuO { - = 8 l &n init()
}
/**
* 注入结构函数,由于 Hilt 需求~ b ~ ) 9知道怎么供给] 2 g c c G T ; WorkServiceImpl 的实例
*/
class WorkServiceImpl @Inject constructor() :
Wo: + V 8 B - ;rkService {
override fun init() {
Log.e(TAG, " I am an WorkServi] r & d . ` 6 N NceImpl")
}
}
@C % bModule
@InstallIn(Apj  B % ~ , 1 z vplicationComz R vponent::class)
// 这儿运用了 ActivityComponent,因p y B 4 7 p q @ F而 WorkServiceModule 绑定到 Act7 Z + / 7ivityComponent 的生命周期。
abx # p 7 ( 1 c @stract class WorkSC J ! f 3 w j eervB  iceModule {
/**
* @Binds 注解告知 Ha 9 ! N U gilt 需K n m T求供给接口实例时运用哪个完成
*
* bindAnalyticsServz c # Z cice 函数需求为 Hilt 供给了以下信息
*      1. 函数, r Y回来类型告知 Hilt 供给了哪个接口的实例
*      2. 函数参数告知 Hilt 供给哪个完成
*/
@BS g + U x j , Einds
abstract fun bindAnalyticsService(
workServiceIm? T w ppl: WorkServiceImpl
): WorkService
}

运用注解 @Binds 时,需求供. 4 F T C % J给以下两个信息:

  • 函数参数告知 Hilt 接口的完成类,例如参数 WorkServiceImpl 是接口 WorkService 的完成类。
  • 函数回来类型告知 Hilt 供给了哪个接口的实例。

注解 @Binds 和 注解 @Provides 的差异?

  • @Binds:需求在办法参数里边清晰指明接口的完成类。
  • @Prot / H 2 0vides:不需求在办法参数里边清晰指明接口的完成类,由第三方结构完成,一般用于和第三方结构进行绑定(RetrofitRoom 等等)
// 有自己的接口完成
@Binds
abstract fun bindAnalyticsService(
workServiceImpl: WorkServiceImpl
): WorkServi` . h 1ce
// 没有自己的接口完成
@Provides
fun providePersonDao(application: Application): PersonDao {
return  Room
.databaseBuilder(application, AppDataBase::class.java, "dhl.db")
.fallbackToDestructiv1 ] o a +eMigration()
.allowk m T N P 4 $MainThreadQueries()
.build().personDao()
}
@Provides
fun provideGitHubService(): GitHubService {
return  Retrofit.Builder()
.baseUrl("https://api.githuby L : K 4.com/"w M $ a R `)
.addConverterFactQ 5 l Y ! jory(GsonConverterFaa c Hctory.create())
.build().create(GitHubServb x 7 L D Lice::class.j8 ( v .ava)
}

限制符 @Qualifie! Q t s Rr 注解的运用

来自 Google:@Qualifier 是一种注解,当类型界说了多个绑定时,运用它来标识该类型的特定绑定。

换句话说 @Qualifier 声明同一个类型,能够在多处进行绑定,我将限制符分为两种。

  1. 自界说限g 9 G ? w a 4制符
  2. 预界说限制符

自界说限制符的运用

咱们先用注解 @Qualifier 声明两个不同的完成。

// 为每个声明的限制符,供给对应的类型实例+ N l b @ N K L,和 @Binds 或许 @Provides 一同运用
@Qualifier
// @Retention 界说了注解的生命周期,对应三个值(SOURCE、BINARY、RUNTIME)
@Retention(AnnotationRetention.BINARY)
annotation class RemoteTasksDataSource // 注解的姓名,后边直接运用它
@Qualifier
@Retention(AnnotationRetention.RUN| h W ! J m ^TIMEd a N & } ? C F)
annotation class LocalTasksDataSource
  • @Qualifier :为每个声明的限制符,供给对应的类型实例,和 @Binds 或许 @Provides 一同运k S . ? T q

  • @Retention:界说了注解的生命周期,对应三个值(SOURCE、BINARY、RUNTIME)

    • Anno# * 6tationRetention.SOURM Z k v 7 0CE:仅编译期,不存储在二进制输出中。
    • AnnotationRetention.BINARY:存储在二进制输出中,但对反射不行见。
    • AK o E )nnotd y # q 6 - b pationRetention.RUNTIME:存储在二进制输出中,对反射可见。

一般y ] f Y _ H 5咱们自界说的注解都是 RUNTIME,所以务必要加上@Retention(RetentionPolicy.RUNTIME) 这个注解

来看一下 @Qualifier@u ` a p ] ; c 9 %Provides 一同运用的比如,界说了两个办法,具有相同的回来类型,可{ x ) : ; ^ 4 d是完成不同,限制符将它们~ 6 j . j % l | J标记为两个不同的绑定。

@Singleton
@RemoteTasksDataSource
@Provides
fun provideTasksRemoteDataSource(): DataSource { // 回来值相同
rI u l E ` _ W @eturI ] R @nB ( M } % 8 S ? RemoteDataSource() // 不同的完成
}
@Singleton
@LocalTasksDataSource
@Providej C u / 3 5 us
ft W l f { 2 [ Pun provideTaskLocalDataSource(appDatabase: AppDataBase): DataSource { // 回来值相同
return LocalDataSoK 2  7 P 6 xurce(appDatabase.personDao()) // 不同的完成
}

当咱们声明完 @Qualifier 注解之后,就能够运用声明的两个 @Qualifier,来看个比如,界说一个 Repository 结构办法里边传入用 @Q& R G 1 3 1 jualifs r @ B Rier 注解声明的两个不同完成。

@Singleton
@Provides
fun provideTasksRepository(
@LocalTasksDataSource localDataSource: DatQ y P ~ N . u G EaSource,
@RemoteTasksDataSource remoteDataSourceq K e 4 v j 7 T: DataSource
): Repository {
return TasksRepository(
localDataSource,
remote! T 1DataSource
)
}

provideTasksRepository 办法内,传入的参数F K r都是 DataSource,可是前面用 @Qualifier 注解声明晰它们不同的完成。

预界说限制符

Hilt 供给 L y / D 0 Q } B了一些预界说限制符,例如你或许在不同的状况下需求不同的 ContextApplictionActivity)Hilt 供给了 @ApplicationContext@ActivityContext 两种限制符。

class HiltViewModel @ViewModelInject constructor(
@ApplicationContext appContext, , 0 S O ( %: Context,
@9 V ] _ ] % YActivityContel v s ~ o 2 Fxt actContext: Context,
private val tasksRepository: Repository,
@Assisted private val savedStateHandle: SavedStateHandle
)

组件效o A x W 果域 @scopes 的运用

默许状况下,Hilt 中的一切绑定都是无效果域的,这意味着每次应用程序恳求绑定时,Hilt 都会创立一个所需类型的新实例。

@scopes 的效果在指定效果域规模内(Application` { / 3 / K K aActivX K ! # 7ity 等等) 供给相同的实例。

Hilt 还允许将绑定的效果域限制到特定组件,Hilt 只为绑定效果域U P / ~ ; v到的组件的每个实n C [ /例创立一次规模绑定,一切绑定恳求共享同一个实例,咱k a E 5们来看一比w # L如。

@SY a b a ? @ Ringleton
class HiltSimple @Inject constructor() {
}

HiltSimple@Singleton 声明晰其效果域,那么在 Application 规模内供给相同的实例,代码如下所示,咱们能够运转 Demo 看一下输出G h . v { A e #结果。

MainActivity: com.hi.dhl.hilt.appstartup.di.HiltK w =Simple@8f75417
HitAppCompatActivity: com.hi.dhl.hilt.appJ @ * - q V 5 n ~startup.di.Hil& c P 2 r WtSimple@8f75417

留意:绑定组件规模或许十分的昂贵,由于O L y Q K { D供给的对象会保留在内存中,直到该组件被毁掉,应该尽量减少在应用程序中运用B 2 r j {绑定` z T Q 2 7组件规模,关于要求在必定规模内运用同一实例的绑定,或许关于创立本钱昂扬的绑定,运用组件规模的绑定是适宜的。

下表列出了每个生成组件的 scope 注解对应的规模。

Android class Generated component Scope
Application ApplicationComponent @SingletoA 0 D n a Z y An
View Modw F Y S g Q ~ rel L d 8 ActivityRetainedComponent @ActivityRetainedScope
Activity ActivityCompon1 5 ? _ | K R Cent @ActivityScoped
Fragment FragmentComponent @FragmentScoped
View ViewComponent @ViewScoped
View annotated with @WithFragX o a i L R X YmentBindings ViewWithFragmentComponent @ViewScoped
Service ServiY N H 8 K ^ 8 %ceComponent @ServiceScoped

在 Hilt 不支撑的类中履行依靠注入

Hilt 支撑最常见的 Android 类 Applica5 s ] 6 . 8 jtionActivityFragmentViewServiceBroadcastReceiver 等等,可是您或许需求在 Hilt 不支撑的类中履行依靠注入,在这种状况下能够运用 @EntryPoint 注解进行创立,Hilt 会供给相应的依靠。

@EntryPoint:能够运用 @EntryPointR z v 4 b v 注解创立入口点,@EntryPoint 允许 Hilt 运用 Hilk h I S % 6 &t 无法在依靠中供给依靠的对象。

例如 Hilt 不支撑 ContentProvider,假如你在想在 ContentProvider 中获取 Hilt 供给的依靠,你能够界– Z j说一个接口,j # T & _ ! 3 :并增加k & / h k N I ] @Eu X G y k )ntryPointh O 7 x W F I解,然后增加 @InstallIn 注解指定 module 的规模,代码如下所示。

@EntryPoint
@InstallIn(ApplicationComponent::class)
inc E = g }terface InitializerEntryPoint {
fun injectWorkSw z 0 Y X k $ D Mervice(): WorkService
companion object {
fun resolve(context: Context): InitializerEntryPo6  Y e 7 jint {
val appContext = context.applicationContext ?6 w D R $ t w u : throw IllegalStateExcep_ N 5 w {tion()
return EntryPointAccessors.fromApplication(
appContext,
InitializerEntryPp z K roint::class.java
)
}
}
}

运用 EntryPointAccesso* D O M 1 f $ j ;rs 供给四个静态办法进行拜访,别离是 fromActivityfromApplicationfromFragmentL T ] W ,fromView 等等

Jetpack 新成员 Hilt 实践之 App Startup(二)进阶篇

EntryPointAccessors 供给四个静态办法,第一个参数是 @EntryPoint 接口上 @InstallIn 注解指定 module 的规模,咱B t 2 D们在接口 InitializerEnG n e @ k 5 * t HtryPoint@InstallIn 注解指定u 0 Y o modull Z 7 o f ; F ]e 的规模是 ApplicationComponent,所以咱们应该运用 EntryPointAcC t | 6 u Jcessors 供给的静态办法 fromApplication

class WorkContentProvider : ContentProvider() {
overH - u = [ | T T Aride fun onCreate(): Boolean {
context?.run {
val service = InitializerEntryPoint.resolve(this).injectWorkService()
Log.e(TAG, "WorkContentProvider ${service.init()}")
}
re N P D ~ jturn true
}M D | } g
......
}

ContentPro8 q } K i ovider 中调X { A ! _EntryPointAccessors 类中的 fromAL B U & z wpplication 办法就能够获取到 Hit 供给的依靠。

Hilt 怎么和 App Startup 一同运用

App Startup 会默许供给一个 InitializationProv* F H u } b uiderInitializationProvider. I y 承继 ContentProvider,那么 Hilt 在 App Startup 中运用的办法和 ContentProvider 相同。

cG ; V 1 | ! flass AppInity | m f H #ializer : Initializer~ K 9 | {<Unit> {
override fun create(context: Context): Unit {
val service = InitializerEntryPoint.re? z ) % 4 R fsolve(context).injectWorkService()
Log.e(TAG, "AppInitializer ${service.init()}")
return Unit
}
override fun dependencies(): MutableList<C0 i [ E l k : { ~lass<out Initializer<*&gF  : 4 @ t Ct;>> =
mutableListOf()
}

经过调用 EntryPoinE 7 S UtAccessors 的静态办法,获取到 Hit 供给的依靠,关于 App Startup 怎么运用能够& U ] W检查这篇文章 Jet^ o c k r 6 E ] `pack 最新成员 AndroidX App Startup 实5 – G践以及原理剖析

总结

到这儿关于 Hilt 的注解运用都介绍完了,代码现] i 6 ~已全部上传到了 GitHub:HiltWithAppStaR [ x ~ u A qrtupSimple。

HiltWithAppStartupSimple 包含了本篇文章` & B ) 4和 Jetpack 新成员 Hilt 实践(一)起程过坑记 文章中运用的事w ; ! 0 J G J例,假如之前[ & # Z ~ t H = ;没有看过能够先去了解一下,之后看代码会更加的清楚。

Hilt 是根据 Dagger; | D 基础上进行开发的,入门要比 Dagger 简略许多,不需求去管理一切的 Dagger 的装备问题,可是e O Q e B B S其入门的门槛仍是很高的,尤其是 Hilt 的注解,需求了解其每个注解的含义才能正确的运用,避免资源的浪费B } g . O

这篇文章和之前4 H S h Jetpack 新成员 Hilt 实践(一)起程过坑记 的文章其中许多事例都重新去设计了,由于 Google 的供给的事例,的确很难让人了解,期望这两篇文章能够协助小伙伴们快速入门 Hilt~ ( ; , D,后边还会有更多实战事例。

计划建立一个最全、最新的 AndroidX Jetpack 相关组件的实战项目 以及 相关组件原理剖析3 = @ ? N u文章,正在逐步增加 Jetpack 新成员! i l N @ P R k,库房持续更新,能够前去检查:AndroE * y z NidX-Jetpack-Practice, 假y e H 7 2如这个库房对你有协助,请在库房右上角帮我点个赞,后边我会连续完成更多 Jetpack 新成员的项目实践。

结语

致力于分享一系列 Android 体系源码、逆向剖析、算法、Q 4 X l | 9 a B 3翻译、Jetpack 源码相关的文章,V T . ] ` M ! ^ )正在尽力写出更好的文章,假如这篇文章对你有协助给个 star,一同来学习,等待与你一同生长。

算法

由于 LeetCode 的题库庞大,每个分类都能筛选; $ w & l出数百道题,由于每个人的精力有限,不行能刷完一切标题,因而我按照经典类型标题去分类、和标题的难易程度去排序。

  • 数据结构: 数组、栈、行列、字符串、链表、树……
  • 算法: 查找算法、搜索算法、2 v y } O T e /位运算、排序、数学、……

每道标题都会用 Java 和 kotliw 2 z /n 去完成,而且每道标题都有解题思路、时刻复杂度和空间复杂度,假如2 [ ! t ] u S你同我相同喜欢算法、LeP H k etCode,能够重视我 GitHub 上的 LeetCode 题解:Leetcode-SolutiK s ) p P P k * [ons-with-Java-And-Kotlin,一同来学习,等待与你一同生长。

Android 10 源码系列

正在写一系列的 Android 10 源码剖析的文章,了解体系源码,不仅有助于剖析问题,在面试过程中,对咱们也是十分有协助的,假如你同我相同喜欢研究 Android 源码,能够重视我 GitH; Y nub 上e i 5的 Android10-Source-Analysis,文章都会同步到这个库房。

  • 0xA01 Android 10 源码剖析:APK 是怎么生成的
  • 0xA02 Android 10 源码剖析:APK 的装置流程
  • 0xA03 And. O [ B Troid 10 源码剖析:APK 加载流程之资源加载
  • 0xA04 Android 10 源码剖析:APK 加载流程之资源加载(二)
  • 0xA05 Android 10 源码剖析r G C i ; + c:Dialog 加载绘制流程以及在 Kotlin、DataBinding 中的运用
  • 0xA06 Android 10 源码剖析:WindowX w rManager 视+ _ R p A _ 1 V P图绑定以及体系结构: % . o F s
  • 0xA07 Android 10 源码剖析:Window 的类型 以及 三维视图层级剖析
  • 更多……

Android 应用系列

  • 怎么在项目中封装 Kotlin + Android Databinding
  • 再会吧 buildSrc, 拥抱 Composing builds 提高 Android 编译速度
  • 为数不多的人知道+ n & L = i ] 3 f的 Kotlin 技巧以及 原了解析
  • Jetpack 最新成员 AndroidX App Startup 实践以及原理剖析
  • Jetpack 成员 Paging3 实践以及源码剖析(一)
  • Jetpack 新成员 Paging3 网络A T ~ n L U 7 j P实践及原s 0 ! T l = ; ^ t理剖析(二)
  • Jetpack 新成员 Hilt 实践(一)起程过坑记

精选译文

现在N 7 M 3 R B U正在整理和翻译一系列精选国外的技术文章,不仅仅是翻译,许多优秀的英文技术文章供给了很好思路和办法,每篇文章都会有译者考虑部分,对原文的更加深化的O h P n M ? ` t E解读,能够重视我 Gt d G t F w e xitHub 上的 Technical% W 3 L-Article-Translation,文章都会同步到这个库房。

  • [译][Google工程师] 刚刚发布了 FrL v n ! ( X b Wagment 的新特性 “F( = X 7 Eragment 间传递数据的新办法” 以及源码剖s y ] j M m % u &
  • [译][Google工程师] 详解 FragmentFactory 怎么高雅运用 Koin 以及部分源码剖析
  • [译][2.4K Start] 放弃 Dagger 拥抱 Koin
  • [译][5k+] Kotlin 的性能( k 0 ( l X ? E p优化那些事
  • [译] 解密 RxJava 的反常处理机_ w :
  • [译][1.4K+ Star] Kotlin 新秀 Coil VS Glide and Picasso
  • 更多……2 c g ~ ;

东西系8 y E k # I j

  • 为数不多的} H o V # U o !人知道m E W ! ~ y I K (的 AndroidStudio 快捷键(一)
  • 为数不多的人知道的 AndroidStudio 快捷键(二)i = . F , 1 j
  • 关于 adb 指令你所需求知道的
  • 10分钟入门 Shell 脚本编程
  • 根据 Smali 文件 Android Studio 动态调试 APP
  • 处理在 Android Studl q R kio 3.2 找不到 Android Device Monitor 东西