在Android中使用Dagger2-Part1

265 阅读4分钟

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();
    }

}

源码地址

本文参考:medium.com/@iammert/ne…

在Android中使用Dagger2-Part2

在Android中使用Dagger2-Part3