» » » Dagger2 и архитектурный компонент «ViewModel»

 

Dagger2 и архитектурный компонент «ViewModel»

Автор: admin от 8-09-2017, 12:40, посмотрело: 555

ViewModel — это компонент из набора библиотек под названием Android Architecture Components, которые были представлены на Google I/O 2017. ViewModel — предназначена для хранения и управления данными связанных с представлением, а также с возможностью “пережить” пересоздание активити (например переворот экрана).

На хабре уже была хорошая статья посвящена ViewModel, где можно ознакомится с данной темой более детально.



В данной статье будет рассмотрены варианты инжекта(предоставление) зависимостей в компонент ViewModel с использованием Dagger 2. Проблема заключается в том, что получение ViewModel должно осуществляться специальным образом, что в свою очередь накладывает некоторые ограничения, которые связанные с предоставлением зависимостей в сам класс ViewModel, а также предоставление ViewModel в качестве зависимости. Данная статья также возможно будет интересна тем, кто интересуется практическим применением такой функциональности Dagger, как multibinding.

здесь.



@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@MapKey
@interface ViewModelKey {
    Class<? extends ViewModel> value();
}


Определим модуль, где мы будем байндить в коллекцию наши вью модели:



@Module
public abstract class ViewModelModule {

    @Binds
    @IntoMap
    @ViewModelKey(UserViewModel.class)
    abstract ViewModel userViewModel(UserViewModel userViewModel);

    @Binds
    @IntoMap
    @ViewModelKey(UserGroupViewModel.class)
    abstract ViewModel groupViewModel(UserGroupViewModel groupViewModel);
}


Перейдем к написанию нашей фабрики по предоставлению ViewModel с использованием мультибайндинга:



public class DemoViewModelFactory implements ViewModelProvider.Factory {
    private final Map<Class<? extends ViewModel>,
                           Provider<ViewModel  viewModels;

    @Inject
    public DemoViewModelFactory(Map<Class<? extends ViewModel>,
                                  Provider<ViewModel viewModels) {
        this.viewModels = viewModels;
    }

    @Override
    public <T extends ViewModel> T create(Class<T> modelClass) {
        Provider<ViewModel> viewModelProvider = viewModels.get(modelClass);

        if (viewModelProvider == null) {
            throw new IllegalArgumentException("model class " 
                                                   + modelClass
                                                   + " not found");
        }

        return (T) viewModelProvider.get();
    }
}


Provider дает нам возможность использовать отложенную инициализацию вью модели, а также получение каждый раз нового инстанса вью модели.

Без мультибайндинга у нас мог бы быть огромный блок из if/else.



Класс Application:



@Component(modules = {AppModule.class, ViewModelModule.class})
@Singleton
public interface AppComponent {

    @Component.Builder
     interface Builder {
        @BindsInstance
        Builder withApplication(Application application);

        AppComponent build();
    }

    void inject(MainActivity mainActivity);

}


Предоставление ViewModel в наше активити:



public class MainActivity extends AppCompatActivity {

    @Inject
    DemoViewModelFactory viewModelFactory;
    UserViewModel userViewModel;

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

        ((App) getApplication())
                .getAppComponent()
                .inject(this);

        userViewModel = ViewModelProviders.of(this, viewModelFactory)
                .get(UserViewModel.class);

    }
}


Преимущества данного способа:




  • Можем использовать @Inject над конструктором в ViewModel и тем самым получать зависимости, а также добавить ViewModel в граф зависимостей. Не нужно писать под каждую вью модель inject метод.

  • Одна простая фабрика для всех моделей.

  • Простота добавления новой вью модели в фабрику.



Недостатки:




  • По ошибке можно запросить модель не через фабрику(не через ViewModelProviders.of(), а с помощью @Inject MyViewModel). Не совсем удобное получение вью модели.

  • Версия Dagger с поддержкой multibinding и Provider<>.



Если бы мы запрашивали модель через @Inject, то мы бы просто получили инстанс вью модели (т.к. она уже находится в графе зависимостей) и она бы никак не была бы связана с жизненным циклом активити или фрагментом и не смогла бы “пережить” например переворот экрана.Чтобы это работало нам необходимо, чтобы создание происходило через фабрику.

Мы не можем дважды добавить в граф вью модели, т.е. мы не можем сделать следующее:



@Module
public class ActivityModule {

//будет ошибка, т.к. UserViewModel  уже в графе зависимостей
//(@Inject над конструктором)
  @Provides
   public UserViewModel productViewModel(
                                      DemoViewModelFactory viewModelFactory,
                                        AppCompatActivity activity) {

        return ViewModelProviders
                     .of(activity, viewModelFactory)
                     .get(UserViewModel .class);
    }
}


Для обхода данного ограничения можно ввести интерфейс для модели и запрашивать вью модель по интерфейсу:



@Module
public class ActivityModule {

  @Provides
   public UserViewModelInterface productViewModel(
                          DemoViewModelFactory viewModelFactory,
                          AppCompatActivity activity) {

        return ViewModelProviders
                      .of(activity, viewModelFactory)
                      .get(ProductViewModelImplementation.class);
    }
}

public class MainActivity extends AppCompatActivity {

    @Inject
    UserViewModelInterface userViewModel;

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

        ((App) getApplication())
                .getAppComponent()
                .activitySubComponentBuilder()
                .with(this)
                .build()
                .inject(this);

    }
}


На момент написания статьи использовался dagger 2.11 и архитектурные компоненты версии 1.0.0-alpha9. Как вы могли заметить архитектурные компоненты на момент написания статьи имеют альфа версию. Возможно в будущем появятся и другие методы получения вью модели.



Источник: Хабрахабр

Категория: Game Development, Android

Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

Добавление комментария

Имя:*
E-Mail:
Комментарий:
Полужирный Наклонный текст Подчеркнутый текст Зачеркнутый текст | Выравнивание по левому краю По центру Выравнивание по правому краю | Вставка смайликов Выбор цвета | Скрытый текст Вставка цитаты Преобразовать выбранный текст из транслитерации в кириллицу Вставка спойлера
Введите два слова, показанных на изображении: *