Development/Servers

Android, Tomcat REST

석찬 2014. 2. 20. 18:20
반응형

원글의 출처 : http://avilyne.com/?p=105


An Android REST Client and Tomcat REST Webservice

Project Overview

This post will describe simple client-server communications between an Android app and a web service using REST techniques, particularly GET and POST. For this demo, we will be sending and receiving data for a simple Java class called Person, which includes a firstName, lastName and email address. The data stored in an instance of this class will be transmitted from our web service in JSON format.

NOTE: This post is a work in progress and will be updated and corrected, as time permits.

Prerequisites

This demo assumes that you have a version of Eclipse that can create a “Dynamic Web Project”, and that you have an instance of the Tomcat servlet container that you can control through Eclipse. This demo also assumes that you have the Android SDK, at least one Android Virtual Device (AVD), and have the Eclipse ADT plugin.

Another important requirement is that your Android device needs to be on the same network as your Tomcat server. The simplest way to do this would be to make sure you are running your emulator on the same computer that is running Tomcat.

About REST

REST – “Representational State Transfer” is a technique that makes use of standard web protocols for the implementation of a web service. A RESTful web service uses the standard HTTP GET PUT DELETE and POST actions to submit, retrieve or modify server-side data.

Commonly, in a REST web service, the standard HTTP actions are used as follows:

  • GET – retrieves or queries for data, usually using passed criteria
  • PUT – creates a new entry, record
  • DELETE – removes a resource
  • POST- updates a resource or creates a resource

The data that is transmitted uses standard MIME types, including images, video, text, html, XML and JSON.

A key feature of a REST service is its use of URI paths as query parameters. For example, a fictional web service for a book library might return a list of Civil War history books with this URI:

http://librarywebservice.com/books/history/CivilWar

Or, for magazines about tennis:

http://librarywebservice.com/magazines/sports/tennis

These are the absolute basics. REST has become a very popular replacement to SOAP for the development of web services.

Download the Jersey Jars

For the web server, this demo will use Tomcat, and will make use of a reference implementation of the JSR 311 Java library, otherwise known as “jersey”. The Jersey API can significantly streamline the development of a RESTful web service, and much of its ease comes from the use of annotations.

The home page, and the place to download the required jars for jersey can be found here. As of the writing of this post, the jars will be found in a zip called jersey-archive-1.12.zip . Please download this zip and expand it to a folder where you can find those jars.

Create a “Dynamic Web Project” in Eclipse

In Eclipse, select “File”->”New…”->”Project” and use the filter to find “Dynamic Web Project”
Eclipse Dynamic Web Project

I have Apache Tomcat 7 running on my laptop, and have previously configured a connection between Tomcat and Eclipse. The Dynamic web module version I am using is 3.0, but this tutorial can work with version 2.5.
Apache Tomcat and web module version

Click “Next” and define your context root. That is the starting-point URL for your web service. With the context root shown in the screenshot below, the resulting URL on my laptop will be http://localhost:8080/RestWebServiceDemo . Also, have Eclipse generate a web.xml deployment descriptor.
Context root and web.xml

Add Jersey Jars to Project

You will need to import the jars that you’ve downloaded from http://jersey.java.net into the WEB-INF/lib folder. Right click that folder (WebContent/WEB-INF/lib) and select Import…
Import jersey jars files

I’ve simply thrown in all the jars into that folder for the purposes of this tutorial. Crude, but effective. The smart thing would be to only use the jars you need. I’ll eventually circle back to that some day.
Jersey jar file list

Update Project Build Path to Include Jersey Jars

Now that the jars are in the WEB-INF/lib folder, you will need to configure the project to include these jars in its build path. From within your project in the Package Explorer on the left of your Eclipse screen, right click to select “Build Path”->”Configure Build Path…”
Configure build path

On the “Libraries” tab, click on “Add External JARS…” and select the jars that are now in your project’s WEB-INF/lib path.
Add external jars

Create the POJO Person class

