Android Hilt的使用以及遇到的問題

簡介

Hilt 提供瞭一種將Dagger 依賴註入到Android 應用程序的標準方法。為Android 應用程序簡化提供一組標準的、簡化設置、可以讀的組件;且為不同類型的構建(例如:測試、調試、發行)提供一種簡單的方法。

可以理解為Google 為瞭統一依賴註入組件,但是Dagger 用起來比較復雜。就針對Android開發瞭一套適配庫。

導入Hilt

apply plugin: 'com.android.application'
apply plugin: 'dagger.hilt.android.plugin'

android {
  // ...
}

dependencies {
  implementation 'com.google.dagger:hilt-android:2.34.1-beta'
  kapt 'com.google.dagger:hilt-compiler:2.34.1-beta'

  // For instrumentation tests
  androidTestImplementation  'com.google.dagger:hilt-android-testing:2.34.1-beta'
  kaptAndroidTest 'com.google.dagger:hilt-compiler:2.34.1-beta'

  // For local unit tests
  testImplementation 'com.google.dagger:hilt-android-testing:2.34.1-beta'
  kaptTest 'com.google.dagger:hilt-compiler:2.34.1-beta'
}

kapt {
 correctErrorTypes true
}

設置correctErrorTypes 為true ,將kapt配置為更正錯誤類型 。
這裡遇到一個問題,當我的gradle 版本為 3.4.1 的時候

classpath 'com.android.tools.build:gradle:3.4.1'

apply plugin: ‘dagger.hilt.android.plugin’插件一直安裝失敗,
提示找不到 “com/android/Version” 把gradle 改成 4.1.2 就沒問題瞭
且註意 如果你是多module的項目,
apply plugin: ‘dagger.hilt.android.plugin’ 一定要plugin在主module下
(也就是跟 apply plugin: ‘com.android.application’ 一起),
若是隻在子module下,主module的註入不會被實現。(問題1,後面會解釋問題原因)

buildscript {
  repositories {
    // other repositories...
    mavenCentral()
  }
  dependencies {
    // other plugins...
    classpath 'com.google.dagger:hilt-android-gradle-plugin:2.34.1-beta'
  }
}

組件層次

Hilt把Dagger 手動創建Component 改成瞭預定義的Component,且自動集成到Android應用程序的各個生命周期中。通過註解的方式@InstallIn(xxxComponent.class)進行綁定。
下圖顯示瞭標準的Hilt組件層次結構。每個組件上方的註釋是作用域註釋,用於將綁定范圍限制為該組件的生存期。組件下方的箭頭指向任何子組件。通常,子組件中的綁定可以依賴於祖先組件中的任何綁定。

組件默認綁定

每個Hilt 組件都帶有一組默認綁定,這些默認綁定可以作為依賴註入到你自定義綁定中

Component Default Bindings
SingletonComponent Application
ActivityRetainedComponent Application
ViewModelComponent SavedStateHandle
ActivityComponent Application, Acitvity
FragmentComponent Application, Acitvity, Fragment
ViewComponent Application, Acitvity, View
ViewWithFragmentComponent Application, Acitvity, Fragment, View
ServiceComponent Application, Service

簡單使用

下面我為大傢介紹以下一些註解的使用:

  • @HiltAndroidApp
  • @AndroidEntryPoint
  • @InstallIn
  • @Module
  • @Provides
  • @Binds
  • @HiltViewModel
  • @EntryPoint

想要瞭解更多的建議直接查看官方文檔

@HiltAndroidApp

介紹

所有使用Hilt的App 必須包含一個被@HiltAndroidApp 註釋的Appliction 類。
@HiltAndroidApp 會生成一個Hilt_MyApplication 的基類,並且繼承與@HiltAndroidApp 註釋的類的基類,然後將@HiltAndroidApp 註釋的類的基類替換成Hilt_MyApplication。例如:
這是我們應用的 MyApplication

@HiltAndroidApp
class MyApplication extends BaseApplication{
}

使用@HiltAndroidApp Hilt 將會生成 Hilt_MyApplication

public abstract class Hilt_MyApplication extends BaseApplication implements GeneratedComponentManagerHolder {
  private final ApplicationComponentManager componentManager = new ApplicationComponentManager(new ComponentSupplier() {
    @Override
    public Object get() {
      return DaggerMyApplication_HiltComponents_SingletonC.builder()
          .applicationContextModule(new ApplicationContextModule(Hilt_MyApplication.this))
          .build();
    }
  });

  @Override
  public final ApplicationComponentManager componentManager() {
    return componentManager;
  }

  @Override
  public final Object generatedComponent() {
    return this.componentManager().generatedComponent();
  }

  @CallSuper
  @Override
  public void onCreate() {
    // This is a known unsafe cast, but is safe in the only correct use case:
    // MyApplication extends Hilt_MyApplication
    ((MyApplication_GeneratedInjector) generatedComponent()).injectMyApplication(UnsafeCasts.<MyApplication>unsafeCast(this));
    super.onCreate();
  }
}

並且使我們的 MyApplication 繼承 Hilt_MyApplication,通過這種方式將modules 註入到我們的應用中。
可以看到具體的註入方法就是Hilt_MyApplication onCreate() 函數中的
((MyApplication_GeneratedInjector) generatedComponent()).injectMyApplication(UnsafeCasts.unsafeCast(this));
這句代碼,generatedComponent() 返回的是 MyApplication_HiltComponents.SingletonC 對象,這個對象中就是我們所有module 的代碼實現。有興趣的同學可以自己去看一下,我這裡就不貼代碼瞭

