In an earlier blog post we build a Restful WS running on Wildfly, now lets build the client part for Android. Thanks to the okHttp, an Apache 2.0 licensed java and Android library, this becomes a very easy challenge. In a mobile application we definitely want to implement a async call as we cannot rely on the response time being fast and a blocked application is not a pleasant user experience.
To make it a bit more interesting for the aviation context of my blog, we will take a real airline webservice and show operational flight data in the second step.
Step 1: The Basic Webservice Client
Lets get started, I skip the project creation steps, you can create any basic empty Android application.
Add the dependency
.. dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:24.1.1' testCompile 'junit:junit:4.12' compile 'com.squareup.okhttp3:okhttp:3.9.0' }
Implement the WS call with a call back
(we use the serverside we implemented previously with 2 parameters)
private void testOKHttp() { OkHttpClient httpClient = new OkHttpClient(); String callURL = "http://www.yourservername.com/DemoRandomizer/randomservice/random/numberrange"; HttpUrl.Builder urlBuilder = HttpUrl.parse(callURL).newBuilder(); urlBuilder.addQueryParameter("min", "10"); urlBuilder.addQueryParameter("max", "100"); callURL = urlBuilder.build().toString(); Request request = new Request.Builder().url(callURL).build(); httpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { System.out.println(e.getMessage()); } @Override public void onResponse(Call call, Response response) throws IOException { ResponseBody responseBody = response.body(); if (!response.isSuccessful()) { throw new IOException("Error response " + response); } else { String str = new String(responseBody.bytes()); System.out.println(str); } } }); }
It is good practice to block UI features (buttons, etc) that rely on the WS response or use a progress dialogm which is in some way blocking but the user is aware of the ongoing request.
// Set progress dialog befor the call prgDialog = new ProgressDialog(this); prgDialog.setMessage("Please wait..."); prgDialog.setCancelable(false); prgDialog.show(); // remove the call after successful response or error prgDialog.dismiss();
Step 2: Client to read British Airways data
Lets gets our hand on some real data and tap into one of the open API’s offered by some airlines and airports, though I have to say that only very few players offer their data for public consumption. I will list some API’s in another blog post. Today we will make use of the British Airways API running on the Tibco Mashery platform. BA offers a variety of flight related data and we look at the flight status webservice. We can use the service for evaluation purpose up to 1 call a second and 500 calls a day, good enough to play with their data. We will collect the status of outbound flights from LHR (London Heathrow) for +/- 2hours. Before jumping into sourcode I recommend using a tool to test the webservice manually, eg. the Chrome Browser Rest Web Service Client extension.
One of the challenges, every API offering might support JSON and XML as WS GET, but the attributes and response format is different, we have to implement for every airline or airport we want to use the data, or add some kind of mapping layer.
For the BA service we have to change to call from step 1 above because we need to add the API key that you apply for under your account, also the parameter are not handled as query parameters but as parameter matrix.
The first code block only lists the relevant parts and the complete code block below adds some extras to handle GUI access, exception handling and the parameter creation.
private void testWSCall() { OkHttpClient httpClient = new OkHttpClient(); String callURL = "https://api.ba.com/rest-v1/v1/flights;departureLocation=LHR;startTime=12:00;endTime=18:00;"; Request request = new Request.Builder().url(callURL) .addHeader("Content-Type", "application/json") .addHeader("client-key", "YOUR_KEY_HERE") .build(); httpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { System.out.println("WS Call failed (1):" + e.getMessage()); } @Override public void onResponse(Call call, Response response) { ResponseBody responseBody = response.body(); if (!response.isSuccessful()) { final String errRep = "WS Call failed (2):" + response.toString(); System.out.println(errRep); } else { String str = new String(responseBody.bytes()); System.out.println(str); } } }); }
private void testOKHttp() { OkHttpClient httpClient = new OkHttpClient(); DateFormat dateFormatShort = new SimpleDateFormat("HH:mm"); Date date = new Date(); long timeNow = date.getTime(); long timePlus = timeNow + 120 * 60 * 1000; long timeMinus = timeNow - 120 * 60 * 1000; Date datePlus = new Date(timePlus); Date dateMinus = new Date(timeMinus); String callURL = "https://api.ba.com/rest-v1/v1/flights;departureLocation=LHR;startTime=" + dateFormatShort.format(dateMinus) +";endTime=" + dateFormatShort.format(datePlus) +";"; System.out.println(callURL); Request request = new Request.Builder().url(callURL) .addHeader("Content-Type", "application/json") .addHeader("client-key", "YOUR_KEY_HERE<span data-mce-type="bookmark" id="mce_SELREST_start" data-mce-style="overflow:hidden;line-height:0" style="overflow:hidden;line-height:0" ></span>") .build(); httpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { System.out.println("WS Call failed (1):" + e.getMessage()); prgDialog.dismiss(); } @Override public void onResponse(Call call, Response response) { ResponseBody responseBody = response.body(); if (!response.isSuccessful()) { final String errRep = "WS Call failed (2):" + response.toString(); mHandler.post(new Runnable() { @Override public void run() { editTextResult.setText(errRep); } }); prgDialog.dismiss(); } else { try { System.out.println("Response " + response.toString()); String str = new String(responseBody.bytes()); prgDialog.dismiss(); final JSONObject svcresponse = new JSONObject(str); int spacesToIndentEachLevel = 2; final String prettyPrintString = svcresponse.toString(spacesToIndentEachLevel); mHandler.post(new Runnable() { @Override public void run() { editTextResult.setText(prettyPrintString); } }); } catch (Exception e) { e.printStackTrace(); final String errStr = e.getMessage(); mHandler.post(new Runnable() { @Override public void run() { editTextResult.setText(errStr); } }); prgDialog.dismiss(); } } } }); }
Pingback: METAR and TAF Webservice | The JavaDude Weblog