Within the \src folder, create a package called com.avilyne.rest.model, and in that package, create a Java class called Person. Note, in the code shown below, the @XmlRootElementannotation. This tells Jersey that this would be the root object of any generated XML (or JSON) representation of this class. This might not be very useful for this class, but if you had a compound class, you’d be able to control what the XML or JSON output would look like by the addition of annotations like this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package com.avilyne.rest.model;
 
import javax.xml.bind.annotation.XmlRootElement;
 
@XmlRootElement
public class Person {
 
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
     
    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }
     
    public Person() {
         
        id = -1;
        firstName = "";
        lastName = "";
        email = "";
         
    }
 
    public Person(long id, String firstName, String lastName, String email) {
 
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
    }
 
     
    private long id;
    private String firstName;
    private String lastName;
    private String email;
         
}

Create the PersonResource class

Again, in the \src folder, create a new package called com.avilyne.rest.resource, and in that package create a class called PersonResource. (We are separating our data model from our controller).

The PersonResource class will be the interface between a Person object and the web. When a client requests a Person object, the PersonResource will handle the request and return the appropriate object.

Note, in the code below, the annotations. The @Path(“/person”) annotation allows us to define the URI that will have access to this resource. It will be something along the lines of http://RestServerDemo/rest/person, but this will be explained in more detail as we map this code in our web.xml file.

The @Context annotation allows us to inject, in this example, UriInfo and Request objects into our PersonResource object. Our code can inspect those injected objects for any desired Context information.

1
2
3
4
5
6
7
8
9
10
11
// The @Context annotation allows us to have certain contextual objects
// injected into this class.
// UriInfo object allows us to get URI information (no kidding).
@Context
UriInfo uriInfo;
 
// Another "injected" object. This allows us to use the information that's
// part of any incoming request.
// We could, for example, get header information, or the requestor's address.
@Context
Request request;

The @GET annotation lets us specify what method will be called when a client issues a GET to the webservice. Similar logic applies to the @POST annotation. Note that the actual method name will not be a part of the client’s URI.

Also note that more than one method has a @GET annotation. The first @GET is simply there so that, when we start our service, we can use a browser to retrieve some sort of response that indicates the service is running. Technically, instead of producing TEXT_PLAIN, the first GET could have produced a TEXT_HTML page.

1
2
3
4
5
6
// Basic "is the service running" test
@GET
@Produces(MediaType.TEXT_PLAIN)
public String respondAsReady() {
    return "Demo service is ready!";
}

The @Path annotation lets us append a parameter onto our URI. Using the example from earlier, the hypothetical URI would be http://RestServerDemo/rest/person/sample .

1
2
3
4
5
6
7
8
9
@GET
@Path("sample")
@Produces(MediaType.APPLICATION_JSON)
public Person getSamplePerson() {
     
    System.out.println("Returning sample person: " + person.getFirstName() + " " + person.getLastName());
     
    return person;
}

Note that paths do NOT need to be literal. One can have a path parameter as a variable, e.g.@Path(“{id}”) would refer to a person whose id value matched the passed URI value. http://RestServerDemo/rest/person/1 should return a person whose id is 1.

The @Produces annotation allows us to define how the output from our resource should be transmitted to our client. Note that, for the getSamplePerson() method, we return a person object, and the annotation lets us tell Jersey to format and transmit that person as a JSON object.

The method with the @POST annotation also includes a @Consumes annotation. As you can guess, this method is called in response to a client’s POST request. The data for the “person” object being transmitted from the client is not a JSON object, but is a collection of Name-Value pairs. The @Consumes annotation allows us to specify that the data passed from the client is an array of these pairs, and we can pull out the values we want from that array.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Use data from the client source to create a new Person object, returned in JSON format.
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.APPLICATION_JSON)
public Person postPerson(
        MultivaluedMap<String, String> personParams
        ) {
     
    String firstName = personParams.getFirst(FIRST_NAME);
    String lastName = personParams.getFirst(LAST_NAME);
    String email = personParams.getFirst(EMAIL);
     
    System.out.println("Storing posted " + firstName + " " + lastName + "  " + email);
     
    person.setFirstName(firstName);
    person.setLastName(lastName);
    person.setEmail(email);
     
    System.out.println("person info: " + person.getFirstName() + " " + person.getLastName() + " " + person.getEmail());
     
    return person;
                     
}

