Пожалуйста, опишите ошибку

Нашли баг? Помогите нам его исправить, заполнив эту форму

Разработка приложений под Андроид. Часть 2

Марк Левковский
Android-разработчик

Это вторая статья из цикла разработки приложения Bramble для Android. В ней мы создадим экран входа в приложение и познакомимся с библиотеками Mosby и Retrofit.

Ознакомится с первой статьей, в которой был описан процесс создание и настройки проекта, вы можете здесь.

1. Mosby

Что это такое? Это библиотека, которая позволяет легко и просто применять паттерн MVP (Model-View-Presenter) при разработке приложений под android. Познакомимся с ней по ходу статьи.

Первым делом нужно подключить библиотеку, добавив в список dependencies в нашем Gradle файле еще один модуль:

compile 'com.hannesdorfmann.mosby:mvp:2.0.1'

Синхронизируем Gradle файл и можем приступать к созданию первого экрана.

Существует много способов удобно и понятно организовать структуру проекта, но лично я пришел к следующему виду: проект содержит в себе пакет screens, который, в свою очередь, содержит пакеты для каждого экрана приложения. Следовательно, в нашем главном пакете com.smedialink.bramble добавим пакет screens, который содержит пакет login.

image-4

Далее нужно создать пустое активити в пакете login:

image

Называем активити LoginActivity, оставляем отмеченным создание layout файла, отмечаем, что это будет Launcher активити, которое будет вызываться при запуске приложения, оставляем обратную совместимость (наше активити будет унаследовано от AppCompat класса, для поддержки более ранних версий андроид).

image-2

Созданное активити будет отвечать за отображение, изменение и отслеживание действий. Далее нам нужно создать класс презентера (Presenter), который будет отвечать за обработку всех действий и давать команды нашему View. Создаем сам интерфейс View для связи нашего активити с презентером. Следовательно, добавим в пакет к активити LoginActivity интерфейс LoginView, который наследуется от Mosby-класса MvpView, и класс LoginPresenter, который наследуется от MvpBasePresenter<MvpView>. Вернемся к нашему активити и также унаследуем его от MvpActivity<MvpView, MvpBasePresenter>, имплементируем наш интерфейс LoginView. В конечном итоге всё должно выглядеть следующим образом.

LoginView

public interface LoginView extends MvpView {
}

LoginPresenter

public class LoginPresenter extends MvpBasePresenter {
}

LoginActivity

public class LoginActivity extends MvpActivity<LoginView, LoginPresenter> implements LoginView {

  private ActivityLoginBinding binding;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    binding = DataBindingUtil.setContentView(this, R.layout.activity_login);
  }

  @NonNull
  @Override
  public LoginPresenter createPresenter() {
    return new LoginPresenter();
  }
}

Основу для экрана сделали. Теперь можно набросать простой интерфейс с двумя полями ввода под логин и пароль и кнопку входа.

image-1

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

LoginView

public interface LoginView extends MvpView {
  void showMessage(String message);
}

LoginPresenter

public class LoginPresenter extends MvpBasePresenter<LoginView> {
  public void onEnterClick(String loginInput, String passwordInput) {
    if (getView() != null) {
      if (loginInput.isEmpty() || passwordInput.isEmpty()) {
         getView().showMessage("Enter Login and Password");
      } else {
        getView().showMessage("Login Success");
      }
    }
  }
}

LoginActivity

public class LoginActivity extends MvpActivity<LoginView, LoginPresenter> implements LoginView {

  private ActivityLoginBinding binding;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    binding = DataBindingUtil.setContentView(this, R.layout.activity_login);
    binding.setClicker(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        String loginInput = binding.loginInput.getText().toString();
        String passwordInput = binding.passwordInput.getText().toString();
        presenter.onEnterClick(loginInput, passwordInput);
      }
    });
  }

  public void showMessage(String message){
    Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
  } 

  @NonNull
  @Override
  public LoginPresenter createPresenter() {
    return new LoginPresenter();
  }
}

2. Retrofit

В приложении Bramble планируется авторизация через Gitlab. На данном этапе мы получим свои данные, используя логин и пароль, что и будет говорить о том, что мы авторизовались. Для этого мы будем использовать библиотеку Retrofit 2. Это простая, но в то же время функциональная библиотека для работы с REST API.

