Saturday, December 14, 2013

Modern talking 2 - The guide to communicating with a server in Android applications, the journey continues

הפוסט זמין גם בעברית כאן - http://iandroid.co.il/dr-iandroid/archives/14998

Hello again,
As i stated in an earlier post, after my Hebrew blog posts started to get published on the wonderful iAndroid website i decided to transform this blog into the English edition.
Slowly but surely i'll translate my earlier posts to English.

For those of you who don't know, all of my posts until now (including this one) are derived from AndconLab; the developer code lab that  +Ran Nachmany+Amir Lazarovich and myself created last year for the great MultiScreenX convention we helped organize.

In the last post we dealt with some basic explanations and examples as to how the actual client server communication action works and that is why i fibbed a bit and didn't give you the complete picture so :for starters, here's the full implementations of the TransactionJob

 private class TransactionJob implements Runnable {  
           private static final String TAG = "Service";  
           @Override  
           public void run() {  
                try {  
            HttpGet httpGet = new HttpGet(GET_EVENTS);  
                  String responseBody = null;  
                  try {  
           responseBody = mHttpClient.execute(httpGet, mResponseHandler);  
                  } catch(Exception ex) {  
                    ex.printStackTrace();  
                  }  
         List<Event> events = null;  
                  if(responseBody != null) {  
           events = JacksonUtils.sReadValue(responseBody, new TypeReference<List<Event>>() {}, false);  
           if (events != null) {  
             SQLiteDatabase db = new DatabaseHelper(CommunicationService.this.getApplicationContext(), DatabaseHelper.DB_NAME, null, DatabaseHelper.DB_VERSION).getWritableDatabase();  
             DBUtils.storeEvents(db, events);  
             //fire an intent  
             Intent intent = new Intent();  
                intent.setAction(RESULTS_ARE_IN);  
                CommunicationService.this.sendBroadcast(intent);  
           }  
                  }  
          } catch (Exception e) {  
               e.printStackTrace();  
          }  
           }  
      }  
For those of you who are good (i.e. lazy) engineers and lack the strength to look for the differences, here they are, these 2 lines:

 SQLiteDatabase db = new DatabaseHelper(CommunicationService.this.getApplicationContext(), DatabaseHelper.DB_NAME, null, DatabaseHelper.DB_VERSION).getWritableDatabase();  
             DBUtils.storeEvents(db, events);  

Some of you are probably questioning my sanity or the amount of free time that i posses by asking questions like:
  • Who cares?
  • Why would that make any difference?
  • Sqlite databases? they are so slow to work with on Android's UI, why would i do such a thing?

To answer some of these questions i must first take one step back.
Normally in posts and tutorials which try to address this issue you see the tactical approach which is  normally an AsynckTask of sorts which fetches and parses the request.
What i want to show you is an example of a strategic approach which could be summed up by; "Which battles must i sacrifice in order to win the war?"
In this case the classic behavior i see in most cases is that the developers forgo the use of sophisticated mechanisms such as ContentProviders in order to speed up the process and shorten the time delay from request to display while forcing themselves to download the same data over and over again which might (in some cases, not all) make their application actually feel more sluggish and nonresponsive and while draining the user's battery (and wallet, if they are working on some 3G packages).

