Handling binary responses in native Android using Java adapters

This is a follow up to Handling binary responses in native iOS using Java adapters. Starting with MobileFirst Platform 7.0, you can send and receive arbitrary data to your client’s device using Java Adapters.

Overview

In this example, I will show a native Android application that can convert a string to a QR code. The user will input a string (for example a URL), submit it to the Java Adapter, which will in turn send an image representing the QR code. The Android application will handle the binary data and display the image.

Adapter code

This section is the same as the iOS version. I've created a MobileFirst project called BinaryResponse. In it I've added a Java Adapter called QR. To make the QR generation easier, I am using the ZXing library. I've copied core-3.2.0.jar and javase-3.2.0.jar into the lib folder of my adapter. I left QRApplication.java as-is and wrote my code in QRResource.java.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Path("/")
public class QRResource {
	//Define logger (Standard java.util.Logger)
	static Logger logger = Logger.getLogger(QRResource.class.getName());
    //Define the server api to be able to perform server operations
    WLServerAPI api = WLServerAPIProvider.getWLServerAPI();
	@GET
	@Path("/{itemId}")
	@Produces("image/png")
	public Response getQR(@PathParam(value="itemId") String id) throws WriterException, IOException{
		QRCodeWriter writter = new QRCodeWriter();
		ByteArrayOutputStream stream = new ByteArrayOutputStream();
		BitMatrix bitMatrix = writter.encode(id, BarcodeFormat.QR_CODE, 200, 200);
		MatrixToImageWriter.writeToStream(bitMatrix, "png", stream);
		return Response.ok(stream.toByteArray(), "image/png").build();
	}
}
I used @Path("/") at line 1 to define that the URL path for my resource should just be /adapters/adapterName. The adapter resource file contains only one method, getQR, which can be accessed via HTTP GET using the path @Path("/{itemId}") at line 11, where itemId is the string provided by the user. The method uses @Produces("image/png") and automatically adds the image/png Content-Type in the response. The implementation details of this method are out of scope and can be understood from the ZXing documentation. All I did was use QRCodeWriter to encode the string to a 200 by 200 bar code. The resulting stream is returned in the response.

Android Studio Project

I've created a new Android project named BinaryResponse and configured it to use MobileFirst Platform. The UI simply shows an EditText to enter the string, a Button to trigger the request and a ImageView to display the generated QRCode.
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
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        WLClient client = WLClient.createInstance(this);
        buttonInvoke = (Button)findViewById(R.id.generate);
        codeInput = (EditText)findViewById(R.id.code);
        imageView = (ImageView)findViewById(R.id.imageView);
        final MainActivity activity = this;
        buttonInvoke.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    Editable input = codeInput.getText();
                    String code = URLEncoder.encode(input.toString(), "utf-8");
                    URI adapterPath = new URI("/adapters/QR/" + code);
                    WLResourceRequest request = new WLResourceRequest(adapterPath,WLResourceRequest.GET);
                    request.send(new MyInvokeListener(activity));
                } catch (URISyntaxException e) {
                    e.printStackTrace();
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
            }
        });
    }

Clicking on the Generate button creates a WLResourceRequest that points to the adapter URL, with the user provided string appended.
I used send(WLHttpResponseListener) and created a new class called MyInvokeListener to handle the response.

MyInvokeListener implements WLHttpResponseListener.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    public MyInvokeListener(MainActivity activity) {
        Log.v(TAG, "MyInvokeListener constructor");
        this.activity = activity;
    }
    @Override
    public void onFailure(HttpResponse httpResponse, Exception e) {
        Log.v(TAG, "onFailure");
    }
    @Override
    public void onSuccess(HttpResponse httpResponse) {
        Log.v(TAG, "onSuccess");
        HttpEntity entity = httpResponse.getEntity();
        try {
            InputStream content = entity.getContent();
            activity.setImage(content);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

The onSuccess method receives a raw HttpResponse object. The content is extracted as an InputStream.

Back in the MainActivity there is a method to take that InputStream and use it to display the image.

1
2
3
4
5
6
7
8
9
10
11
12
13
    public void setImage(final InputStream input){
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                imageView.setImageBitmap(BitmapFactory.decodeStream(input));
                try {
                    input.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }

Sample

You can find the code for this example on GitHub.

For best results, make sure you use MobileFirst with the latest iFix available by checking for updates in Eclipse, or IBM Fix Central if you have installed a Studio version from there.

missing_alt
Inclusive terminology note: The Mobile First Platform team is making changes to support the IBM® initiative to replace racially biased and other discriminatory language in our code and content with more inclusive language. While IBM values the use of inclusive language, terms that are outside of IBM's direct influence are sometimes required for the sake of maintaining user understanding. As other industry leaders join IBM in embracing the use of inclusive language, IBM will continue to update the documentation to reflect those changes.
Last modified on May 01, 2016