Server communication

Communicating with a server can be complicated, so we will be using a library that takes a lot of the complexity away, making things easier to work with.

The library we will be using is called Retrofit. Retrofit is an open source project written by engineers at Square that is used to let an Android app talk to a server over a "RESTful API". You can learn more about Retrofit here: http://square.github.io/retrofit/, but we will be going over the basics below.

Gradle

Since Retrofit is a library, we will need to tell our Android Studio project that we have a "depedency" on it. This is done through Gradle files.

In the same way that we took a dependency on RecyclerViews earlier in this series, we will be taking a dependency on Retrofit now.

We simply need to add the following to our build.gradle file. (Make sure that you use the build.gradle that is for "Module: app").

dependencies {
...
//This tells the app that we will be using Retrofit
compile 'com.squareup.retrofit2:retrofit:2.1.0'
//This is a helper library for Retrofit for JSON conversion.
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
...
}

Reminder: This tells Android Studio to download the binaries for Retrofit from JCenter, a java code repository.

Using Retrofit

There are two main parts of a Retrofit client. The schema (or model) which tells Retrofit what format the data will be in and the client itself, which provides a way for querying the server.

The Schema

In order to use Retrofit, we need to figure out the format of our data. We do this by looking at the format of the JSON that is returned from the server. Go back to your web browser and load the server URL as before.

After formatting the JSON response properly, we see the following:

[
  {
    "_id":"57e453a469dfe113a3bc0154",
    "message":"Hello world!",
    "room":"B146",
    "user":"David",
    "__v":0
  }
]

Notice that there are three lines with message, room, and user in each block of JSON. (The rest can be ignored). These are the parts of our chat messages. Now that we know the format of our data, we can tell Retrofit how to read it.

Retrofit models

Retrofit defines its models as Java objects. Create a new Java class in the same folder as your MainActivity.java and add the following to match the format of the chat message we see in our browser.

public class Message {

    private String user;
    private String message;
    private String room;

    //constructors and methods.
}

Notice that the names of the three variables in this Java class match the names of the members of the JSON object. This is how Retrofit will know what data goes where.

In addition to the three String members, we need to add two constructors as well as getter and setter methods for each of these members. The completed code for this class can be found here.

Retrofit Service

Now that we have a model for our data, we need to tell the app how it can interact with the server. In our case there are two operations that can be done. These operations are

  1. Getting all of the messages
  2. Posting a message

In order to get all the messages, we simply GET the messages from this URL: http://hello-world-server420.cloudapp.net/messages. Our browser runs a GET on that URL when we try to view it like a typical website.

As we saw in Postman, we can also POST a new message to the server.

Note: To learn more about how the server handles these two types of interactions please check out the Hello Server tutorial here (LINK TO HELLO SERVER).

We need to tell our Retrofit client that these two operations exist. In Retrofit this is done by creating an interface.

To do this, create a new Java file in the same folder as your Message and MainActivity classes and insert the following.

public interface MessageService {
    @GET("/messages")
    Call<ArrayList<Message>> getMessages();

    @POST("/messages")
    Call<String> postMessage(
            @Body Message message
    );
}

Remember: Use [alt]+[enter] on all of the missing symbols to automatically import them.

Let's break down the different parts of the code above.

The first method in this interface:

@GET("/messages")
Call<ArrayList<Message>> getMessages();

tells Retrofit that when you GET the URL at /messages (Notice that the domain isnt listed here, that will be added later), that you will be given a List of Messages. This is consistent with what we see when we open the API in the browser.

The second part:

@POST("/messages")
Call<String> postMessage(
        @Body Message message
);

tells Retrofit that you can POST to that URL and give it a Message in the Body.

Pulling it together

Now we have two components of the Retrofit client that we are building.

  1. The model, which is the Message class. It tells Retrofit what our data looks like.
  2. The Service, which is the MessageService interface, which tells Retrofit what URLs it can interact with.

Now we need to pull these together so that we can interact with the API.

Creating the Service object

Retrofit recommends that you create only one instance of the Retrofit client that we are about to create.

Notice how above we only coded in the paths of the URL that we will be interacting with. We now need to tell Retrofit what domain to use. We also need to tell Retrofit that we will be using JSON.