For each of the methods in this PersonResource, I’ve added a System.out.println() as a crude way of letting us see when a request is being processed. There are probably more elegant ways of doing this (Logging, for one), and one would almost never include a System.out.println in a production service. This is just a demo.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package com.avilyne.rest.resource;
 
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.core.Request;
 
import com.avilyne.rest.model.Person;
 
@Path("/person")
public class PersonResource {
 
    private final static String FIRST_NAME = "firstName";
    private final static String LAST_NAME = "lastName";
    private final static String EMAIL = "email";
         
    private Person person = new Person(1, "Sample", "Person", "sample_person@jerseyrest.com");
     
    // The @Context annotation allows us to have certain contextual objects
    // injected into this class.
    // UriInfo object allows us to get URI information (no kidding).
    @Context
    UriInfo uriInfo;
 
    // Another "injected" object. This allows us to use the information that's
    // part of any incoming request.
    // We could, for example, get header information, or the requestor's address.
    @Context
    Request request;
     
    // Basic "is the service running" test
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String respondAsReady() {
        return "Demo service is ready!";
    }
 
    @GET
    @Path("sample")
    @Produces(MediaType.APPLICATION_JSON)
    public Person getSamplePerson() {
         
        System.out.println("Returning sample person: " + person.getFirstName() + " " + person.getLastName());
         
        return person;
    }
         
    // Use data from the client source to create a new Person object, returned in JSON format. 
    @POST
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    @Produces(MediaType.APPLICATION_JSON)
    public Person postPerson(
            MultivaluedMap<String, String> personParams
            ) {
         
        String firstName = personParams.getFirst(FIRST_NAME);
        String lastName = personParams.getFirst(LAST_NAME);
        String email = personParams.getFirst(EMAIL);
         
        System.out.println("Storing posted " + firstName + " " + lastName + "  " + email);
         
        person.setFirstName(firstName);
        person.setLastName(lastName);
        person.setEmail(email);
         
        System.out.println("person info: " + person.getFirstName() + " " + person.getLastName() + " " + person.getEmail());
         
        return person;
                         
    }
}

Create or Edit the WebContent\WEB-INF\web.xml file

In our web.xml file, we want to direct all requests to a servlet called Jersey REST Service. We will also tell this service where to find the resources that we want to make available to our client app.

Update the web.xml file (or create it, in WebContent\WEB-INF\web.xml, if it does not exist) to match the XML shown below.

The XML defines the Jersey REST Service from the com.sun.jersey.spi.container.ServletContainer class. That class, as you might guess, is in one of the Jersey Jars. The init-param section allows us to tell that servletcontainer to use the classes found in the com.avilyne.rest.resource package for the mapping of URIs to java code.

In the servlet-mapping section, we create a global url-pattern, which essentially says that any request that goes to /rest/ will attempt to be mapped to the appropriate methods.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8"?>
<web-app
    id="WebApp_ID"
    version="3.0">
  <display-name>JerseyRESTServer</display-name>
  <servlet>
    <servlet-name>Jersey REST Service</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>com.sun.jersey.config.property.packages</param-name>
        <param-value>com.avilyne.rest.resource</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Jersey REST Service</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
</web-app>

Run the WebService

Run the completed project as a service on your Tomcat servlet container. The actual URL for the service will vary slightly, depending partly on the name of your project. I called my project JerseyRESTServer, and here is the URL for the web service on my computer:

http://localhost:8080/JerseyRESTServer/rest/person

Correction! Paul, a recent commenter, has pointed out that the URL should actually be:

http://localhost:8080/RestWebServiceDemo/rest/person

RestWebServiceDemo is the context root that we defined when we first started this project. I’ll mention that I built a few versions of this project before writing this post, so unfortunately some of the images show a URL from an earlier version of the project.

If I bring up this on a browser, it returns with this:
Service is Ready

If I add the “sample” path to the URL, like this:

http://localhost:8080/RestWebServiceDemo/rest/person/sample

The web service returns a JSON object:

