Retrofit, Realm, Dagger. Тестовое задание по Android за два часа

Retrofit 2, Realm 3, Dagger 2.

Обычно в тестовой задаче требуется получить feed. Предусмотреть локальное добавление и удаление компонентов. Кэш. Использовать библиотеки для уменьшения количества кода.

Удаление по Swipe бонус.

Кэш можно реализовать на OkHttp но каждый раз придется его парсить и локального добавления не получится. Придется использовать контент провайдер с Sqlite или ORM.

Остановимся на ORM Realm. Realm написан на native и при правильном включении как плагин не значительно увеличит apk

Realm
realm.io/news/android-installation-change

Using the plugin we can now ship Realm as an AAR, as opposed to a JAR. We can also avoid including the annotation processor in the library and make it a standalone package. This means the your final APKs will not have to include the annotation processor, shaving a few kilobytes off your app

Retrofit известная библиотека от Square. Умеет подписывать результат на Observer, но на самом деле это не критично. Асинхронных запросов достаточно для большинства задач. Полезное применение Observer с Retrofit видится если надо фильтровать или преобразовать данные перед их использовании в Presenter.

Другая полезная библиотека это Dagger. Dagger 2 использует генерацию кода в отличии от рефлексии в первой версии. Первая версия от Square. Вторая версия от Google.

Теперь о Dagger. Есть много статей, но я небуду долго перечислять и копировать все подробно. Я пошагово покажу что и где надо определить. Это статья поможет понять начинающему как организовать структуру модулей и сделать иньекцию. Обьяснение на пальцах.

Чтобы создать в Activity обекты Retrofit и Realm нужны две строчки кода.

@Inject
Retrofit retrofit;
@Inject
Realm mRealm;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
	...
	((App) getApplication()).getApplicationComponent().inject(this);

Это так называемая инъекция.

Теперь посмотрим на ApplicationComponent. Dagger генерит имена названий классов. Например DaggerApplicationComponent из ApplicationComponent, Realm из provideRealm,…

public class App extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        WEB_SERVICE_BASE_URL = "http://" + AppUtils.getNetworkHost(this);
        mApplicationComponent = DaggerApplicationComponent.builder()
                .appModule(new AppModule(this))
                .netModule(new NetModule(WEB_SERVICE_BASE_URL))
                .realmModule(new RealmModule(this))
                .build();
    }
    public ApplicationComponent getApplicationComponent() {
        return mApplicationComponent;
    }
}

Здесь мы создали наши модули. Точнее Dagger их создал за нас. Как происходит генерация?
Dager использует Анотацию

@Module
public class RealmModule {
    @Provides
    @Singleton
    Realm provideRealm(RealmConfiguration realmConfiguration) {
		...

Singleton provideRealm становится Inject Realm mRealm

Аналогичным образом происходит в модуле NetModule. Так происходит даже с параметрами в самом модуле:

Provides
Singleton
Retrofit provideRetrofit(Gson gson, OkHttpClient okHttpClient) {
provideOkhttpClient и provideGson соответственно Gson, OkHttpClient

Далее В ApplicationComponent используем Component декларацию и перечисляем модули

@Singleton
@Component(modules = {AppModule.class, NetModule.class, RealmModule.class})
public interface ApplicationComponent {
    void inject(MainActivity activity);
	}

Если понадобится использовать в другой Activity то надо добавить inject. Например для DetailActivity это выглядит так:

public interface ApplicationComponent {
    void inject(MainActivity activity);
    void inject(DetailActivity activity);
	}

Это распространяется на Fragments и Service так же.

Вернемся к Realm. Используемые объекты должны наследоваться от extends RealmObject. Их нельзя использовать напрямую для получения данных с Retrofit. Иначе ваш запрос просто зависнет.

В Activity:

mBooksViewAdapter = new BooksViewAdapter(
        mRealm.where(RealmBook.class).findAllSorted("id", Sort.ASCENDING), this);

В Adapter:

public BooksViewAdapter(RealmResults<RealmBook> books, OnStartDragListener dragStartListener) {
    this.mBooks = books;

Дальнейшая работа с объектом выглядит как с транзакциями Sqlite. Ниже пример добавления и удаления.

public void addBook(View view) {
    if (!mRealm.isInTransaction())
        mRealm.beginTransaction();
    RealmBook book = mRealm.createObject(RealmBook.class);
    book.setId("1" + Random();
    book.setTitle("Terminator");
    book.setLink("http;//rerer/rerer/trytry");
    book.setPrice(12332d);
    mRealm.commitTransaction();
    mBooksViewAdapter.notifyDataSetChanged();
}
@Override
public void onDeleted(int position, String id) {
    RealmBook realmBooks = mRealm.where(RealmBook.class).equalTo("id", id).findFirst();
    if (realmBooks != null) {
        if (!mRealm.isInTransaction())
            mRealm.beginTransaction();
        realmBooks.deleteFromRealm();
        mRealm.commitTransaction();
    }
}

Realm поддерживает события через интерфейс RealmChangeListener.

mBooks.addChangeListener(this);

Будет отрабатывать метод onChange если в выборке произошли изменения. Может быть полезно для обновления данных в Adapter.

@Override
public void onChange(Object element) {
    notifyDataSetChanged();
}

Приводится только самые базовые вещи из библиотек Dagger и Realm. Retrofit не рассмотрен в текущей статье.

Проект на github

Возможно в следующих публикациях я расскажу о Clean Architecture. Популярный подход для проектирования больших приложений.

Конструктивная критика приветствуется!
Поделиться с друзьями
-->

Комментарии (2)


  1. terrakok
    29.03.2017 20:26

    Только не путайте: Dagger 2 не от Square, а от Google


  1. app-z
    30.03.2017 02:57

    Спасибо. Исправил