Dagger2介绍
为什么我们需要注解依赖注入?
Dependency Injection(注解依赖)是在IOC(Inversion of cnontrol)的基础上实现的,一个类的依赖需要从外部进行获取。换句话说一个类不能直接实例化另一个类,需要从配置文件中获取实例化的类的对象。
如果一个类使用new操作符来实例化另外一个类,那么这两个类直接就产生依赖关系,被称为Hard dependency。
依赖注入的优点
- 增加类的可复用性,可以独立于其他类进行测试;
Dagger2
Dagger2使用注解的方式来构建依赖图,在工程构建的过程中,使用Javax inject包生成依赖,在程序运行之前就可以就可以对代码中的错误进行检测。
注入模式
- 构造函数注入:以传递参数的方式;
- 字段(Field)注入:以变量的方式,不可以注入private类型;
- 方法注入:以传递参数的方式;
“(dependency consumer)依赖调用者”通过一个“连接器”从“(dependency provider)依赖提供者”调用“依赖对象”
- Dependency provider:使用注解
@Module表示的类,用于提供可以进行注入的对象。类中的方法使用注解@Providers表示该方法的返回对象可以被依赖注入。@Moudle可将引用的类(非自己编码的类)的注入,如果是自己编码的类仅用@Inject即可 - Dependency consumer:注解
@Inject用于定义一个依赖。 - Connection consumer and producer:使用注解
@Component的接口定义module对象中provider与依赖对象之间的连接,接口的实现类由Dagger自动生成。
Dagger2的局限性
- Dagger2不能自动注入域
- Dagger2不能注入private类型
- Dagger2如果需要注入field,需要在使用
@Component注解的接口中,定义一个提供变量的方法。
注入结构图
在Android开发中通用的注入方式形成的依赖结构如下图所示:
当AppComponent通过其modules构建完成后,我们就在结构图中有了所有我们需要的实例了。如果我们需要将Activity连接到Dagger的依赖结构图中,构建一个使用@Subcomponent注解的Component即可。
在使用上述方法进行构建时,我们需要向不断向AppModule中添加subComponent,而且子component中相似的代码较多,Dagger2中提供@ContributesAndroidInjector注解来解决这一问题,依赖结构图如下图所示:
代码实现
我们采用第二种结构进行代码的实现
最简方式,向Fragment和Activity中注入UserDao对数据库进行操作。
项目结构
数据库实现
//User
@Entity
public class User {
@PrimaryKey(autoGenerate = true)
@ColumnInfo
private int id;
@ColumnInfo
private String name="";
//省略Getter和Setter
}
//UserDao
@Dao
public abstract class UserDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
public abstract void insert(User user);
@Query("select * from user")
public abstract List<User> getUsers();
}
//UserDb
@Database(entities = {User.class}, version = 1,exportSchema = false)
public abstract class UserDb extends RoomDatabase {
public abstract UserDao userDao();
}
测试用的Bean类
public class Bean {
public Bean(){
}
public void printInfo(){
Log.i("++",this.toString());
}
}
AppMoudle实现
AppMoudle中用于提供一些全局的单例,如:Retrofit、OkHttp、Room等。
@Module
public class AppModule {
@Provides
@Singleton
public Context provideContext(Application application) {
return application;
}
//用于注入DB单例
//这里只能用Application 而非AppApplication,用user_db而非user.db
@Singleton
@Provides
public UserDb provideDb(Application application) {
return Room.databaseBuilder(application, UserDb.class, "user.db").build();
}
//用于注入UserDao
@Singleton
@Provides
public UserDao provideUserDao(UserDb userDb){
return userDb.userDao();
}
}
MainActivityModule实现
在MainActivityModule提供一些需要在MainActivity中进行注入的实例。
@Module
public class MainActivityModule {
@Provides
Bean provide(){
return new Bean();
}
}
FragmentBuildersModule实现
@Module
public abstract class FragmentBuildersModule {
@ContributesAndroidInjector
abstract MainFragment contributeDetailFragment();
}
AppComponent实现
AppComponent使用注解@Component,程序在编译的工程中通过AppComponent形成依赖注入关系图,Component使用modules提供注入的实例。
@Component.Builder在构建的过程中,有时需要向关系图中绑定一些实例,在本项目中绑定的是Application。
注:如果需要在Component中提供Builder,Builder接口中需要添加一个builder()方法,该方法返回Component
@Singleton
@Component(modules = {AndroidInjectionModule.class, AppModule.class, ActivityBuildersModule.class})
public interface AppComponent {
@Component.Builder
interface Builder {
@BindsInstance
Builder application(Application application);
AppComponent build();
}
void inject(AppApplication application);
}
AppApplication实现
.application(this)方法将application实例绑定职Dagger的依赖图标中。Application需要实现HasActivityInjector接口,有Activity需要注入。如果Activity中有Fragment,则Activity中需要实现HasSupportFragmentInjector接口,
public class AppApplication extends Application implements HasActivityInjector {
@Inject
DispatchingAndroidInjector<Activity> activityDispatchingAndroidInjector;
@Override
public void onCreate() {
super.onCreate();
DaggerAppComponent
.builder()
.application(this)
.build()
.inject(this);
}
@Override
public DispatchingAndroidInjector<Activity> activityInjector() {
return activityDispatchingAndroidInjector;
}
}
MainActivity中实现
在Activity中以及Fragment中都需要将依赖进行注入, AndroidInjection.inject(this); AndroidSupportInjection.inject(this);
public class MainActivity extends AppCompatActivity implements HasSupportFragmentInjector {
@Inject
DispatchingAndroidInjector<Fragment> dispatchingAndroidInjector;
@Inject
MainFragment mainFragment;
@Inject
Context context;
@Inject
Bean bean;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidInjection.inject(this);
setContentView(R.layout.activity_main);
getSupportFragmentManager().beginTransaction().replace(R.id.container, mainFragment).commitAllowingStateLoss();
}
@Override
public AndroidInjector<Fragment> supportFragmentInjector() {
bean.printInfo();
return dispatchingAndroidInjector;
}
}
MainFragment实现
public class MainFragment extends Fragment {
@Inject
UserDao userDao;
//用于获取DetailFragment实例
FragmentDetailBinding fragmentDetailBinding;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
AndroidSupportInjection.inject(this);
fragmentDetailBinding = DataBindingUtil
.inflate(inflater, R.layout.fragment_detail, container, false);
fragmentDetailBinding.insertDateTv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
User user = new User("++++");
userDao.insert(user);
Log.i("User插入", "+++");
}
}).start();
}
});
fragmentDetailBinding.readDataTv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
Log.i("User数量:", userDao.getUsers().size() + "");
}
}).start();
}
});
return fragmentDetailBinding.getRoot();
}
}
使用DaggerActivity, DaggerFragment, DaggerApplication
在上述代码中,在进行注入时每一个Activity中都需要实现HasFragmentInjector或者HasSupportFragmentInjector接口,再进行注入,在Dagger 2 中对接口的实现和注入进行封装
修改AppComponent, 使得AppComponent继承AndroidInjector<DaggerApplication>,将AndroidInjectionModule修改为AndroidSupportInjectionModule
@Singleton
@Component(modules = {AndroidSupportInjectionModule.class, AppModule.class, ActivityBuildersModule.class})
public interface AppComponent extends AndroidInjector<DaggerApplication> {
@Component.Builder
interface Builder {
@BindsInstance
Builder application(Application application);
AppComponent build();
}
void inject(AppApplication app);
@Override
void inject(DaggerApplication instance);
}
修改AppApplcation
public class AppApplication extends DaggerApplication {
@Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
AppComponent appComponent=DaggerAppComponent.builder().application(this).build();
appComponent.inject(this);
return appComponent;
}
}
修改MainActivity
public class MainActivity extends DaggerAppCompatActivity {
@Inject
DispatchingAndroidInjector<Fragment> dispatchingAndroidInjector;
@Inject
MainFragment mainFragment;
@Inject
Context context;
@Inject
Bean bean;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidInjection.inject(this);
setContentView(R.layout.activity_main);
getSupportFragmentManager().beginTransaction().replace(R.id.container, mainFragment).commitAllowingStateLoss();
bean.printInfo();
}
}
注入带构造函数的ViewModel
在初始化一个带参数的ViewModel,需要使用ViewModelProvider.Factory的实例进行初始化,如果将一个ViewModelFactory用在不同的ViewModel上,ViewModelFactory需要对各种不同的参数进行处理。
在本项目中使用线程池对插入和读取数据的线程进行管理;使用UserRepo对User的插入和读取进行管理。
在Dagger2中有 Multibindings为对象创建一个Map:
- Key:表示ViewModel的类型,在本项目中表示为
MainFragmentViewModel::class - Vaule:表示ViewModel的实例
Dagger2在编译的时候会创建Map,我们将该Map以参数的形式传入到ViewModelFactory中,当我们调用create()方法时,将会从map中取出正确的实例。
AppExecutors创建
使用Javax包中@Singleton注解,表示其为单例模式;对于自定义类直接在构造函数AppExecutors()上添加注解@Inject,可以直接进行注入,无需在Module中提供providers
@Singleton
public class AppExecutors {
private final Executor diskIO;
public AppExecutors(Executor diskIO){
this.diskIO=diskIO;
}
@Inject
public AppExecutors(){
this(Executors.newSingleThreadExecutor());
}
public Executor getDiskIO() {
return diskIO;
}
}
UserRepo 创建
直接在构造函数添加@Inject进行注入
@Singleton
public class UserRepo {
private final UserDb userDb;
private final UserDao userDao;
private final AppExecutors appExecutors;
@Inject
public UserRepo(UserDb userDb,UserDao userDao,AppExecutors appExecutors){
this.userDb=userDb;
this.userDao=userDao;
this.appExecutors=appExecutors;
}
public void insertUser(final User user){
appExecutors.getDiskIO().execute(new Runnable() {
@Override
public void run() {
userDao.insert(user);
}
});
}
public void readUser(){
appExecutors.getDiskIO().execute(new Runnable() {
@Override
public void run() {
int m =userDao.getUsers().size();
Log.i("+++","++++"+m);
}
});
}
}
ViewModelFactory创建
@Singleton
public class ViewModelFactory implements ViewModelProvider.Factory {
private final Map<Class<? extends ViewModel>, Provider<ViewModel>> creators;
@Inject
public ViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> creators) {
this.creators = creators;
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
Provider<? extends ViewModel> creator = creators.get(modelClass);
if (creator == null) {
for (Map.Entry<Class<? extends ViewModel>, Provider<ViewModel>> entry : creators.entrySet()) {
if (modelClass.isAssignableFrom(entry.getKey())) {
creator = entry.getValue();
break;
}
}
}
if (creator == null) {
throw new IllegalArgumentException("未知类" + modelClass);
}
try {
return (T)creator.get();
}catch (Exception e){
throw new RuntimeException(e);
}
// return null;
}
}
ViewModelModule创建
创建ViewModelModule并使用@Binds注解提供注入
@Module
public abstract class ViewModelModule {
@Binds
abstract ViewModelProvider.Factory bindViewModelFactory(ViewModelFactory factory);
}
ViewModelKey创建
创建需要进行多重绑定的Map,并绑定ViewModel
@Target({ ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@MapKey
public @interface ViewModelKey {
Class<? extends ViewModel> value();
}
在ViewModelModule中添加需要注入的ViewModel
通过@Binds @IntoMap @ViewModelKey 注解,提供给前文创建的Map数据,Key为 MainFragmentViewModel.class value为MainFragmentViewModel 实例
@Module
public abstract class ViewModelModule {
@Binds
@IntoMap
@ViewModelKey(MainFragmentViewModel.class)
abstract ViewModel bindViewModel(MainFragmentViewModel detailViewModel);
}
在AppMoudle中添加ViewModelModule
@Module(includes = {DbModule.class,ViewModelModule.class})
public class AppModule {
}
修改MainFragment
public class MainFragment extends DaggerFragment
@Inject
ViewModelProvider.Factory viewModelFactory;
private MainFragmentViewModel mainFragmentViewModel;
//用于获取DetailFragment实例
FragmentDetailBinding fragmentDetailBinding;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
AndroidSupportInjection.inject(this);
fragmentDetailBinding = DataBindingUtil
.inflate(inflater, R.layout.fragment_detail, container, false);
fragmentDetailBinding.insertDateTv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mainFragmentViewModel.insertUser();
}
});
fragmentDetailBinding.readDataTv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mainFragmentViewModel.readUser();
}
});
return fragmentDetailBinding.getRoot();
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mainFragmentViewModel = ViewModelProviders.of(this, viewModelFactory).get(MainFragmentViewModel.class);
}
}