使用

使用分為兩種情況,添加和沒有添加 Hilt Gradle插件

//沒有添加插件
@HiltAndroidApp(BaseApplication.class)
class MyApplication extends Hilt_MyApplication{}

//添加插件
@HiltAndroidApp
class MyApplication extends BaseApplication{}

建議添加插件,使用起來會更簡單。本文以下的示例都假定以使用插件。
這裡需要註意的是如果要在MyApplication 中使用註入的對象,需要在 super.onCreate() 之後才能使用。 原因且看介紹中的 Hilt_MyApplication 源碼。
這裡解釋一下問題1出現的原因,是因為我沒有添加插件但@HiltAndroidApp 使用的時候用的卻是添加瞭插件的用法。所以會出現module 註入不被實現的情況。

@AndroidEntryPoint

介紹

安卓成員註入,使用@AndroidEntryPoint 註解後就可以在該類中使用module註入的成員變量。但@AndroidEntryPoint 有類型限制,隻能在以下的類上使用:

  1. Activity
  2. Fragment
  3. View
  4. Service
  5. BroadcastReceiver

使用

@AndroidEntryPoint
public final class MyActivity extends MyBaseActivity {
  // Bindings in SingletonComponent or ActivityComponent
  @Inject Bar bar;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    // Injection happens in super.onCreate().
    super.onCreate();

    // Do something with bar ...
  }
}

同樣要註意是是需要在 super.onCreate() 後使用註入的成員變量

@Module 和 @InstallIn

介紹

@Module 跟Dagger 裡的是同一個,沒什麼好說的。
@InstallIn 通過使用@InstallIn(xxxComponent.class) 將module 安裝到指定的組件中,在Hilt 中所以module 都必須添加這個註釋,如果組件中就找不到這個module ,可能引起編譯錯誤。
當然一個module 也可安裝到多個組件上如:@InstallIn({ViewComponent.class, ViewWithFragmentComponent.class})

使用

@Module
@InstallIn(SingletonComponent.class)
public final class FooModule {
  // @InstallIn(SingletonComponent.class) module providers have access to
  // the Application binding.
  @Provides
  static Bar provideBar(Application app) {...}
}

每個組件都帶有作用域註釋,該註釋可用於記住對組件生存期的綁定。例如,要將范圍綁定到 SingletonComponent組件,請使用@Singleton批註:

@Module
@InstallIn(SingletonComponent.class)
public final class FooModule {
  // @Singleton providers are only called once per SingletonComponent instance.
  @Provides
  @Singleton
  static Bar provideBar() {...}
}

此外,每個組件都有默認情況下可用的綁定。例如,該SingletonComponent組件提供瞭Application 綁定:

@Module
@InstallIn(SingletonComponent.class)
public final class FooModule {
  // @InstallIn(SingletonComponent.class) module providers have access to
  // the Application binding.
  @Provides
  static Bar provideBar(Application app) {...}
}

@Provides 和 @Binds

介紹

@Provides 註釋Module 中的方法以創建提供者方法綁定。該方法的返回類型綁定到其返回值。
@Binds 註釋Module 中的抽象方法,一般方法的返回是一個接口,參數是實現接口的子類,在調用是會調用參數的子類中的方法實現。

使用

@Module
@InstallIn(SingletonComponent.class)
public final class FooModule {
  @Provides
  @Singleton
  static Bar provideBar() {...}
}

@Module
@InstallIn(SingletonComponent.class)
public abstract class BindModule {  
  @Binds
  @Singleton
  abstract Random bindRandom(SecureRandom secureRandom);
}

@HiltViewModel

介紹

使用 @HiltViewModel 註釋ViewModel,ViewModel 在創建的時候就會走Hilt 創建的HiltViewModelFactory 進行創建。就可以使用在創建的時候使用Module 中提供的實例

使用

@HiltViewModel
public final class FooViewModel extends ViewModel {

  @Inject
  FooViewModel(SavedStateHandle handle, Foo foo) {
    // ...
  }
}

然後就可以在帶有@AndroidEntryPoint 註解的activity、fragment 中使用瞭

@AndroidEntryPoint
public final class MyActivity extends AppCompatActivity {

  private FooViewModel fooViewModel;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    fooViewModel = new ViewModelProvider(this).get(FooViewModel.class);
  }
}

@EntryPoint

介紹

為不能使用註解的地方提供註入的對象。因為@AndroidEntryPoint 使用范圍有限,在這范圍之外要使用Hilt 註入的實例就可以使用@EntryPoint 來實現。
這個像是Hilt 把Component標準化後,使用者不能再裡面添加方法,導致不能為使用不瞭註解的地方提供依賴而做出的解決方案。

@EntryPoint
@InstallIn(SingletonComponent.class)
public interface FooBarInterface {
  Bar getBar();
}

如果使用上面的定義

Bar bar = EntryPoints.get(applicationContext, FooBarInterface.class).getBar();

小結

一開始使用的時候我看到是 安卓開發平臺“Hilt 和 Jetpack 集成”這個文檔,真坑,文檔不及時更新也不把官方鏈接放一下。吐槽一下。然後幾經周轉找到瞭官方文檔才能有幸為大傢介紹一下Hilt。
使用起來確實要比Dagger 舒服的多,少瞭很多模板代碼,范圍和生命周期的綁定也更好理解。不多bb 學它

以上就是Android Hilt的使用以及遇到的問題的詳細內容,更多關於Android Hilt的資料請關註WalkonNet其它相關文章!