In this series, Android Basics, I will guide you through the topics that you'll need to know in order to integrate this network into your apps.Introduction
Hey there! If you're embarking on the journey of learning all things Android, make sure that you give this a read first. It's the first article in our series on "Android Basics" here at Avenue Code Snippets.
In this article, I will make a comparison to the native connection methods suggested by Google and Retrofit, using the Star Wars API as an example of remote data.
Let's begin with a simple question: why would you want to connect your application to a remote service in the first place?
Here are some possible reasons:
- To preserve or save data;
- You have some data from outside the app such as movie catalogues, music information, or other data you don't want to load inside your app;
- To use services that are readily available on the internet when you don't want to "reinvent the wheel";
- To avoid large scale processing in the user's phone by using the server to do the hard work and process the data.
- Or, you simply want to connect to something.
Objective
It's always helpful to begin by defining your objectives. My goal in this exercise is to consume the data from the API by making a simple list of the Star Wars characters.
Approach
I will make two simple branches of the same project on GitHub, both using a MainActivity with a RecyclerView to show the character data. Later, in another post, I will go more in depth regarding RecyclerViews and Adapters.
Using Android Connection (AsyncTask)
In order to use a native Android connection, we have to use an AsyncTask, which is the method Google suggests for multi-threading inside an app. The AsyncTask consists of an interface that has two methods that have to be implemented. The first is used to do things in a background thread (doInBackground), and the second warns the main thread when the background processing is done (onPostExecute). The other methods are optional, and you can check them out in the link above.
Achieving my goal
The following steps will help you in achieving your end goal:
- Make the AsyncTask.
- Make the connection with the network or API.
- Get the data from the API.
- Parse the JSON data to a Java Object.
- Integrate it somehow with the activity containing the list.
Now, let's code!
Step One
I've decided to make the AsyncTask in a different file from the Activity. So, I've used an interface that gives me access to the data anywhere I decide to use the Task. Its structure should look like this:
public class CharacterTask extends AsyncTask<Void, Void, JsonResult> { public interface CharacterTaskCallback { void onFinishWithSuccess(JsonResult content); void onFinishWithError(); } public CharacterTask(Context context, CharacterTaskCallback callback) { ... } @Override protected void onPreExecute() { ... } @Override protected JsonResult doInBackground(Void... args) { ... } @Override protected void onPostExecute(JsonResult result) { ... } }
Steps Two, Three, and Four
When making a connection inside any app, you should make it in a different thread from the Main Thread. I used the method recommended by Google, which is to make Http connections: HttpURLConnection. Therefore, I used the doInBackground method to execute behind the scenes:
@Override protected JsonResult doInBackground(Void... args) { JsonResult result; URL url = null; HttpURLConnection connection = null; try{ url = new URL(ENDPOINT_ADDRESS); connection = (HttpURLConnection) url.openConnection(); connection.setConnectTimeout(60000); //time in miliseconds. Gson gson = new Gson(); Reader reader = new InputStreamReader(connection.getInputStream()); result = gson.fromJson(reader, JsonResult.class); } catch (Exception e){ Log.e(TAG, e.getMessage()); result = null; } return result; }
Special Comment
Here, I used GSON to parse the data directly from the server to an Object that I have mapped to have the same structure as JSON from the server. More information on GSON and how to implement serialization/deserialization can be found here. Later, you can delve even more into this process by checking the GitHub from this project.
In this sample, I have made the JsonResult model that follows the specification of the JSON on the server:
public class JsonResult { private int count; private String next; private String previous; private List<Character> results; public JsonResult(int count, String next, String previous, List<Character> results) { ... } ... Getters and Setters ... }
Step Five
As I said in Step One, by using an interface, I was able to find a way to allow the data to go from the task to anywhere within the project. Excellent!
So, in my activities that have the list, I just use the interface when executing the task:
public class MainActivity extends AppCompatActivity implements CharacterTask.CharacterTaskCallback { ... ... CharacterTask task = new CharacterTask(this, this); ... @Override public void onFinishWithSuccess(JsonResult content) { ... } @Override public void onFinishWithError() { ... } }
You can check out that particular branch of this project to see how I've made it with just the Android AsyncTask and simple HttpURLConnection.
Using Retrofit
Since Retrofit is a library, we have to load it into our project using Gradle.
In the build.gradle file, we add:
compile 'com.squareup.retrofit2:retrofit:2.3.0' compile 'com.squareup.retrofit2:converter-gson:2.3.0'
We have to tell Retrofit which serialization/deserialization method we want to use. Therefore, we have to use GSON, which is why we have that second line. On the Retrofit site, there are six common libraries for use, so feel free to adapt them to your needs. We can now use this in our project!
Achieving the Goal
Now the easy part:
- Make Retrofit usable in my activities.
- Use it!
Step One
I've decided to make a static class so I can easily access Retrofit from anywhere in the code. So, I've made the interface, which is the file that has all the methods of the API I will utilize:
public interface SWapiService { @GET("people/") Call<JsonResult> characters(); }
In this case, I only have one method right now. In the future, of course, I can have more, such as the search method or the method to return specific data of only one character.
Now, back to the static class, which I can invoke from anywhere in the code:
public class ApiClient { private static final String BASE_URL = "http://swapi.co/api/"; private static SWapiService swapiService; public static SWapiService getServices(){ if (swapiService == null){ buildServices(); } return swapiService; } private static void buildServices(){ swapiService = new Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build().create(SWapiService.class); } }
Step Two
Retrofit uses the Call method to make requests from the API. It uses Callbacks to make the communication between the API and the App.
The Call instances can be executed either synchronously or asynchronously, and the Callbacks will be executed on the main thread.
So:
public class MainActivity extends AppCompatActivity implements Callback<JsonResult>{ ... ApiClient.getServices().characters().enqueue(this); ... @Override public void onResponse(Call<JsonResult> call, Response<JsonResult> response) { ... } @Override public void onFailure(Call<JsonResult> call, Throwable t) { ... } }
In this case, I chose the asynchronous method .enqueue to make the call from the API. I could use the synchronous .execute; however, the asynchronous method was a better fit in this case. For more information on Retrofit methods, you can check out this Retrofit javadoc.
And that's how you use Retrofit.
Conclusion
In every project, we often have to use different methods when approaching the same topic. For networking in Android, I think 90% of the time Retrofit is an excellent choice for the network layer in your application, simply because it lessens the amount of hard work and saves a lot of time with communication between the app and backend.
Author
Samuel Filizzola
Samuel Filizzola is a Senior Android Engineer at Avenue Code. He is a full-time nerd who is passionate about technology and old-school games.