android Jetpack-Hilt基础入门

相关资料

https://developer.android.com/codelabs/android-hilt?hl=zh-cn#11

https://developer.android.com/training/dependency-injection/hilt-android

https://blog.csdn.net/guolin_blog/article/details/109787732

项目引用

注意: 同时使用 Hilt 和数据绑定的项目需要 Android Studio 4.0 或更高版本

项目的根级 build.gradle添加

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

然后,应用 Gradle 插件并在 app/build.gradle 文件中添加以下依赖项:

apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'

android {
 ...
}

dependencies {
 implementation "com.google.dagger:hilt-android:2.28-alpha"
 kapt "com.google.dagger:hilt-android-compiler:2.28-alpha"
}

Hilt 使用 Java 8 功能,将以下代码添加到 app/build.gradle 文件中:

android {
 ...
 compileOptions {
 sourceCompatibility JavaVersion.VERSION_1_8
 targetCompatibility JavaVersion.VERSION_1_8
 }
}

注解关键字

@HiltAndroidApp

使用在Application上,不用就报错

@AndroidEntryPoint

使用在要注入的地方的类上

@InstallIn

作用在Module上,标明作用域范围。

@Inject

用在要注入的类的构造参数上

@Binds

标记抽象方法, 返回接口类型, 实现是方法的唯一参数.

@Named

别名,基本用于接口多实现/多构造参数时候

@Provides

用在方法上, 提供返回值类型的依赖,

@Singleton

单例

@Qualifier

用在注解上,注解用来区分要提供的参数

@Module

用在类上,标记这是一个module. 在Kotlin代码中, module可以是一个Object.

@ViewScoped

用于提供对象的方法上,标识为作用于view范围

@ServiceScoped

用于提供对象的方法上,标识为作用于Service范围

@FragmentScoped

用于提供对象的方法上,标识为作用于Fragment范围

@ActivityScoped

用于提供对象的方法上,标识为作用于Activity范围

@ActivityRetainedScoped

用于提供对象的方法上,标识为作用于ViewModle

@ActivityContext

内置的Activity类型Context,当做参数的时候会默认提供一个ActivityContext,而不用再传参

@ApplicationContext

内置的Application类型Context,当做参数的时候会默认提供一个Application 类型的Context,而不用再传参

注解使用

@HiltAndroidApp

使用hilt首先要在Application上添加 不然会报错

Hilt Activity must be attached to an @AndroidEntryPoint Application.

@HiltAndroidApp
public class HiltDemoApp extends Application {
}
@AndroidEntryPoint

@AndroidEntryPoint 添加在要注入类的对象顶部,不添加直接报null指针错误。

@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {
 @Inject
 Man man;
}
@Inject

@Inject 用的地方有两处。如下

  • 用在要注入对象里,就是告知 要注入哪个构造参数 用来实例化
public class Man implements IPerson {
    
     @Inject
     public Man() {

     }
     @Override
     public void say() {

     }
    }
  • 在注入的页面里或者对象里 代表我要注入一个该类型对象
 @AndroidEntryPoint
    public class MainActivity extends AppCompatActivity {
     @Inject
     IPerson man;
    
    }
@Binds

官网说 标记抽象方法, 返回接口类型, 实现是方法的唯一参数。我总结如下

  • 用在modle里

  • 作用在抽象方法上

  • 返回你传入的参数类型,也就是参数括号里传啥返回啥

  • 返回值如果是接口的时候,则该接口只有一个实现。 在使用的地方可以直接用 xxx接口 对象的方法中注入如下。

@Binds
abstract IPerson getWoMan( Woman man);
@Inject
IPerson woman;

如果返回值是接口,且有多个实现的时候,那么需要处理。需要添加一个注解进行区分,下面注解部分会详细说。或者使用@Named("xxx")进行区分

@ManTag
@Binds
abstract IPerson getMan( Man man);


@WoManTag
@Binds
abstract IPerson getWoMan( Woman man);
 @ManTag
 @Inject
 IPerson man;
 @WoManTag
 @Inject
 IPerson woman;
@Module

对外提供对象

@Module 使用场景有两种

@Binds 和@ Provides

注意事项

  • @Binds和@Provides不能放在同一个module里.

  • @Provides和@Inject冲突

  • @Named和@自定义注释效果一样

  • 使用@Binds Modle 类是个抽象类,方法是抽象方法,传参是要返回的对象类型

  • 使用@Provides Modle 类不能是抽象类

  • 使用 @Module 必须 使用 @InstallIn添加使用范围

使用@Module 个人认为大部分为了解决下面几个问题

  • 对象构造带参数/多构造

  • 返回值为接口,实现对象唯一

  • 返回值为接口 但是接口实现不唯一

  • 返回对象 不能直接new,比如三方对象

