📜 ⬆️ ⬇️

Studying Retrofit 2



In the world of Android development there are many interesting libraries, and today we look at the brainchild of the company Square - Retrofit . What kind of animal is this? Retrofit (according to the official site) is a type-safe HTTP client for Android and Java. It is an indispensable tool for working with APIs in client-server applications. Some years ago, Android developers had to turn up mountains of code with callbacks, AsyncTasks and other "low-level" things to work with the network. And Square has released such a wonderful library - Retrofit.

I could not find intelligible tutorials on the second version of the library on the Internet, so today we will deal with it using the example of an application that receives posts from bash.im

Better to see once than hear a hundred times


We will create an application that receives data from the Umorili site API , as soon as they provide data from the bash in a form suitable for parsing. This is what the final version will look like:
')
  Design, of course, does not shine 
Well, children, are you ready?

Dependencies


Library Retrofit can be connected in three ways: Gradle, Maven, Jar. We describe each method.

Gradle


In most cases, this tool is used to build Android applications, so if you are not sure, take this option :) (hereinafter, Gradle will be used for dependencies).

To connect the application module to the build.gradle file, insert the line in the dependencies section:

 compile 'com.squareup.retrofit2:retrofit:2.1.0' 

Maven


If someone uses this dependency system and build, then the dependency fragment will look like this:

 <dependency> <groupId>com.squareup.retrofit2</groupId> <artifactId>retrofit</artifactId> <version>2.1.0</version> </dependency> 

Jar


I do not welcome the use of this option, but some love it. Download from the official site jar-file ( link ) and throw it into the libs folder.

In addition to the library itself, we will need a JSON parser and RecyclerView-v7, so we connect them:

 compile 'com.squareup.retrofit2:converter-gson:2.1.0' // JSON, ,  ,  Jackson compile 'com.android.support:recyclerview-v7:25.0.0' //RecyclerView 

With the dependencies figured out, now move on to the sweetest part - development. First of all we need to describe requests to the API, therefore.

Description of API requests


Retrofit allows you to make a full-fledged REST client that can perform POST, GET, PUT, DELETE. Annotations are used to indicate the type and other aspects of the request. For example, in order to indicate that a GET request is required, we need to write before the GET method, for POST request POST , and so on. In brackets to the type of request is the target address. For example, take the GitHub API. The full URL to get the list of repositories of a specific user can be represented as https://api.github.com/users/octocat/repos , where:


There are also request parameters, for example, in the request to Umorili we will use the following address - https://umorili.herokuapp.com/api/get?name=bash&num=50 , where name=bash&num=50 are parameters.

But the description does not end with annotations alone, but we need to describe them somewhere. And we describe them in the interface. For our application, the interface will be as follows:

 package ru.mustakimov.retrofittutorial.api; import java.util.List; import retrofit2.Call; import retrofit2.http.GET; import retrofit2.http.Query; import ru.mustakimov.retrofittutorial.PostModel; public interface UmoriliApi { @GET("/api/get") Call<List<PostModel>> getData(@Query("name") String resourceName, @Query("num") int count); } 

Let's sort this interface. We have a getData method that returns an object of type Call<List<PostModel>> . Methods should always return an object of type Call<T> and have an annotation of the type of request (GET, POST, PUT, DELETE).

Annotation @Query("name") String resourceName shows the Retrofit that you need to set the name = <String resourceName> value as the query parameter.

If we have an alias in the target address, then in order to supply a value instead of an alias, we need to write @Path("< >") SomeType variable , where SomeType is any type (for example, String, int, float).

PostModel is a class generated by jsonschema2pojo based on server response.

Here is the class itself
 package ru.mustakimov.retrofittutorial; import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; public class PostModel { @SerializedName("site") @Expose private String site; @SerializedName("name") @Expose private String name; @SerializedName("desc") @Expose private String desc; @SerializedName("link") @Expose private String link; @SerializedName("elementPureHtml") @Expose private String elementPureHtml; /** * @return The site */ public String getSite() { return site; } /** * @param site The site */ public void setSite(String site) { this.site = site; } /** * @return Site name */ public String getName() { return name; } /** * @param name Site name */ public void setName(String name) { this.name = name; } /** * @return Site description */ public String getDesc() { return desc; } /** * @param desc Site description */ public void setDesc(String desc) { this.desc = desc; } /** * @return The link */ public String getLink() { return link; } /** * @param link The link */ public void setLink(String link) { this.link = link; } /** * @return The elementPureHtml */ public String getElementPureHtml() { return elementPureHtml; } /** * @param elementPureHtml The elementPureHtml */ public void setElementPureHtml(String elementPureHtml) { this.elementPureHtml = elementPureHtml; } } 


Preparing for the request