So, if you except my premise and agree with my conclusion, lets get started by viewing the activity code and explain when i download the data and when i use what's already available to me:
 public class MainActivity extends SherlockActivity implements OnItemClickListener{  
      private ListView mList;  
      private ProgressDialog mProgressDialog;  
      private BroadcastReceiver mUpdateReceiver;  
      @Override  
      protected void onCreate(Bundle savedInstanceState) {  
           super.onCreate(savedInstanceState);  
           setContentView(R.layout.main_activity);  
           mList = (ListView) findViewById(R.id.list);  
           mList.setOnItemClickListener(this);  
      }  
      @Override  
      protected void onPause() {  
           super.onPause();  
           if (null != mUpdateReceiver) {  
                unregisterReceiver(mUpdateReceiver);  
                mUpdateReceiver = null;  
           }  
      }  
      @Override  
      protected void onResume() {  
           super.onResume();  
           mUpdateReceiver = new BroadcastReceiver() {  
              @Override  
              public void onReceive(Context context, Intent intent) {  
                  if (intent.getAction().equalsIgnoreCase(CommunicationService.RESULTS_ARE_IN)) {  
                      new lecturesLoader().execute((Void) null);  
                  }  
                  if (null != mProgressDialog)  
                      mProgressDialog.dismiss();  
              }  
           };  
           final IntentFilter filter = new IntentFilter();  
           filter.addAction(CommunicationService.RESULTS_ARE_IN);  
           registerReceiver(mUpdateReceiver, filter);  
           new lecturesLoader().execute((Void)null);  
      }  
      @Override  
      public void onItemClick(AdapterView<?> list, View view, int position, long id) {  
           Intent i = new Intent(this,SingleLectureActivity.class);  
           i.putExtra(SingleLectureActivity.EXTRA_LECTURE_ID, id);  
           startActivity(i);  
      }  
   @Override  
   public boolean onCreateOptionsMenu(Menu menu) {  
     getSupportMenuInflater().inflate(R.menu.main, menu);  
     return true;  
   }  
   @Override  
   public boolean onOptionsItemSelected(MenuItem item) {  
     switch (item.getItemId()) {  
       case R.id.action_refresh:  
         refreshList(true);  
         return true;  
       default:  
         return super.onOptionsItemSelected(item);  
     }  
   } 
private void refreshList(boolean showProgressbar) {
if (showProgressbar) {
mProgressDialog = ProgressDialog.show(this, getString(R.string.progress_dialog_starting_title), getString(R.string.progress_dialog_starting_message));
}
Intent i = new Intent(this,CommunicationService.class);
startService(i);
}
////////////////////////////////
// Async task that queries the DB in background
////////////////////////////////
private class lecturesLoader extends AsyncTask<Void, Void, Cursor> {
private SQLiteDatabase db;
@Override
protected Cursor doInBackground(Void... params) {
db = new DatabaseHelper(MainActivity.this.getApplicationContext(), DatabaseHelper.DB_NAME,null , DatabaseHelper.DB_VERSION).getReadableDatabase();
return DBUtils.getAllLectures(db);
}
@Override
protected void onPostExecute(Cursor result) {
if (0 == result.getCount()) {
//we don't have anything in our DB, force network refresh
refreshList(true);
}
else {
LecturesAdapter adapter = (LecturesAdapter) mList.getAdapter();
if (null == adapter) {
adapter = new LecturesAdapter(MainActivity.this.getApplicationContext(), result);
mList.setAdapter(adapter);
}
else {
adapter.changeCursor(result);
}
}
db.close();
}
} }
The workflow explanation to the code above is this:
  1. The application is initialized. 
  2. The Broadcast intent for the "RESULTS_ARE_IN" action is registered.
  3. The LectureLoader AsyncTask is initializes and checks whether or not he has saved data in the database and if so; the application uses the existing data and if not it calls the refreshList method  which starts the fetching and parsing Service i talked about in the last post(if the CommunicationService Service is initialized it will not interfere with the UI because it updates the database in the background and only then sends the intent which we talked about in item #2).
And here's the data base handling code:

 public class DBUtils {  
      private static final String TAG = "DBUtils";  
      //select lecturerImage from assets where lectureVideoId='Y4UMzOWcgGQ';  
      /**  
       * Create DB table  
       *  
       * @param db    Reference to the underlying database  
       * @param sb    Clears any existing values before starting to append new values  
       * @param tableName The name of the DB table  
       * @param columns  Tuples of column names and their corresponding type and properties. This field must be even for that same  
       *         reason. I.e. "my_column", "INTEGER PRIMARY KEY AUTOINCREMENT", "my_second_column", "VARCHAR(255)"  
       */  
      public static void createTable(SQLiteDatabase db, StringBuilder sb, String tableName, String... columns) {  
           if (columns.length % 2 != 0) {  
                throw new IllegalArgumentException(  
                          "Columns length should be even since each column is followed by its corresponding type and properties");  
           }  
           StringUtils.clearBuffer(sb);  
           // Prepare table  
           sb.append("CREATE TABLE ");  
           sb.append(tableName);  
           sb.append(" (");  
           // Parse all columns  
           int length = columns.length;  
           for (int i = 0; i < length; i += 2) {  
                sb.append(columns[i]);  
                sb.append(" ");  
                sb.append(columns[i + 1]);  
                if (i + 2 < length) {  
                     // Append comma only if this isn't the last column  
                     sb.append(", ");  
                }  
           }  
           sb.append(");");  
           // Create table  
           db.execSQL(sb.toString());  
      }  
      /**  
       * Drop table if exists in given database  
       *  
       * @param db    Reference to the underlying database  
       * @param tableName The table name of which we try to drop  
       */  
      public static void dropTable(SQLiteDatabase db, String tableName) {  
           dropTable(db, new StringBuilder(), tableName);  
      }  
      /**  
       * Drop table if exists in given database  
       *  
       * @param db    Reference to the underlying database  
       * @param sb    Clears any existing values before starting to append new values  
       * @param tableName The table name of which we try to drop  
       */  
      public static void dropTable(SQLiteDatabase db, StringBuilder sb, String tableName) {  
           StringUtils.clearBuffer(sb);  
           sb.append("DROP TABLE IF EXISTS ");  
           sb.append(tableName);  
           // Drop table  
           db.execSQL(sb.toString());  
      }  
      /**  
       * Stores events and their lectures and speakers in db  
       * @param db - Writeable SQLITE DB  
       * @param events - events to be stored  
       * @return  
       */  
      public static boolean storeEvents(SQLiteDatabase db, List<Event> events) {  
           db.beginTransaction();  
           ContentValues cv;  
           List<Lecture> lectures;  
           List<Speaker> speakers;  
           long eventId;  
           for (Event event : events) {  
                //store event  
                cv = event.getContentValues();  
                db.replace(Event.TABLE_NAME, null, cv);  
                eventId = event.getId();  
                //loop through all lectures  
                lectures = event.getLectures();  
                for (Lecture lecture : lectures) {  
                     //set event id  
                     lecture.setEventId(eventId);  
                     cv = lecture.getContentValues();  
                     db.replace(Lecture.TABLE_NAME, null, cv);  
                     //remove all speakers from this lecture  
                     clearLectureSpeakers(db, lecture);  
                     //loop through all the speakers  
                     speakers = lecture.getSpeakers();  
                     for (Speaker speaker : speakers) {  
                          //store speaker in db  
                          cv = speaker.getContentValues();  
                          db.replace(Speaker.TABLE_NAME, null, cv);  
                          //add speaker to this lecture  
                          addSpeakerToLecture(db, speaker, lecture);  
                     }  
                }  
           }  
           db.setTransactionSuccessful();  
           db.endTransaction();  
           return true;  
      }  
      private static void addSpeakerToLecture(SQLiteDatabase db, Speaker speaker, Lecture lecture) {  
           ContentValues cv = new ContentValues();  
           cv.put(DatabaseHelper.PAIR_LECTURE_ID, lecture.getId());  
           cv.put(DatabaseHelper.PAIR_SPEAKER_ID, speaker.getId());  
           db.insert(DatabaseHelper.LECTURE_SPEAKER_PAIT_TABLE, null, cv);  
      }  
      private static void clearLectureSpeakers(SQLiteDatabase db, Lecture lecture) {  
           db.delete(DatabaseHelper.LECTURE_SPEAKER_PAIT_TABLE, DatabaseHelper.PAIR_LECTURE_ID + "=" + lecture.getId(), null);  
      }  
      /**  
       * Fetch all events from DB  
       * @param db  
       * @return cursor holding id, name, description and logo url  
       */  
      public static Cursor getEventsCurosr(SQLiteDatabase db) {  
           String[] cols = new String[] {  
                     Event.COLUMN_NAME_ID,  
                     Event.COLUMN_NAME_NAME,  
                     Event.COLUMN_NAME_DESCRIPTION,  
                     Event.COLUMN_NAME_LOGO_URL  
           };  
           Cursor c;  
           c = db.query(Event.TABLE_NAME, cols, null, null, null, null, Event.COLUMN_NAME_START_DATE + " DESC");  
           return c;  
      }  
      public static Cursor getLecturesByEventId(SQLiteDatabase db, long eventId) {  
           String[] cols = new String[] {  
                     Lecture.COLUMN_NAME_ID,  
                     Lecture.COLUMN_NAME_NAME,  
                     Lecture.COLUMN_NAME_DESCRIPTION,  
           };  
           Cursor c;  
           c = db.query(Lecture.TABLE_NAME, cols, Lecture.COLUMN_NAME_EVENT_ID +" = " +eventId, null, null, null, Lecture.COLUMN_NAME_NAME);  
           return c;       
      }  
      public static Cursor getAllLectures (SQLiteDatabase db) {  
           String[] cols = new String[] {  
                     Lecture.COLUMN_NAME_ID,  
                     Lecture.COLUMN_NAME_NAME,  
                     Lecture.COLUMN_NAME_DESCRIPTION,  
                     Lecture.COLUMN_NAME_DURATION  
           };  
           return db.query(Lecture.TABLE_NAME, cols, null, null, null, null, Lecture.COLUMN_NAME_EVENT_ID + " DESC");  
      }  
      /**  
       * Fetches a lecture from db  
       * @param db  
       * @param id  
       * @return Lecture object or null if no lecture found.   
       */  
      public static Lecture getLectureById (SQLiteDatabase db, long id) {  
           Cursor c = db.query(Lecture.TABLE_NAME, null, Lecture.COLUMN_NAME_ID + "=" + id, null, null, null, null);  
           Lecture lecture = new Lecture();  
           if (c.moveToNext()) {  
                lecture.buildFromCursor(c);  
                c.close();  
                return lecture;  
           }  
           return null;  
      }  
      public static ArrayList<Speaker> getSpeakersByLectureId (SQLiteDatabase db, long id) {  
           ArrayList<Speaker> speakers = new ArrayList<Speaker>();  
           String select = "SELECT * FROM " + Speaker.TABLE_NAME +" WHERE " + Speaker.COLUMN_NAME_ID +" IN ("+  
                     " SELECT " + DatabaseHelper.PAIR_SPEAKER_ID + " FROM " + DatabaseHelper.LECTURE_SPEAKER_PAIT_TABLE + " WHERE " + DatabaseHelper.PAIR_LECTURE_ID + " = " +id +")";  
           Cursor c = db.rawQuery(select, null);  
           Speaker speaker;  
           while (c.moveToNext()) {  
                speaker = new Speaker();  
                speaker.buildFromCursor(c);  
                speakers.add(speaker);  
           }  
           c.close();  
           return speakers;  
      }  
 }  

And The database helper code:

 public class DatabaseHelper extends SQLiteOpenHelper{  
      public static final String DB_NAME = "db";  
      public static final int DB_VERSION = 7;  
      public static final String LECTURE_SPEAKER_PAIT_TABLE = "lecture_speaker_pair";  
      public static final String PAIR_LECTURE_ID = "lecture_id";  
      public static final String PAIR_SPEAKER_ID = "speaker_id";  
      public DatabaseHelper(Context context, String name, CursorFactory factory,  
                int version) {  
           super(context, name, factory, version);  
      }  
      @Override  
      public void onCreate(SQLiteDatabase db) {  
           // create events table  
           StringBuilder sb = new StringBuilder();  
           try {  
           DBUtils.createTable(db, sb,   
                     Event.TABLE_NAME,  
                     Event.COLUMN_NAME_ID, "INTEGER PRIMARY KEY ",  
                     Event.COLUMN_NAME_NAME, "TEXT",  
                     Event.COLUMN_NAME_DESCRIPTION, "TEXT",  
                     Event.COLUMN_NAME_START_DATE, "TEXT",  
                     Event.COLUMN_NAME_END_DATE, "TEXT",  
                     Event.COLUMN_NAME_LOGO_URL, "TEXT",  
                     Event.COLUMN_NAME_WEBSITE_URL, "TEXT");  
           //create lectures table  
           DBUtils.createTable(db, sb,   
                     Lecture.TABLE_NAME,  
                     Lecture.COLUMN_NAME_ID, "INTEGER PRIMARY KEY ",  
                     Lecture.COLUMN_NAME_DESCRIPTION, "TEXT",  
                     Lecture.COLUMN_NAME_DURATION, "TEXT",  
                     Lecture.COLUMN_NAME_EVENT_ID, "INTEGER",  
                     Lecture.COLUMN_NAME_NAME, "TEXT",   
                     Lecture.COLUMN_NAME_SLIDES_URL, "TEXT",  
                     Lecture.COLUMN_NAME_VIDEO_URL, "TEXT",  
                     Lecture.COLUMN_NAME_YOUTUBE_ASSET_ID, "TEXT");  
           //create speakers table  
           DBUtils.createTable(db, sb,   
                     Speaker.TABLE_NAME,  
                     Speaker.COLUMN_NAME_ID, "INTEGER PRIMARY KEY",  
                     Speaker.COLUMN_FIRST_NAME, "TEXT",  
                     Speaker.COLUMN_LAST_NAME, "TEXT",  
                     Speaker.COLUMN_NAME_BIO, "TEXT",  
                     Speaker.COLUMN_NAME_IMAGE_URL, "TEXT");  
           //create lecture <-> speaker pair table  
           DBUtils.createTable(db, sb, LECTURE_SPEAKER_PAIT_TABLE,   
                     PAIR_LECTURE_ID, "TEXT" ,  
                     PAIR_SPEAKER_ID, "TEXT");  
           }  
           catch (Exception e) {  
                e.printStackTrace();  
           }  
      }  
      @Override  
      public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {  
           StringBuilder sb = new StringBuilder();  
           DBUtils.dropTable(db,sb , Event.TABLE_NAME);  
           DBUtils.dropTable(db,sb , Lecture.TABLE_NAME);  
           DBUtils.dropTable(db,sb , Speaker.TABLE_NAME);  
           DBUtils.dropTable(db,sb , LECTURE_SPEAKER_PAIT_TABLE);  
           onCreate(db);  
      }  
 }  

For further explanations about the database manipulation code i'm afraid you will have to wait for my next post which will cover everything you need to know about the Android ContentProviders

I would like to conclude this post by saying thanks again to +Ran Nachmany and +Amir Lazarovich which wrote most of the code which I used in this post and to link you to the full source code for AndConLab here: https://github.com/RanNachmany/AndconLab

I would also like to thank the great Jake Wharton who created the wonderful Action Bar Sherlock which we used here and in pretty much everything else.

הפוסט זמין גם בעברית כאן - http://iandroid.co.il/dr-iandroid/archives/14998

Royi is a Google Developer Expert for Android in 2013, a mentor at Google's CampusTLV for Android and (last but not least) the set top box team leader at Vidmind, an OTT TV and Video Platform Provider. www.vidmind.com

Monday, December 9, 2013

Modern talking - The guide to communicating with a server in Android applications

הפוסט זמין גם בעברית כאן - http://iandroid.co.il/dr-iandroid/archives/14894

Hello again,
As i stated in an earlier post, after my Hebrew blog posts started to get published on the wonderful iAndroid website i decided to transform this blog into the English edition.
Slowly but surely i'll translate my earlier posts to English and i'll start with the first one (I think you find it quite logical). 

For those of you who don't know, all of my posts until now (including this one) or derived from AndconLab; the developer code lab that  +Ran Nachmany+Amir Lazarovich and myself created last year for the great MultiScreenX convention we helped organize.

To get started, I want to make a point on the fact that when you approach a project you have a few choices to make which will dictate the server-client communication state for all times, here are a few of them:

First choice - format.
There are usually two options; JSON and XML and the key differences between them are made very clear by a short example.

Here is some data about 2 events that i helped orgenize last year:

  1.  GDG Herzeliya's April event..
  2. Google I/O 2013 reloaded.

JSON:
   {  
    "events": [  
     {  
      "name": "Herzeliya GDG April",  
      "lectures": [
            {
               title: "Google play services",
               speaker: "Royi Benyossef"
            },
            {
               title: "MVC in Android",
               speaker: "Mr. Ruslan Novikov"
            }
     },  
     {  
      "name": "Google I/O 2013 reloaded",  
      "lectures": [
            {
               title: "Google play services #2",
               speaker: "Royi Benyossef"
            },
            {
               title: "The pro-tip trilogy",
               speaker: "Ran Nachmany "
            }
     }  
    ]  
   }  
XML:
 <events>  
    <event>  
     <name>Herzliya GDG April</name>  
     <lectures>  
       <lecture>  
         <title>Google play services</title>  
         <speaker>Royi Benyossef</speaker>  
          <title>MVC in Android</title>  
         <speaker>Mr. Ruslan Novikov</speaker>  
       </lecture>  
     </lectures>  
    </event>  
    <event>  
     <name>Google I/O 2013 reloaded</name>  
     <lectures>  
       <lecture>  
         <title>Google play services #2</title>  
         <speaker>Royi Benyossef</speaker>  
          <title>The pro-tip trilogy</title>  
         <speaker>Ran Nachmany</speaker>  
       </lecture>  
     </lectures>  
    </event>  
   </events>  

I would imagine that most of you switched to reading the XML example to understand what's written there (i.e. human readability is in the pros column for the XML format), however, simply counting the amount of characters needed to represent the same amount of data in the two formats will give you the key advantage of the JSON format, but, you might be asking yourself why would i care how much characters are used?

Well, normally you wouldn't really care but since this post is about mobile server-client communication in which some (if not most) of the time, connectivity is provided by 3G which might be priced by used transportation volume and thus each char might make your application more costly to the end user, i would say you should care, don't you agree?

Right now you're probably wondering if this means that JSON is more efficient than XML, well if you were, the answer is NO

Many benchmark tests have shown that in many cases XML is more efficient but fortunately in the case of most mobile application, these states are rare and the benefit from XML is negligible.
You can read about these tests in the following links:
  1.   http://www.edwardawebb.com/tips/xml-json
  2.  http://blog.mudynamics.com/2009/05/01/json-xml-performance/


Second choice - Service, Class או Singleton.

In most applications there is a class which manages the server-client communication and it is usually designed with a subset of these requirements in mind:
  1. Must be accessible from everywhere in the application.
  2. Must perform all of its actions in the background so they will not affect the UI thread.
  3. Must be lean and not use many resources (runtime memory).
  4.  Must be able to commence and cease actions on demand as well as in other predetermined forms. 
these aforementioned requirements figuratively spell out Service which is exactly the approach we took (finally, some code :))).

So first of all, since we are trying to approach a remote server, you must ask permissions from the user (even if the user is you right now).
  <uses-permission android:name="android.permission.INTERNET"/>  

And we need to let the device and application know that we have a service by defining it in the Manifest like so:
  <service android:name="CommunicationService"/>  

And here's the basic form of the service class itself:
 public class CommunicationService extends Service{  
      public static final String RESULTS_ARE_IN = "RESULTS_ARE_IN";  
      private Thread mWorker;  
   private HttpClient mHttpClient;      
      private ResponseHandler<String> mResponseHandler;  
      @Override  
      public void onCreate() {  
           super.onCreate();  
           mHttpClient = new DefaultHttpClient();  
     HttpParams params = mHttpClient.getParams();  
     int serverConnectionTimeout = getResources().getInteger(R.integer.http_connection_timeout_in_seconds) * 1000;  
     HttpConnectionParams.setConnectionTimeout(params, serverConnectionTimeout);  
     HttpConnectionParams.setSoTimeout(params, serverConnectionTimeout);  
           mResponseHandler = new BasicResponseHandler();  
           initApiStrings();  
      }  
      @Override  
      public IBinder onBind(Intent intent) {  
           return null;  
      }  
      @Override  
      public int onStartCommand(Intent intent, int flags, int startId) {  
           if (null != mWorker && mWorker.isAlive()) {  
                //we are already running a transaction, nothing to do here  
                return Service.START_STICKY;  
           }  
           //start new job  
           mWorker = new Thread(new TransactionJob());  
           mWorker.start();  
           return Service.START_STICKY;  
      }  
 }  

Please notice a few details:
  1.  A function by the name of initApiStringsin which we used in order to sort out the API format is missing and because is post is long enough i decided against adding it but please feel free to fill in any gap by looking at the actual full version of the code here.
  2. The Service is of the START_STICKY form which pretty much states that Android will place it at the end of the queue of components to kill off in order to release resources when in need and it will restart it automatically once the crisis is everted and there are enough resources..
  3. The Service uses a thread because unlike IntentService it might interfere with the smooth operation of the main UI thread.
And now, here's the code for that  Thread which holds all functionality that has to do with; sending the request, receiving the response, and parsing the response from the format we selected (JSON) to POJO (Plain Old Java Object):

 private class TransactionJob implements Runnable {  
           private static final String TAG = "Service";  
           @Override  
           public void run() {  
                try {  
            HttpGet httpGet = new HttpGet(GET_EVENTS);  
                  String responseBody = null;  
                  try {  
                      responseBody = mHttpClient.execute(httpGet, mResponseHandler);  
                  } catch(Exception ex) {  
                    ex.printStackTrace();  
                  }  
         List<Event> events = null;  
                  if(responseBody != null) {  
           events = JacksonUtils.sReadValue(responseBody, new TypeReference<List<Event>>() {}, false);  
           if (events != null) {  
             //fire an intent  
             Intent intent = new Intent();  
                intent.setAction(RESULTS_ARE_IN);  
                CommunicationService.this.sendBroadcast(intent);  
           }  
                  }  
          } catch (Exception e) {  
               e.printStackTrace();  
          }  
           }  
      }  

For the actual parsing we chose the open source library Jackson , although we could have gone with any number of other open source solutions such as; gsonjson-simple and more.
All of these libraries are good and each has it's own strong point as you can see on any number benchmarks that were done online but as i said we went with Jackson and.... that's that.
 The code for the parsing could be found here and will require you to download the appropriate .jar file.

And... that's about all for now, my next post to be translated will cover the likes of issues such as; connecting the server responses to the UI smoothly by using response caching in 2 tiers.

I would like to conclude this post by saying thanks again to +Ran Nachmany and +Amir Lazarovich which wrote most of the code which I used in this post and to link you to the full source code for AndConLab here: https://github.com/RanNachmany/AndconLab

הפוסט זמין גם בעברית כאן - http://iandroid.co.il/dr-iandroid/archives/14894

Royi is a Google Developer Expert for Android in 2013, a mentor at Google's CampusTLV for Android and (last but not least) the set top box team leader at Vidmind, an OTT TV and Video Platform Provider. www.vidmind.com