Сталкиваясь с разными проектами я заметил, что большинство до сих пор используют разные библиотеки, которые реализуют запросы внутри UI или вообще через AsyncTask. Это как по мне глупо, так как для реализации API в приложении есть такая чудесная библиотека, как Retrofit 2.
По данной библиотеке существует огромное количество статей, которые в полной мере описывают работу с ней. Я опишу только то, как я с ней работаю, потому что не знаю почему, но большинство туториалов используют много лишнего кода при работе с ней.
Приведу описание библиотеки из вики:
Retrofit является REST-клиентов для безопасной работы в Android и Java. Библиотекой удобно пользоваться для запроса к различным веб-сервисам с командами GET, POST, PUT, DELETE. Может работать в асинхронном режиме, что избавляет от лишнего кода.
Пы. Сы.: Проще говоря, это инструмент, который позволяет в пару строк выполнить огромное количество запросов, которые работают стабильно и в фоне.
Начнем с того что нам нужно подключить библиотеку в Build Gradle файле. Вот так выглядит мой:
app/build.gradle
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile 'com.jakewharton:butterknife:8.0.1'
apt 'com.jakewharton:butterknife-compiler:8.0.1'
compile 'com.squareup.retrofit2:retrofit:2.0.1'
compile 'com.squareup.retrofit2:converter-gson:2.0.1'
compile 'com.squareup.okhttp3:logging-interceptor:3.2.0'
compile 'com.squareup.retrofit2:converter-scalars:2.0.1'
compile 'com.android.support:recyclerview-v7:25.0.+'
}
Добавляем эти строки в наш файл, синхронизируем проект и едем дальше.
Дальше нам нужно создать интерфейс, в котором опишем наши запросы, у нас он будет один. Мы будем искать музыку из ВКонтакте с api.xn--41a.ws этого сайта. Интерфейс мы создаем для того, чтобы определить нужные нам ссылки к API и какие параметры мы будем посылать.
Также как я писал ранее, у нас есть несколько типов запросов GET, POST, PUT и DELETE. Эти запросы получают разные параметры для их работы.
@GET
— принимает параметры с @Query
, @Path
и @QueryMap.
Примеры:
@GET("group/{id}/users")
Call<List<User>> getList(@Path("id") int groupId, @Query("q") String q, @QueryMap Map<String, String> options);
@POST
, @PUT
, @DELETE
— принимает параметры типа @Part
, @Field и @Body
. Тут есть еще один нюанс. @Part
мы можем использовать только с параметром @Multipart, а @Field с параметром @FormUrlEncoded. Наоборот нельзя, потому что они работают только в паре.
Пример:
@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last, @Body someBody);
Собственно с параметрами разобрались, теперь смотрим что у нас будет в нашем интерфейсе. В нем у нас один единственный параметер @GET
, и колбек который возвращает SearchModel. SearchModel же - это класс который сгенерирован с помощью сайта www.jsonschema2pojo.org, тут просто вставляем json и там он нам сгенерирует нужные переменные. Также там мы задаем параметры, которые мы будем отправлять вместе с ссылкой.
API.java
import com.project.retrofitexample.api.model.SearchModel;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;
public interface API {
@GET("api.php?method=search")
Call<SearchModel> searchAudio(@Query("q") String query, @Query("key") String key);
}
А вот тут у нас SearchModel, который мы сгенерировали. Он нам будет возвращать список List<List> в котором нам возвращают нужные данные для отображения названия. К сожалению этот json очень хреново сделан, там параметры хранятся в StringArray, через запятую вместо параметров, но это нам не помеха:)
SearchModel.java
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import java.util.ArrayList;
import java.util.List;
public class SearchModel {
@SerializedName("list")
@Expose
private List<List<String>> list = new ArrayList<>();
public List<List<String>> getList() {
return list;
}
public void setList(List<List<String>> list) {
this.list = list;
}
}
Дальше нам нужно создать класс, который будет реализовывать работу Retofit с интерфейсом который мы создали ранее. В этом классе у нас стандартный код для настройки ретрофита. Засетапим интерйфес и создадим пару инстансов.
RestClient.java
import com.google.gson.Gson;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.converter.scalars.ScalarsConverterFactory;
public class RestClient {
public static final String BASE_URL = "http://api.xn--41a.ws/";
public static final String API_KEY = "711b23b60ff8da0c3aa2451ab3a6beb9";
private static final RestClient instance = new RestClient();
public static API instance() {
return instance.service;
}
public static Gson gson() {
return new Gson();
}
private final API service;
public RestClient() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(logLevel())
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
service = retrofit.create(API.class);
}
private static OkHttpClient logLevel() {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.readTimeout(60, TimeUnit.SECONDS)
.connectTimeout(60, TimeUnit.SECONDS)
.build();
return client;
}
}
Вот так вот выглядит наш класс.
Дальше нам нужно создать адаптер для RecycleView в котором мы отобразим наши данные с респонса, который нам вернется из нашего респонса, который мы получим с помощью Retrofit. О, как закручено получилось :)
MusicRecycleList.java
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.project.retrofitexample.R;
import java.util.ArrayList;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
public class MusicRecycleList extends RecyclerView.Adapter<MusicRecycleList.ViewHolder>{
private List<List<String>> searchModels = new ArrayList<>();
private OnItemClickListener onItemClickListener;
public MusicRecycleList(List<List<String>> searchModels) {
this.searchModels = searchModels;
}
@Override
public MusicRecycleList.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_music, parent, false);
MusicRecycleList.ViewHolder pvh = new MusicRecycleList.ViewHolder(v);
return pvh;
}
@Override
public void onBindViewHolder(final MusicRecycleList.ViewHolder holder, final int position) {
holder.title.setText(searchModels.get(position).get(4) + " - " + searchModels.get(position).get(3));
}
@Override
public int getItemCount() {
return searchModels.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.textView)
TextView title;
ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int position = MusicRecycleList.ViewHolder.super.getAdapterPosition();
onItemClickListener.onItemClick(position);
}
});
}
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
public interface OnItemClickListener {
void onItemClick(int id);
}
}
А вот так будет выглядеть item_music, который является айтемом для кастомного списка.
item_music.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="15dp"
android:text="TextView"
android:textSize="18sp" />
</LinearLayout>
Дальше нам нужно перейти в MainActivity и создать и проинициализировать наш RestClient, тоже самое надо сделать с адаптером в onResponse и тогда у нас все заработает как часы. В общем в нашем XML будет всего два элемента, это EditText и RecycleView. В первый мы будем вводить текст, во втором будем выводить список аудио.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp">
<EditText
android:id="@+id/searchView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="@string/search_text"
android:inputType="textPersonName" />
<android.support.v7.widget.RecyclerView
android:id="@+id/recycleView"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
MainActivity.java
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;
import com.project.retrofitexample.adapter.MusicRecycleList;
import com.project.retrofitexample.api.RestClient;
import com.project.retrofitexample.api.model.SearchModel;
import butterknife.BindView;
import butterknife.ButterKnife;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class MainActivity extends AppCompatActivity implements TextWatcher,
Callback<SearchModel>,MusicRecycleList.OnItemClickListener {
@BindView(R.id.searchView)
EditText searchView;
@BindView(R.id.recycleView)
RecyclerView recycleView;
MusicRecycleList musicRecycleList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
recycleViewSetup(recycleView);
searchView.addTextChangedListener(this);
}
public void recycleViewSetup(RecyclerView recyclerView) {
recyclerView.setHasFixedSize(true);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
}
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { }
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
RestClient.instance().searchAudio(charSequence.toString(), RestClient.API_KEY).enqueue(this);
}
@Override
public void afterTextChanged(Editable editable) { }
@Override
public void onResponse(Call<SearchModel> call, Response<SearchModel> response) {
musicRecycleList = new MusicRecycleList(response.body().getList());
musicRecycleList.setOnItemClickListener(this);
recycleView.setAdapter(musicRecycleList);
}
@Override
public void onFailure(Call<SearchModel> call, Throwable t) {
t.printStackTrace();
}
@Override
public void onItemClick(int id) {
}
}
Ну и добавляем в файл string.xml строку для edittext для того что бы в hint отображалось что-то, когда он пустой.
<string name="search_text">Search text here</string>
Вот собственно и все. У нас получилось приложение, которое по вводимому тексту в edittext будет искать музыку похожую на название той, что мы вводим. Вот такой экран, с результатами.
Исходники:
Комментариев нет:
Отправить комментарий