Showing posts with label Android Tips And Tricks. Show all posts
Showing posts with label Android Tips And Tricks. Show all posts

Tuesday, September 28, 2010

google protocol buffers in android

Lets face it... there is a good chance your android app will need some sort of mobile to server communication of some sort. You could go the route of raw sockets, with "packets" of your own design. You could post json, you could post xml, or you could build a web form and post to a url.

All of these are very valid options, however I've kind of decided I'm a fan of protocol buffers. What are protocol buffers you might ask? A language / platform independent framework for serializing / deserializing binary data that generally produces lightweight messages quickly.(read more about it here: http://code.google.com/p/protobuf/)

Protocol buffers

The general idea is you have a .proto file that defines your "protocol" in plain human readable text. It supports many data types, ability to not provide a property, and even send raw bytes.

I've tried both the usual protocol buffers implementation from url above with and without the -lite flag, and the protobuf implementation for j2me and frankly seems like so far the j2me implementation is faster and lighter weight, and hasn't yet been problematic for missing anything.  (http://code.google.com/p/protobuf-javame/)

I am going to create a very simple proto file that might represent a message you may be sending in your application.


message LocationSearch{
       required string searchterms = 1; // the text body of the search
       optional double latitude  = 2; //optional lat/lng coords... if its provided we'll use it.
       optional double longitude = 3;
}


I can run proto generator (more on this in a moment) on it and it will create LocationSearch.java on the android side and then proto generator on the .net side (my server will be .net in this case) and get LocationSearch.cs.

In android we need to add the reference, so right click project and then go to Build Path -> Add External Archives. Choose the location you downloaded protobuf-javame-1.0.0.jar. Copy the generated .java file to some place in your packages and we'll get started momentarily.

How you decide to delineate messages could be anything, but I've just implemented handlers so if I post to /LocationSearch on a specific server, I should get a response back. To do this, we'll need to download and reference (the same way we referenced protobuf) apache httpclient library v 4.02.

I've created a utility method that posts a simple message from byte[] and returns a byte[] representing the response. I've not read up on optimizing for android recently... so there are undoubtedly places that need to be final etc. First the method...


public static byte[] postMessage(String url,byte[] data, String filename) throws Exception
    {
  byte[] rData = new byte[0];
  HttpClient httpclient = new DefaultHttpClient();
  HttpPost httppost = new HttpPost(url);

  MultipartEntity reqEntity = new MultipartEntity(
   HttpMultipartMode.BROWSER_COMPATIBLE);

  reqEntity.addPart("message",
    new InputStreamKnownSizeBody(
      new ByteArrayInputStream(data),
      data.length, "application/octet-stream", "message"));
  Log.i("Sending:",new String(data));
  httppost.setEntity(reqEntity);

  System.out.println("executing request " + httppost.getRequestLine());
  HttpResponse response = httpclient.execute(httppost);
  HttpEntity resEntity = response.getEntity();

  if (resEntity != null) {
   
   ByteArrayOutputStream bos = new ByteArrayOutputStream();
   resEntity.writeTo(bos);
   rData = bos.toByteArray();
   Log.i("Response",new String(rData));

  }
  return rData;
 }

and a prerequisite class to handle being able to upload from a byte[] vs disk.

public class InputStreamKnownSizeBody extends InputStreamBody{
 private int lenght;

 public InputStreamKnownSizeBody(
   final InputStream in, final int lenght,
   final String mimeType, final String filename) {
  super(in, mimeType, filename);
  this.lenght = lenght;
 }

 @Override
 public long getContentLength() {
  return this.lenght;
 }
}

Ok... so now we have the facility to post a message and get a response back. Lets see what a round trip might look like...

//lets create a protocol buff object, and send it on its way.
 LocationSearch search = LocationSearch.newBuilder().setsearchterms("rob john*");  

 // remember lat/lng are optional
 byte[] response = postMessage("127.0.0.1/LocationSearch",search.toByteArray(),"dummy");

 //now if we had created a "LocationSearchResponse class with applicable data, we could do...
 LocationSearchResponse responseObj = LocationSearchResponse.parseFrom(response);

A couple notes about my examples
  • I assume the server portion of this you guys can set up, and handle parsing/sending a response. The general scenario is pretty much the same everywhere
  • You'll want to make sure all your basics are covered as far as exception handling and failing gracefully (some of these things get in the way of conveying concepts =)
  • Obviously if you aren't running a server on your local machine, the URL in the example above will simply not work.
  • Filename in the postmessage method above is currently not doing a thing (yet)

Homework

  1. Create the corresponding return message with whatever you think might be appropriate to receive from server. 
  2. Implement server side logic. Once you parse the form and get the "message" element which from the server appears as a posted file, .parseFrom into your message format. Create the response, and write the resulting bytes to the response.
I hope this example proved helpful if nothing else, than to know what another option is as far as device to server (or even device to device) communications.

Until next time!

Sunday, September 26, 2010

Android reusable button wiring

Prior to android 1.6, button wiring in android was a process of building out your event listeners and wiring programmatically.  1.6 introduced declarative button wiring in the xml for the ui.

Earlier on, I was trying to build something that loosely resembled the phone dialer but of course NOT the phone dialer. About 15 buttons each having the differentiation from one another nothing more than having a value of a diff number.

I quickly got frustrated writing switch cases and if's for something that was simply taking the value and passing it directly on. I instead I realized I could do the following...




...

And for my click handler...
public void dialerButtonClick(View target) {
doSomethingWithButtonClick(Integer.parseInt(target.getTag().toString()));
}

Is this going to make sense in every case? No... probably not. getTag almost definitely is more of a hit, than simply comparing R.id's, but it seemed to work pretty good for what I wanted it for.

Until next time.



Understanding the Activities and Intents in android

When I first started tinkering in android I had a problem finding the equivalent to views and activities  in languages I've written for before. It just took a while to understand when I should be building an activity etc. Not a difficult concept to understand, just realizing that this isnt exactly a 1:1 map to other architectures. An activity is generally a process, perhaps create account, perhaps create blog post.

Imagine for example that you have a twitter app (I honestly dont use twitter, so my example may be bad, but I think its something enough people use to understand where I am going with it.). This twitter app has a few key "core" concepts. Posting, Change Follow status, Change settings etc. It may be obvious to some, but these would be ideal candidates to be activities. Activities which may consist of one or more Views being displayed. How exactly does the application entry points have anything to do with this? Using intent filters etc, I can allow any of these activities to be launched from other apps, or directly from launcher itself as exactly that... a well defined process. Multiple activities dont have to mean multiple entry points, but when you think of exposing New Tweet functionality to other apps, you expose the activity in your activity filter.


Intent intent = new Intent();
intent.setClassName("com.mycompany.someapp", "com.mycompany.someapp.Tweet");
intent.putExtra("com.mycompany.someapp.TweetMessage", "Hello world!"); 
startActivity(intent);


Which loosely translates to the pseudo code:
Open Tweet activity setting TweetMessage to "Hello World" (which I can retrieve and use later

A brief post, but hopefully may ease the transition from development in other languages