Before sending the request and getting the result, we need to initialize the Retrofit and the interface object. So that the application does not have a hundred objects that perform the same function, we will perform all initialization in the class inherited from Application. The code will then be as follows:

 package ru.mustakimov.retrofittutorial; import android.app.Application; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; import ru.mustakimov.retrofittutorial.api.UmoriliApi; public class App extends Application { private static UmoriliApi umoriliApi; private Retrofit retrofit; @Override public void onCreate() { super.onCreate(); retrofit = new Retrofit.Builder() .baseUrl("https://umorili.herokuapp.com") //   .addConverterFactory(GsonConverterFactory.create()) //,    JSON'   .build(); umoriliApi = retrofit.create(UmoriliApi.class); // ,       } public static UmoriliApi getApi() { return umoriliApi; } } 
PS do not forget to register in the manifest that we use our Application class

Now from any class we have access to the API.

Data acquisition


We can execute requests (and, therefore, receive data) in two ways - synchronous and asynchronous requests. For synchronous (blocking) receipt, we use the execute() method on an object of type Call. For our example, the code would be as follows:

 Response response = App.getApi().getData("bash", 50).execute(); 

As a result of execution, we get an object of type Response (response), from where we can already get the parsed response using the body() method.

For asynchronous retrieval, we replace execute() with enqueue() , where in the parameters we pass callback functions (callbacks). In our example, it will look something like this:

 App.getApi().getData("bash", 50).enqueue(new Callback<List<PostModel>>() { @Override public void onResponse(Call<List<PostModel>> call, Response<List<PostModel>> response) { //  ,    response.body()  null } @Override public void onFailure(Call<List<PostModel>> call, Throwable t) { //  } }); 

We do data mapping


We have already received the data, and how and now to display? We throw in a marking of activity RecyclerView and somehow we call it. After that we create markup for the element.

That's what happened with me
activity_main.xml
 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="ru.mustakimov.retrofittutorial.MainActivity"> <android.support.v7.widget.RecyclerView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" android:id="@+id/posts_recycle_view" android:layout_alignParentStart="true" /> </RelativeLayout> 

post_item.xml
 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:padding="5dp" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/postitem_post" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="    ,     ,     " android:textColor="?android:attr/textColorPrimary" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" /> <TextView android:id="@+id/postitem_site" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Bash.im" android:layout_below="@+id/postitem_post" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" android:gravity="end" android:textAlignment="textEnd" /> </RelativeLayout> 


After we create an adapter for RecyclerView:

Adapter code
 package ru.mustakimov.retrofittutorial; import android.os.Build; import android.support.v7.widget.RecyclerView; import android.text.Html; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import java.util.List; public class PostsAdapter extends RecyclerView.Adapter<PostsAdapter.ViewHolder> { private List<PostModel> posts; public PostsAdapter(List<PostModel> posts) { this.posts = posts; } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.post_item, parent, false); return new ViewHolder(v); } @Override public void onBindViewHolder(ViewHolder holder, int position) { PostModel post = posts.get(position); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { holder.post.setText(Html.fromHtml(post.getElementPureHtml(), Html.FROM_HTML_MODE_LEGACY)); } else { holder.post.setText(Html.fromHtml(post.getElementPureHtml())); } holder.site.setText(post.getSite()); } @Override public int getItemCount() { if (posts == null) return 0; return posts.size(); } class ViewHolder extends RecyclerView.ViewHolder { TextView post; TextView site; public ViewHolder(View itemView) { super(itemView); post = (TextView) itemView.findViewById(R.id.postitem_post); site = (TextView) itemView.findViewById(R.id.postitem_site); } } } 


And we set up in the MainActivity.java the initialization of the RecyclerView, the adapter, as well as the data acquisition.

And here is the MainActivity
 package ru.mustakimov.retrofittutorial; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.widget.Toast; import java.io.IOException; import java.util.ArrayList; import java.util.List; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; public class MainActivity extends AppCompatActivity { RecyclerView recyclerView; List<PostModel> posts; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); posts = new ArrayList<>(); recyclerView = (RecyclerView) findViewById(R.id.posts_recycle_view); LinearLayoutManager layoutManager = new LinearLayoutManager(this); recyclerView.setLayoutManager(layoutManager); PostsAdapter adapter = new PostsAdapter(posts); recyclerView.setAdapter(adapter); try { Response response = App.getApi().getData("bash", 50).execute(); } catch (IOException e) { e.printStackTrace(); } App.getApi().getData("bash", 50).enqueue(new Callback<List<PostModel>>() { @Override public void onResponse(Call<List<PostModel>> call, Response<List<PostModel>> response) { posts.addAll(response.body()); recyclerView.getAdapter().notifyDataSetChanged(); } @Override public void onFailure(Call<List<PostModel>> call, Throwable t) { Toast.makeText(MainActivity.this, "An error occurred during networking", Toast.LENGTH_SHORT).show(); } }); } } 


On GitHub, you can find the full code for this application.

Source: https://habr.com/ru/post/314028/


All Articles