@Binds 基础用法 接口单实现

IPerson 是一个接口 当前只有Man类实现,这么写注入正常。对了记得在Man 构造添加 @Inject

//Module
@Module
@InstallIn(ActivityComponent.class)
abstract class PersonModel {
 @Binds
 abstract IPerson getMan( Man man);
}


//注入
@Module
@InstallIn(ActivityComponent.class)
abstract class PersonModel {

 @Binds
 abstract IPerson getMan( Man man);}

public class Man implements IPerson {

 //记得添加@Inject
 @Inject
 public Man() {

 }

 @Override
 public void say() {

 }
}

当 IPerson 多实现的时候,我们只对外提供Man,上面代码还不会有问题,但是我们添加Woman的提供时候,就有问题了。代码如下。其实想想也是,多实现,你直接接口注入,谁知道要Man还是Woman

//Iperson 多实现的时候
public class Woman implements IPerson {
 public Woman() {

 }

 @Override
 public void say() {

 }
}



// 添加对外提供woMan方法
@Module
@InstallIn(ActivityComponent.class)
abstract class PersonModel {

 //提供Man
 @Binds
 abstract IPerson getMan( Man man);


 //提供Woman
 @Binds
 abstract IPerson getWoMan( Woman man);


}

//注入点
@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {


 @Inject
 IPerson person;}

解决方案

@Named("xxx")

//利用@Named区分
@Module
@InstallIn(ActivityComponent.class)
abstract class PersonModel {

 @Named("Man")
 @Binds
 abstract IPerson getMan( Man man);


 @Named("Woman")
 @Binds
 abstract IPerson getWoMan( Woman man);


}


//注入 利用@Named区分
@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {

 @Named("Man")
 @Inject
 IPerson man;

 @Named("Woman")
 @Inject
 IPerson woMan;}

@自定义注解 这里 会用到@Qualifier

定义两个注解,在注入的时候添加。

其实自定义注解和@Named 其实就是起别名操作方式。看个人需求选择吧

//ManTag
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@interface ManTag {
}

//WoManTag
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@interface WoManTag {
}


//注入
@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {

 @ManTag
 @Inject
 IPerson man;

 @WoManTag
 @Inject
 IPerson woMan;}
@Provides

适宜用情况

  • 多构造参数(无法通过构造函数注入)

  • 接口多实现

  • 某个类不归自己所有,类无法直接创建(RetrofitOkHttpClient 或Room 数据库这种)

注意:

  • 带参数的就不要带@Inject了,Inject在多构造参数的时候会报错。

  • 使用Provides @Moulde 类不能是抽象类

小tips:

  • 函数返回类型会告知 Hilt 函数提供哪个类型的实例。

  • 函数参数会告知 Hilt 相应类型的依赖项。

  • 函数主体会告知 Hilt 如何提供相应类型的实例。每当需要提供该类型的实例时,Hilt 都会执行函数主体。

    在@Provides上 也可以用@Named 区分具体注入类型, 也可以使用自定义注解

//我们给Woman添加一个带参的构造 注意 用@Provides 构造就不用加@Inject了
public class Woman implements IPerson {

 public Woman() {
 }


 public Woman(String name) {
 }
 @Override
 public void say() {

 }


}




//module 
@Module
@InstallIn(ActivityComponent.class)
public class ProvideModel {

 //返回Man类型
 @Provides
 @ManTag
 public IPerson getMan() {
 return new Man();
 }

 //返回Woman无参构造
 @WoManTag
 @Provides
 public IPerson getManName() {
 return new Woman();
 }


 //返回Woman 带参构造
 @WoManNameTag
 @Provides
 public IPerson getWoManByName() {
 return new Woman("lili");
 }

 //返回第三方对象
 @Provides
 public static OkHttpClient getOkHttp() {
 return new OkHttpClient();
 }


}

//注入点
@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {

 @ManTag
 @Inject
 IPerson man;

 @WoManTag
 @Inject
 IPerson woMan;

 @WoManNameTag
 @Inject
 IPerson woManName;

 @Inject
 OkHttpClient okHttpClient;

}
@InstallIn

@InstallIn()括号里类类的范围

  • ApplicationComponent

  • ActivityComponent.class

  • ActivityRetainedComponent.class

  • FragmentComponent.class

  • ServiceComponent.class

  • ViewComponent.class

  • ViewWithFragmentComponent.class

如名字所见,当前module作用范围 可以是整个Application,也可以是某个view。ActivityRetainedComponent.class很特殊,对应是ViewModle。

如何限定提供的对象的使用范围,如下。

scopes

@ViewScoped

@ServiceScoped

@FragmentScoped

@ActivityScoped

@ActivityRetainedScoped

