02162: Course on Software Engineering 2 (e14)
In this tutorial, you will learn how to implement an Android App which accesses
some web services for reporting sensor information and for showing the current
information of a remote sensor in the App. To this end, you will also need to
extend the web services in order to make them thread safe and to simulate
pushing information from the server to the App.
For implementing web clients in an Android App, we use the RestTemplates of
the Spring framework for Android.
In this assignment, you will implement an App that reports the absolute
value of the acceleration sensor to the web server (the sensor number
can be chosen by the user); in addition, the App should show the
current reading of a sensor, which also can be choosen by the user (which
might be different from the one the App reports values for).
Tasks
Here are the steps for the tasks of the fourth assignment:
- In the first part, you will implement an App that reports the absolute values of the accelerator
to the web server. The sensor is should be a number that can be freely
entered by the user via some text input field (called
EditText in Android).
- In order to make it easier for the App to communicate with the web server, you
need to slightly change the web services from tutorial 2, first.
Create another Dynamic Web Project, where you can reuse most of the code from
tutorial 2. To start with, you only need to
replace the annotations
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
with
@Produces(MediaType.APPLICATION_JSON)
in the implementation of your web services from tutorial 2.
The result will be that the web service returns actual JSON representations of the
result instead of the XML that we used in tutorial 2.
The reason for this is that RestTemplates work with JSON by default.
You should also change the data type for events, so that each event is assigned a unique
identifier, which could be the time when it is registered by the server. We will make
some other extensions to this web project later in this tutorial.
- Next, create another Android Application Project (see Tutorial 3).
In order to use RestTemplates
of the Spring framework for Android in your App, you need to download some additional
libraries. For your convenience, all the needed jar files are available in the
following archive: spring-android-rest-templates-libs.zip.
Copy the all jar files from this archive to the lib folder of your new Android Application Project.
Since this App will need access to the internet, you need to add this information to the
AndroidManifest.xml in your newly created Android Application Project. The
corresponding XML snippet looks as follows:
<uses-permission android:name="android.permission.INTERNET"/>
- Next, add a text input field (called a
EditText in Android) to the layout of the
main activity (actually, its fragment). This field needs to be given a unique identifier, so
that its value can be accessed from the program code. This field should be used by the user to enter
the sensor id (the default could be 1) for which the values of the accelerator should be reportet.
- Then, create and register a
SensorEventListener (see Tutorial 3)
when the main activity starts. When the value of the sensor changes significantly, your
listener should register this value to the web server vy invoking the addevent service.
The code for this using RestTemplates could look as follows:
final String url = BASE_URL + "/addevent?id={id}&value={value}";
final Map parameters = new HashMap();
parameters.put("id", String.valueOf(id));
parameters.put("value", String.valueOf(value));
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJacksonHttpMessageConverter());
Event result = restTemplate.getForObject(url, Event.class, parameters);
This code will be explained in the tutorial on Friday, September 26. You also find some
explanations here or in some of the links below.
The id should be read from the text input field (see Tutorial 3
for how to access the value of such a field); the value should be the current absolute value of
the sensor.
Since calling a web service can take some time, it is a good idea to run this part of the code
asynchrounusly in a thread (but make sure you do not run too many of these concurrently). If you
are not familiar to Thread programming yet, you might (ab-)use the AsyncTask provided
by Android to issue an asynchronous task. It is a slight abuse, since in this case the
AsyncTask would not need report anything back to the GUI (which is the main purpose
of this construct).
- In order to test your App, you need to deploy your web application to a web server. For now, this will
be the localhost; but, each group will be assigned a webserver to which they can deploy their web
applications soon. When you use your local host at DTU, it is recommended that you connect both, your
phone and the webserver to the internet via eduroam; then, you can use the dynamic IP address assigned
to your computer by eduroam in the above BASE_URL (on Windows, you can see this IP address by executing
ipconfig on the console after you connected to eduroam).
By entering some URLs to your Web browser for registering events and for looking them up again,
check whether the web services are running properly (see Tutorial 2).
Before your test your App, use the admin interface of the web server to increase the maximal
number of threads in the pool for http-requests (the default of 5 might not be enough with more
than one phone, and services might be stuck for a long time).
- Next start your App and shake the phone. Use the
getEventList web services in the
browser to check whether the values are properly registered in your web application.
- In the second part, you add another feature to your App, which is supposed to show the most current
value of some sensor (chosen by the user via another edit text) to the user. Note that the
sensor id could be an id of a remote sensor, so the value needs to be obtained via the web server.
- This part, actually requires some thread programming on the server side as well as on the client
side. The reason is that web services follow the client server architecture. This means that
the server has no way of pushing a new value to the client. This in turn, means that the
client needs to poll the server for new values all the time, which is quite inefficient and
causes lots of communication.
Therefore, we try another technique here: a long-running request which we call awaitevent .
When a new a awaitevent t request comes in, the server puts this thread on hold (in Java thread
programming, this is achieved with a wait() ). Once a new value for that
sensor comes in (via a call of an addevent service), this thread is woken up (in Java thread
programming, this is achieved with a notifyAll() ), the service can than
look up the value and return that value to the client. Note that this means, that a request
is open for an extremely long time; the implementation must therefore make sure that
not too many of such requests are open at the same time; and it might be a good idea to
build in a timeout.
The details of the thread programming will be discussed in the tutorial, and you will find much
more information on concurrent programming in Java here and specifically
on the wait/notify mechanism
and here.
Now add a web service awaitevent which takes the sensor id as a parameter.
This should be implemented as a long-running request, which returns when
the next event for that sensor is reported to the server.
- Deploy the extended web application to the web server and check via URL interactions
whether the services
awaitevent and addevent properly
work together.
- Next, you should extend the App, so that the user can select a sensor id (which can
be different from the sensor id that it reports values for). The App should then
show the current value for this sensor, and that value should be updated whenever
the server receives a new value for the respective sensor. This can be done by calling the
awaitevent
service.
Since the awaitevent service can take very long time. It needs to be invoked
from an independent thread. In Android, however, an independent thread should not
update the Android GUI (the textfield showing the current value). In order to
properly update the GUI from another thread, you can issue the actual update in the following way:
textview.post(new Runnable() {
public void run() {
textview.setText(text);
}
});
This makes sure that the textfield will be updated by the GUI thread at an
appropriate time.
Also make sure that your thread is properly shut down when the activity stops.
This and some other issues will be discussed in some more detail in the tutorial.
- Test your App together with the server. Also enter some events (for a different sensor
id) via your web browser or use a second phone reporting values for another sensor id.
- Note that some of the techniques of thread programming and having long-running request
require careful design. Some of them will be discussed in the tutorial. In you project later,
you should be aware of the following issues:
- Make sure that the web server is configured with enough threads in the pool of HTTP requests
(increas the maximum number).
- Make sure that you do not leave behind too many open connections/request when programming
the client (when abandoning a request).
- The implementation of "pushing a value" from the server to the client via
an open long-running request can be improved (you could use another channel
to change the events you would like to be notified about).
- For the real project, you should pre-process the low-level events on the
smart phone and only send higher-level (and less frequent) events to the
server in order to avoid too much communication.
- In the real project, you need to think about some reasonable timeouts.
Useful links
Here are some links that might be helpful:
|