1
{"email":"sample_person@jerseyrest.com","firstName":"Sample","id":"1","lastName":"Person"}

I would recommend that you make sure you can retrieve this sample person before you create the Android client app.

Create an Android Client App

Create a new Android project, calling it something like AndroidRESTClient. I used API level 10 for this project, but one can probably use a lower level API, if required. Android HttpClient requests have been around since the earliest days of the OS. Use com.avilyne.android as the package for the main activity.

Edit the \res\layout\main.xml File

The interface will be very simple, having three labels and three edit controls for firstName, lastName and email address. It will also have three buttons – one to GET, one to POST, and one to clear the controls.

The main.xml file should look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/tableLayout1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:shrinkColumns="*"
    android:stretchColumns="*" >
 
    <TableRow
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
 
        <TextView
            style=""
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/firstName" >
        </TextView>
 
        <EditText
            android:id="@+id/first_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:inputType="textCapWords"
            android:layout_span="2" />
    </TableRow>
 
    <TableRow
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
 
        <TextView
            style=""
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/lastName" >
        </TextView>
 
        <EditText
            android:id="@+id/last_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:inputType="textCapWords"
            android:layout_span="2" />
    </TableRow>
 
    <TableRow
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
 
        <TextView
            style=""
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/email" >
        </TextView>
 
        <EditText
            android:id="@+id/email"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:inputType="textEmailAddress"
            android:layout_span="2" />
    </TableRow>
 
    <TableRow
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
 
        <Button
            android:id="@+id/bn_retrieve"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="retrieveSampleData"
            android:text="@string/retrieve" />
 
        <Button
            android:id="@+id/bn_post"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="postData"
            android:text="@string/post" />
         
        <Button
            android:id="@+id/bn_clear"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="clearControls"
            android:text="@string/clear" />
         
    </TableRow>
 
</TableLayout>

Note that an onClick method is defined for each button.

Edit the \res\values\strings.xml file

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="utf-8"?>
<resources>
 
    <string name="firstName">First Name</string>
    <string name="lastName">Last name</string>
    <string name="email">Email</string>
    <string name="retrieve">Retrieve</string>
    <string name="post">Post</string>
    <string name="clear">Clear</string>
    <string name="app_name">AndroidRESTClient</string>
 
</resources>

Edit the AndroidRESTClientActivity.java file

Discuss the WebServiceTask, and the handleResponse() method.

The full source code for the Android client is listed below, but I want to highlight a few items in the code that you should be aware of. On my home network, the computer running the service on Tomcat is at IP address 192.168.1.9. On your network, the address will most definitely be different. If you are running the Android client and the Tomcat service on the same computer, you can get away with using “localhost” in your Android code.

(CORRECTION: (Thanks ABa) Actually, you *can’t* use “localhost”. “Localhost” in this context would refer to the Android device itself. Use the IP address of the computer on your network that is running the Tomcat service. See this StackOverflow link for details.)

1
2
3
public class AndroidRESTClientActivity extends Activity {
 
    private static final String SERVICE_URL = "http://192.168.1.9:8080/RestWebServiceDemo/rest/person";

For this tutorial, the most important code is the internal “WebServiceTask” class, which is extended from an “AsyncTask” class. An AsyncTask class descendant allows a process to run in a separate thread. If our communication with our service were on the Android app’s main thread, the user interface would lock up as the process was waiting for results from the server.

One can define the types of parameters that are passed to one’s instance of the AsyncTask. (more on that later). The communication with the web service occurs in the WebServiceTask’s “doInBackground()” code. This code uses Android’s HttpClient object, and for the GET method, uses HttpGet, and for the POST method, uses HttpPost.

The AsyncTask class includes two other methods that one has the option to overwrite. One is onPreExecute(), which one can use to prepare for the background process, and the other is onPostExecute(), which one can use to do any required clean-up after the background process is complete. This code overrides those methods to display and remove a progress dialog.

The background task also includes two timeout options. One is a timeout period for the actual connection to the service, and the other is a timeout period for the wait for the service’s response.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
package com.avilyne.android;
 
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
 
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.json.JSONObject;
 
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.Toast;
 
public class AndroidRESTClientActivity extends Activity {
 