This is done with the following code.

Retrofit rf = new Retrofit.Builder()
    .baseUrl("http://hello-world-server420.cloudapp.net")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

Breaking this snippet apart, the .baseUrl(String) method is setting the correct domain for the API. The .addConverterFactory(...) line is giving Retrofit a way to convert the JSON that was returned from the API, and turn it into the Message object that we created. (You do not need to know exactly how this works, just remember to always add that line for JSON APIs).

We now have variable rf containing the Retrofit object, but we need to create a MessageService. We do this by adding the following.

MessageService messageService = rf.create(MessageService.class);

We finally have a way to use the API from our Android project!

Singletons

We want a way to access the classes we have created from anywhere in our application.

The simplest way to do it is by making a Singleton. Create a class called MessageAPIClient in the same folder as your MainActivity and add the following:

public class MessageAPIClient {
  private static MessageService INSTANCE;
  public static MessageService getMessageService() {
    if(INSTANCE == null) {
      Retrofit rf = new Retrofit.Builder()
        .baseUrl("http://hello-world-server420.cloudapp.net")
        .addConverterFactory(GsonConverterFactory.create())
        .build();
      INSTANCE = rf.create(MessageService.class);      
    }
    return INSTANCE;
  }
}

Using the API Client

Now that we have a way to interact with the server, we need to load the messages from the server into the app. We want to add two things to the app.

  1. A way to post messages to the server
  2. A way to load messages from the server into the app

The fist part is easier, but to do the second part properly would be out of the scope of this tutorial. Instead of polling the server for new messages on a timer or implementing a passive messaging service (like Google Cloud Messaging), we will simply have a button that loads new messages when it is pressed.

If you want to learn more about the proper ways to live update data, check out the links below.

(INSERT LINKS TO GCM AND TIMERS)

Sending new chats to the server

Right now when someone sends a message in our app, we simply add it to the end of the RecyclerView. But now we want to instead send it to the server.

Open your MainActivity.java file and find the onClick(View) method. Remember that this is the method that is called when the user clicks the send button in the app.

This is where we will be sending data to the server.

This method should still have the code from the previous sections:

@Override
 public void onClick(View view) {
     String inputtedText = mEditText.getText().toString();

     mChatAdapter.addMessage(inputtedText);

     //Clears the EditText contents
     mEditText.getText().clear();
 }

First, we need to create a message object. We can do this with the constructor we created.

Message newMessage = new Message("David", inputtedText, "B155");

Now that we have a Message object, we can send it to the server.

MessageAPIClient.getMessageService().postMessage(newMessage)

Because we are interacting with the network it is possible that an error of some kind may occur. Either the user does not have access to the internet or the server crashes, so we need to make sure we know if the message is successfully sent to the server.

Retrofit provides a way to deal with this. It is called a Callback, and we need to have one in order to make a network call. So far we have been working with methods like onCreate(), onCreateViewHolder(), onClick(), that are called when certain events are triggered. This will work the same way.

For simplicity modify the line we added above to read:

MessageAPIClient.getMessageService().postMessage(newMessage).enqueue(this);

You should see an error on the word "this", we will use [alt]+[enter] to fix this. Do not use the default We do not want to "Cast parameter to...", we want to "Make MainActivity implement 'retrofit2.Callback'". When pressing [alt]+[enter], use your arrow keys to select that option in the list and press [enter].

Click OK on the alert that appears. You will now see that two new methods have been added to your MainActivity. These will be called when your message either succeeds or fails at posting to the server.

Edit your onResponse() method to match the following.

@Override
public void onResponse(Call<String> call, Response<String> response) {
  Toast.makeText(this, "Message sent successfully", Toast.LENGTH_SHORT).show();
}

Now when a chat is successfully sent to the server you will get a small popup on your screen to confirm.

Note: We should also handle the failure case. Usually the easiest way to do this is to make a message appear that tells the user to "Try again later". How would you improve the app to make that happen?

Congrats!

You have now sent a message to the server!

The only thing left to do is to download messages from the server periodically so that users can see when their friends chat to them.

How would you add that functionality?

results matching ""

    No results matching ""