//作用范围在activity 同理切换就可以了
@ActivityScoped
@WoManTag
@Provides
public IPerson getManName() {
 return new Woman();
}

下面几张图是我截取的官网的图,可以看下作用域,和上下级绑定,生命周期等

组件的作用域注释
image.png
组件默认绑定
image.png
image.png
组默认绑定
image.png
组件生命周期
image.png
作用域
image.png
@Singleton

单例,当作用域@Module中的时候,要求@InstallIn后面跟的必须是ApplicationComponent.class。其实想想也是,单例生命周期和APP同样长。

导入Context

当需要的参数包含Contxt的时候,Hilt内部提供如下Contenxt ,作为参数传入即可。

  • @ApplicationContext

  • @ActivityContext

注意事项:如果@InstallIn 添加的是ActivityComponent.class ,

下面@Provides 里是@ApplicationContext,会报错。当前module中有@Singleton 则@InstallIn 那里肯定要是ApplicationComponent.class 。下面@Provides 写@ActivityContext 也会报错 。@InstallIn和Context 要统一级别。

给WoMan添加一个需要context的参数
public class Woman implements IPerson {


 public Woman(Context context, String name) {
 }

 @Override
 public void say() {

 }
}

//module中 这么提供就可以了 系统会自动填充一个Context
@Module
@InstallIn(ActivityComponent.class)
public class ProvideModel {

 @WoManContextTag
 @Provides
 public IPerson getWoManByContext(@ActivityContext Context context) {
 return new Woman(context, "lili");
 }

}

viewModel

依赖
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha01'
// When using Kotlin.
kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha01'
// When using Java.
annotationProcessor 'androidx.hilt:hilt-compiler:1.0.0-alpha01'
代码 @ViewModelInject和@Assisted
//使用    @ViewModelInject
public class HiltViewModel extends ViewModel {
    @ViewModelInject
    public HiltViewModel() {
    }



    public void getData(){};
}

//@Assisted
 @ViewModelInject
    public HiltViewModel(@Assisted SavedStateHandle savedStateHandle) {
        
    }


//使用
@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {

    private HiltViewModel hiltViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);

        hiltViewModel =new ViewModelProvider(this).get(HiltViewModel.class);
        hiltViewModel.getData();

    }}

WorkManger

依赖
 implementation 'androidx.hilt:hilt-work:1.0.0-alpha01'
  // When using Kotlin.
  kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha01'
  // When using Java.
  annotationProcessor 'androidx.hilt:hilt-compiler:1.0.0-alpha01'


注意

在 Worker 对象的构造函数中使用 @WorkerInject 注释来注入一个 Worker。您只能在 Worker 对象中使用 @Singleton 或未限定作用域的绑定。您还必须使用 @Assisted 为 Context 和 WorkerParameters 依赖项添加注释:



@HiltAndroidApp
public class HiltDemoApp extends Application implements Configuration.Provider {

    @Inject
    HiltWorkerFactory workerFactory;

    @NonNull
    @NotNull
    @Override
    public Configuration getWorkManagerConfiguration() {
        return new Configuration.Builder().setWorkerFactory(workerFactory).build();
    }
  
}



//感觉work 使用率还是比较低的
public  class HiltWorkManger  extends Worker {
    private static final String TAG = "HiltWorkManger";
    @WorkerInject
    public HiltWorkManger(@Assisted @NonNull @NotNull Context context, @Assisted @NonNull @NotNull WorkerParameters workerParams) {
        super(context, workerParams);
    }

    @NonNull
    @NotNull
    @Override
    public Result doWork() {
        return Result.success();
    }
}

@EntryPoint

其实这个@EntryPoint 个人不是搞的很明白,但是感觉使用概率不高

看代码和注释

// @InstallIn 
//@EntryPoint 注入点 要注意是接口 返回你要注入的对象
public class EntryModel {
    @InstallIn(ActivityComponent.class)
    @EntryPoint
    public interface IEntryPointModel {
        DemoEntry getEntry();
    }

}

//注入的对象 要注入的构造要有@Inject
public class DemoEntry {
    private String name;

    @Inject
    public DemoEntry() {
    }

    public String getName() {
        return "9999";
    }

    public void setName(String name) {
        this.name = name;
    }
}

//注入点 EntryPointAccessors 是个关键点 
//EntryPointAccessors 通过接口 调用返回的注入对象
@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {

    @Inject
    DemoEntry demoEntry;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);

        this.demoEntry = EntryPointAccessors.fromActivity(this, IEntryPoint.class).getDemoEntry();
        
    }}



image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,340评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,762评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,329评论 0 329
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,678评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,583评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,995评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,493评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,145评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,293评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,250评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,267评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,973评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,556评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,648评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,873评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,257评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,809评论 2 339

推荐阅读更多精彩内容