    private static final String SERVICE_URL = "http://192.168.1.9:8080/RestWebServiceDemo/rest/person";
 
    private static final String TAG = "AndroidRESTClientActivity";
     
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
 
    public void retrieveSampleData(View vw) {
 
        String sampleURL = SERVICE_URL + "/sample";
 
        WebServiceTask wst = new WebServiceTask(WebServiceTask.GET_TASK, this, "GETting data...");
         
        wst.execute(new String[] { sampleURL });
         
    }
 
    public void clearControls(View vw) {
 
        EditText edFirstName = (EditText) findViewById(R.id.first_name);
        EditText edLastName = (EditText) findViewById(R.id.last_name);
        EditText edEmail = (EditText) findViewById(R.id.email);
 
        edFirstName.setText("");
        edLastName.setText("");
        edEmail.setText("");
                 
    }
     
    public void postData(View vw) {
 
        EditText edFirstName = (EditText) findViewById(R.id.first_name);
        EditText edLastName = (EditText) findViewById(R.id.last_name);
        EditText edEmail = (EditText) findViewById(R.id.email);
 
        String firstName = edFirstName.getText().toString();
        String lastName = edLastName.getText().toString();
        String email = edEmail.getText().toString();
 
        if (firstName.equals("") || lastName.equals("") || email.equals("")) {
            Toast.makeText(this, "Please enter in all required fields.",
                    Toast.LENGTH_LONG).show();
            return;
        }
 
        WebServiceTask wst = new WebServiceTask(WebServiceTask.POST_TASK, this, "Posting data...");
 
        wst.addNameValuePair("firstName", firstName);
        wst.addNameValuePair("lastName", lastName);
        wst.addNameValuePair("email", email);
 
        // the passed String is the URL we will POST to
        wst.execute(new String[] { SERVICE_URL });
 
    }
 
    public void handleResponse(String response) {
         
        EditText edFirstName = (EditText) findViewById(R.id.first_name);
        EditText edLastName = (EditText) findViewById(R.id.last_name);
        EditText edEmail = (EditText) findViewById(R.id.email);
         
        edFirstName.setText("");
        edLastName.setText("");
        edEmail.setText("");
         
        try {
             
            JSONObject jso = new JSONObject(response);
             
            String firstName = jso.getString("firstName");
            String lastName = jso.getString("lastName");
            String email = jso.getString("email");
             
            edFirstName.setText(firstName);
            edLastName.setText(lastName);
            edEmail.setText(email);        
             
        } catch (Exception e) {
            Log.e(TAG, e.getLocalizedMessage(), e);
        }
         
    }
 