Как и любую библиотеку, подключаем её через Gradle файл, добавив строчки (Сам Retrofit и Gson конвертер):

compile 'com.squareup.retrofit2:retrofit:2.0.2'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'

Далее создадим пакет network в нашем основном пакете Bramble. В него будем добавлять все классы, отвечающие за соединение с сетью. Первым и главным классом будет RestClient, который отвечает за все запросы. Его принято делать синглтоном. Так же рядом создадим интерфейс ApiClient, который будет хранить в себе список всех API запросов.

image-5

public class RestClient {

  public static final String URL = "https://gitlab.smedialink.com/api/v3/";

  private APIClient apiClient;

  private RestClient() {
    Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(URL)
      .addConverterFactory(GsonConverterFactory.create())
        .build();

    apiClient = retrofit.create(APIClient.class);

  }

  public static RestClient getInstance() {
    return InstanceHolder.INSTANCE;
  }

  public APIClient getApiClient() {
    return apiClient;
  }

  private static class InstanceHolder {
    private static RestClient INSTANCE = new RestClient();
  }
}
public interface APIClient {
}

RestClient содержит в себе URL с основным путем к API Gitlab’a (в данном случае это gitlab нашей компании) и билдер ретрофита, который инициализируется при первом запросе к классу.

Для запроса нам еще потребуется класс User, который будем получать в ответ при успехе. Он будет моделью, так что добавим еще один пакет models в пакет bramble и создадим класс с двумя полями name и username, которых хватит для начала, чтоб понять, что мы получили что ожидали.

image-6

public class User {
  private String name;
  private String username;

  public String getName() {
    return name;
  }

  public String getUsername() {
    return username;
  }
}

В интерфейсе создадим нужным нам запрос на получение юзера:

public interface APIClient {

  @FormUrlEncoded
  @POST("session")
  Call<:User> getUser(@Field("login") String login,
                        @Field("password") String password);

}

Так как в RestClient мы указали главный путь к API, то для запроса именно на получение юзера остается добавить «sessions», который отвечает в этом API именно за это. Нам нужно передать поля с нашим логином и паролем, помечаем их как Field, а раз мы используем Field, то сам запрос нужно пометить как FormUrlEncoded.

Запрос готов, осталось его использовать в нашем LoginPresenter:

public class LoginPresenter extends MvpBasePresenter<LoginView> {

  public void onEnterClick(String loginInput, String passwordInput) {
    if (getView() != null) {
      if (loginInput.isEmpty() || passwordInput.isEmpty()) {
        getView().showMessage("Enter Login and Password");
      } else {

        RestClient.getInstance().getApiClient().getUser(loginInput, passwordInput).enqueue(new Callback<User>() {
          @Override
          public void onResponse(Call<User> call, Response<User> response) {
            User user = response.body();
            if (user != null) {
  getView().showMessage(user.getName() + " " + user.getUsername());
} else {
  getView().showMessage(response.message());
}


          @Override
          public void onFailure(Call<User> call, Throwable t) {
            getView().showMessage(t.getMessage());
          }
        });
      }
    }
  }

}

Добавляем его в то место, где мы проверяли, что поля не пусты. Получаем инстанс RestClient и обращаемся к нашему запросу getUser(). В getUser передаем наши значения из полей ввода логина и пароля соответственно и делаем асинхронный запрос, который вернет нам ответ в onResponse(), либо ошибку в onFailure(), когда есть проблемы с сетью или сервер не отвечает. Нас интересует onRespose(). Если введенный логин и пароль верны, то в объекте response поле body будет не пустым, а содержать нашего юзера, иначе, body будет null, и можно вывести на экран message, которое будет содержать краткое описание ошибки.

Указываем существующие логин и пароль в соответствующие поля и нажимаем кнопку «enter». Видим, что всё работает — сообщение с нашим Name и Username.

image-3

Читать и комментировать
Как мы настраивали CI

Георгий Кирий

5 августа 2015

Как мы настраивали CI

HERE maps для Android: обзор технологии

Владислав Герасименко

10 апреля 2017

HERE maps для Android: обзор технологии

Android Wear: авторизация без пароля

Владислав Герасименко

22 ноября 2016

Android Wear: авторизация без пароля

Анимация в Android: как это работает

Илья Михайленко

27 февраля 2017

Анимация в Android: как это работает