    private void hideKeyboard() {
 
        InputMethodManager inputManager = (InputMethodManager) AndroidRESTClientActivity.this
                .getSystemService(Context.INPUT_METHOD_SERVICE);
 
        inputManager.hideSoftInputFromWindow(
                AndroidRESTClientActivity.this.getCurrentFocus()
                        .getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
    }
     
     
    private class WebServiceTask extends AsyncTask<String, Integer, String> {
 
        public static final int POST_TASK = 1;
        public static final int GET_TASK = 2;
         
        private static final String TAG = "WebServiceTask";
 
        // connection timeout, in milliseconds (waiting to connect)
        private static final int CONN_TIMEOUT = 3000;
         
        // socket timeout, in milliseconds (waiting for data)
        private static final int SOCKET_TIMEOUT = 5000;
         
        private int taskType = GET_TASK;
        private Context mContext = null;
        private String processMessage = "Processing...";
 
        private ArrayList<NameValuePair> params = new ArrayList<NameValuePair>();
 
        private ProgressDialog pDlg = null;
 
        public WebServiceTask(int taskType, Context mContext, String processMessage) {
 
            this.taskType = taskType;
            this.mContext = mContext;
            this.processMessage = processMessage;
        }
 
        public void addNameValuePair(String name, String value) {
 
            params.add(new BasicNameValuePair(name, value));
        }
 
        private void showProgressDialog() {
             
            pDlg = new ProgressDialog(mContext);
            pDlg.setMessage(processMessage);
            pDlg.setProgressDrawable(mContext.getWallpaper());
            pDlg.setProgressStyle(ProgressDialog.STYLE_SPINNER);
            pDlg.setCancelable(false);
            pDlg.show();
 
        }
 
        @Override
        protected void onPreExecute() {
 
            hideKeyboard();
            showProgressDialog();
 
        }
 
        protected String doInBackground(String... urls) {
 
            String url = urls[0];
            String result = "";
 
            HttpResponse response = doResponse(url);
 
            if (response == null) {
                return result;
            } else {
 
                try {
 
                    result = inputStreamToString(response.getEntity().getContent());
 
                } catch (IllegalStateException e) {
                    Log.e(TAG, e.getLocalizedMessage(), e);
 
                } catch (IOException e) {
                    Log.e(TAG, e.getLocalizedMessage(), e);
                }
 
            }
 
            return result;
        }
 
        @Override
        protected void onPostExecute(String response) {
             
            handleResponse(response);
            pDlg.dismiss();
             
        }
         
        // Establish connection and socket (data retrieval) timeouts
        private HttpParams getHttpParams() {
             
            HttpParams htpp = new BasicHttpParams();
             
            HttpConnectionParams.setConnectionTimeout(htpp, CONN_TIMEOUT);
            HttpConnectionParams.setSoTimeout(htpp, SOCKET_TIMEOUT);
             
            return htpp;
        }
         
        private HttpResponse doResponse(String url) {
             
            // Use our connection and data timeouts as parameters for our
            // DefaultHttpClient
            HttpClient httpclient = new DefaultHttpClient(getHttpParams());
 
            HttpResponse response = null;
 
            try {
                switch (taskType) {
 
                case POST_TASK:
                    HttpPost httppost = new HttpPost(url);
                    // Add parameters
                    httppost.setEntity(new UrlEncodedFormEntity(params));
 
                    response = httpclient.execute(httppost);
                    break;
                case GET_TASK:
                    HttpGet httpget = new HttpGet(url);
                    response = httpclient.execute(httpget);
                    break;
                }
            } catch (Exception e) {
 
                Log.e(TAG, e.getLocalizedMessage(), e);
 
            }
 
            return response;
        }
         
        private String inputStreamToString(InputStream is) {
 
            String line = "";
            StringBuilder total = new StringBuilder();
 
            // Wrap a BufferedReader around the InputStream
            BufferedReader rd = new BufferedReader(new InputStreamReader(is));
 
            try {
                // Read response until the end
                while ((line = rd.readLine()) != null) {
                    total.append(line);
                }
            } catch (IOException e) {
                Log.e(TAG, e.getLocalizedMessage(), e);
            }
 
            // Return full string
            return total.toString();
        }
 
    }
}

(Code discussion, with excerpts, here)

Update the AndroidManifest.xml

Since this app needs to communicate via the internet, one must give the app the appropriate permissions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="utf-8"?>
    package="com.avilyne.android"
    android:versionCode="1"
    android:versionName="1.0" >
 
    <uses-sdk android:minSdkVersion="10" />
    <uses-permission android:name="android.permission.INTERNET" />
     
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".AndroidRESTClientActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
 
</manifest>

Running the Android Client App

This screenshot shows the Android client app with its blank screen.
Android client blank

If one presses the “Retrieve” button, the web service will respond:
Server returning person

The Android client’s controls should get populated with the retrieved Person:
Android client person

Here I’ve modified the values on my Android client screen, and pressed the “Post” button:
Android client test human

The web service responds to the post:
Storing test human

Hopefully, this will give you some idea of how to build an Android client that communicates with a web service using REST.


반응형

'Development > Servers' 카테고리의 다른 글

MySQL 테이블 명세를 위한 쿼리  (0) 2014.12.15
Mac에서 MariaDB  (0) 2014.02.25
SVN을 사용하기 위해 발버둥 치면서...  (0) 2014.01.08
Tomcat autoReconnect  (0) 2013.12.24
MariaDB  (0) 2013.12.12