Showing posts with label WorxForUs. Show all posts
Showing posts with label WorxForUs. Show all posts

Wednesday, April 23, 2014

Android Networking Example and Tutorial (with easy retries)

This post explains how to use the WorxForUs Network framework to have an Android app that works robustly even in areas that have poor network connectivity. 

Background:

I wrote this library because none of the tutorials I found went clearly into the different types of faults that you can experience when developing web apps.  They worked fine to get you started, but what about if you are now on a mobile network and not all of your packets are getting through?  What happens if you socket times out?  What do I do if I need to handle cookies?  This framework is an attempt to address those issues along with adding a simple way to retry communications that did not go through properly.

Update:

A sample project written using Eclipse is available on GitHub.  It shows the most basic usage of the WorxForUs Android network framework.

Features:

  • Automatic cookie handling
  • Baked in network retries (just specify number of retries to allow before failing)
  • Easy to understand handling of the various types of network errors
  • Handles authenticated and unauthenticated HTTP requests

Accessing the Network with Retries (without authentication):

First, download the WorxForUs framework from the github page (or clone here).
Import the project into your Eclipse or Android Studio.

This code all needs to be wrapped in a Thread or AsyncTask, otherwise you can expect to get either an app not responding error or an android.os.NetworkOnMainThreadException, because network tasks are long running and should not be run on the main thread.

Check to see if the network is connected (you don't want to try and download something if the user has activated flight mode).

    //NetResult is an object to store the results
    NetResult netResult = null; 
    String url = "http://www.google.com/";
    //Load the values being posted
    List<NameValuePair> params = new ArrayList<NameValuePair>();    params.add(new BasicNameValuePair("q", "bears"));

    String serverResponse ="";
    //If the network is not currently connected, don't try to talk
    if (NetHandler.isNetworkConnected(con)) {
        netResult = NetHandler.handlePostWithRetry(url, params, NetHandler.NETWORK_DEFAULT_RETRY_ATTEMPTS);

        //get the server response as a string
        serverResponse = Utils.removeUTF8BOM(EntityUtils.toString(net_result.net_response_entity, Utils.CHARSET));
        //Notify the HTTP client that all data was read
        netResult.closeNetResult();
    }
What is this NetResult object ?  This object contains all the information you need to decode the response from the webserver. If netResult.net_response_entity is not null, then that is the response from the server.  Send that value to your handling routing.

    NetResult.net_success - Equals true when the server was successfully contacted (this says nothing about if your request was valid though).
    NetResult.net_error - Contains the error message or exception associated with the connection
    NetResult.net_error_type - Contains the name of the type of error that occurred (ie. HttpResponseException, SocketTimeoutException, SocketException, or IOException)
    NetResult.net_response_entity - This is the actual response from the server.  A common use is to run:
String consume_str = Utils.removeUTF8BOM(EntityUtils.toString(net_result.net_response_entity, Utils.CHARSET)); 
or if you want to capture JSON data
NetResult.handleGenericJsonResponseHelper(net_result, this.getClass().getName()); 
Note: the class name is passed for logging purposes only
Now net_result.object will contain your JSON objects parsed for you

After reading your data from the netResult.net_response_entity, you will need to call netResult.closeNetResult().  This function eventually calls HttpEntity.consumeContent() which releases any resources associated with that object.


Accessing the Network with Authentication:

 To call the network with authentication is simple once you have the authentication helper configured for your particular webserver.  Alternatively, you can also just load the login parameters to most websites by putting the correct variables in POST parameters.





// load authentication data
if (!AuthNetHandler.isAuthenticationSet()) {
    // passing the context here allows the system to update the preferences with a validated usernumber (if found)
    AuthNetHandler.setAuthentication(host, new MyAuthenticationHelper(con));
    NetAuthentication.loadUsernamePassword(username, password);
}
// if network is ready, then continue
// check if network was disabled by the user

if (NetHandler.isNetworkConnected(context)) {
    // if user has credentials - upload then download so data is not lost
    if (NetAuthentication.isReadyForLogin()) {
        netResult = AuthNetHandler.handleAuthPostWithRetry(url, params, num_retries);
        //...handle the netResult response here
        netResult.closeNetResult();
    } else {
        Log.e(this.getClass().getName(), "Did not attempt to login, no authentication info");
    }
}


When you have authenticated requests there are a few extra steps before you call  AuthNetHandler.handleAuthPostWithRetry(...).

First you will need to implement a class from NetAuthenticationHelper.  This will tell the framework how you are going to login, what messages are errors,  and basically how you are validating a login.   It may seem like a lot of extra code, but try to stick with it.  Having all your authentication code in one place can be extremely helpful.

public class MyAuthHelper implements NetAuthenticationHelper {
    @Override
    public String getLoginURL(String host) {
        return "https://myweb/remote_login_address.php";
    }


getLoginURL is simple the location where you expect the app to connect to when sending login parameters.  Just put the URL you need in here.

    @Override
    public void markAsLoginFailure(NetResult result) {

        result.object = new String("Login Error");
    }


or, in my case I use JSON objects

    @Override
    public void markAsLoginFailure(NetResult result) {
        try {
            result.object = new JSONObjectWrapper("Jsonstring");
        } catch (JSONExceptionWrapper e) {
            throw new RuntimeException("Could not parse default login failed JSON string.");
        }
    }

Put whatever your webserver responds with in here on a failed user login.  This section forces a result to be simulated as a login failure in the result.object variable.  Let's say you've identified a failed login, this output gets sent through validateLoginResponse(...) where the failed login will be identified.

    @Override
    public void markAsLoginSuccessFromCache(NetResult result) {

        result.object = new String("Login Successful");
    }


Put whatever your webserver could respond with on a successful login.  This section forces a result to be simulated as a login success in the result.object variable.

    @Override
    public String getLoginErrorMessage() { return "Could not login user"; }


Here put what you would like be passed as the error message to netResult.net_error when a user is not able to be logged in.

    @Override
    public int validateLoginResponse(NetResult netResult) {
        //Returns NetAuthentication.NO_ERRORS, NETWORK_ERROR, or SERVER_ERROR
        //Check the login server result from netResult.object
        //to determine if your login was successful or not
        if (netResult.net_success) {
            String response = netResult.object;
            if (response.contains("not logged in indication")) {
                return NetAuthentication.NOT_LOGGED_IN;
            } else if (response.contains("login error indication"))) {
                return NetAuthentication.LOGIN_FAILURE;
            } else if (response.contains("cant parse data indication"))) {
                return NetAuthentication.SERVER_ERROR;
            }
        } else {
            return NetAuthentication.NETWORK_ERROR;
        }
        return NetAuthentication.NO_ERRORS;    
    }

validateLoginResponse(...)performs the bulk of the login checking, it determines if the user wasn't logged in, there was a login error (ie. wrong password), a server error, or network error.  Depending on what you expect from the server, you will send a response of NetAuthentication.NO_ERRORS, NETWORK_ERROR, or SERVER_ERROR.

    @Override
    public int peekForNotLoggedInError(NetResult netResult) {

        //... check the netResult.object for a login failure on a page that wasn't the login page.
    }


Similarly to the previous function, the peekForNotLoggedInError(...) checks for login errors, but on pages that are not the login page.  Consider the example where you have already logged in, but then check a different page to download some other data.  If your user's session is suddenly logged out, you will get an error that could look different than the one you get on the login page.  So that specific logic for unexpected login failure goes in here. 

    @Override
    public NetResult handleUsernameLogin(String host, String username, String password) {
        List<NameValuePair> params = new ArrayList<NameValuePair>();
        params.add(new BasicNameValuePair("uname_field", "username"));
        params.add(new BasicNameValuePair("pword_field", "password"));
       
        NetResult netResult = NetHandler.handlePostWithRetry(this.getLoginURL(host), params , NetHandler.NETWORK_DEFAULT_RETRY_ATTEMPTS);
        //save the result and close network stream
        consume_str = Utils.removeUTF8BOM(EntityUtils.toString(result.net_response_entity, Utils.CHARSET));
        netResult.object = consume_str;
        netResult.closeNetResult();
        return netResult;
    }


The handleUsernameLogin(...) function provides the actual fields and logic needed to send the request to the webserver.  Simply fill in your specific fields for login.

If you have a different request using a token, the handleTokenLogin(...) function can be used for that purpose.

Wow, if you've made it to the end of this tutorial, you are a real trooper and I salute you!

Notes:

HTTP Params are encoded with UTF-8.  If your webserver expects another character set, you will need to change the handlePost(...) routing in NetHandler to use that encoding scheme.

Permissions: Obviously you will need to have the correct permissions in your app or you will get a permission exception.  These must be in your manifest file:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>





Friday, March 21, 2014

Android Database Example and Tutorial (thread safe version)

This post explains how to use the WorxForUs SQLite Database framework to have an Android app that works well in multi-threaded applications and supports table level upgrades (instead of the entire database). 

Background:

I wrote this library because I my app was based on the Android examples and worked on the device I had at the time (a Galaxy S), but suddenly was having problems when working on newer devices.  After much searching, I found out that the new devices were multi-core and was accessing the database in multiple places in the program.  I didn't have that problem with the earlier devices because they were single-threaded and so the database access was naturally serialized.  The Android samples were good, but didn't go far enough to address the problems I was seeing.

Typical Classic Style Database access exceptions:

This framework corrects some of the following issues that are commonly seen once your app starts getting more complicated and calls the database from multiple locations or background threads.

Android 4
java.lang.IllegalStateException: Cannot perform this operation because the connection pool has been closed.
android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)

Android 2.3.3
java.lang.IllegalStateException: database not open
android.database.sqlite.SQLiteException: database is locked
Failed to setLocale() when constructing, closing the database

Accessing a SQLite database in a thread-safe manner:

First, download the WorxForUs framework from the github page (or clone here).
Import the project into your Eclipse or Android Studio.

Create your new project: right-click and select 'New / Android Application Project'.

I'm going to name it: 'WorxforusDbSample' and create the project using defaults for the remaining options. 

Once the project is loaded, you will need to add a reference to the worxforus_library project from Properties / AndroidCreate a new class in com.example.worxforusdbsample called Nugget and enter the following code.

package com.example.worxforusdbsample;

public class Nugget {
    String type="";
    int id =0;
  
    public static final String IRON = "Iron";
    public static final String GOLD = "Gold";
    public static final String DIAMOND = "Diamond";

    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }
    public void setType(int type) {
        if (type == 1)
            setType(GOLD);
        else if (type == 2)
            setType(DIAMOND);
        else //set remaining to Iron
            setType(IRON);
    }
  
    public int getId() {
        return id;
    }
  
    public void setId(int id) {
        this.id = id;
    }
  
    public String getDescription() {
        return type+" nugget";
    }
}


Creating your object is the easy part, now create the association with the database.  Create a new class called NuggetTable and extend from the abstract class TableInterface<Nugget>.   Extending from TableInterface allows us to us the TableManager which is what serializes access to the database and prevents from multiple threads colliding when accessing the data.  Get the sample app code here.

package com.example.worxforusdbsample;

import java.util.ArrayList;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.util.Log;

//Result is a convenience class to capture errors and pass objects back to the caller
import com.worxforus.Result;
import com.worxforus.db.TableInterface;

public class NuggetTable extends TableInterface<Nugget> {
    public static final String DATABASE_NAME = "sample_db"; //Instead of a text string, this should be a static constant for your app
    public static final String TABLE_NAME = "nugget_table";
    public static final int TABLE_VERSION = 1;
    // 1 - Initial version

    static int i = 0; // counter for field index
    public static final String NUGGET_ID = "nugget_id"; // int
    public static final int NUGGET_ID_COL = i++;
    public static final String NUGGET_TYPE = "nugget_type"; // String
    public static final int NUGGET_TYPE_COL = i++;

    private static final String DATABASE_CREATE = "CREATE TABLE " + TABLE_NAME + " ( "
            + NUGGET_ID + "     INTEGER PRIMARY KEY AUTOINCREMENT,"
            + NUGGET_TYPE + "   TEXT"
            + ")";

    private SQLiteDatabase db;
    private NuggetDbHelper dbHelper;

    public NuggetTable(Context _context) {
        dbHelper = new NuggetDbHelper(_context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public Result openDb() {
        Result r = new Result();
        try {
            db = dbHelper.getWritableDatabase();
        } catch (SQLException e) {
            Log.e(this.getClass().getName(), r.error);
            throw(new RuntimeException(e));
        }
        return r;
    }

    @Override
    public void closeDb() {
        if (db != null)
            db.close();
    }

    @Override
    public void createTable() {
        dbHelper.onCreate(db);
    }

    @Override
    public void dropTable() {
        db.execSQL("DROP TABLE IF EXISTS "+TABLE_NAME);
        invalidateTable();
    }

    public void wipeTable() {
        synchronized (TABLE_NAME) {
            db.delete(TABLE_NAME, null, null);
        }
    }
   
    @Override
    public void updateTable(int last_version) {
        dbHelper.onUpgrade(db, last_version, TABLE_VERSION);
    }

    @Override
    public String getTableName() {
        return TABLE_NAME;
    }

    @Override
    public int getTableCodeVersion() {
        return TABLE_VERSION;
    }

    /**
     * For ease of use, not efficiency, I combined insert and update as a single statement.  Note that if the item exists,
     * that two operations are performed, a delete and insert.
     */
    @Override
    public Result insertOrUpdate(Nugget t) {
        synchronized (TABLE_NAME) {
            Result r = new Result();
            try {
                ContentValues cv = getContentValues(t);
                r.last_insert_id = (int) db.replace(TABLE_NAME, null, cv);
            } catch( Exception e ) {
                Log.e(this.getClass().getName(), e.getMessage());
                r.error = e.getMessage();
                r.success = false;
            }
            return r;
        }
    }
   
    public Result insert(Nugget t) {
        synchronized (TABLE_NAME) {
            Result r = new Result();
            try {
                ContentValues vals = new ContentValues();
                if (t.getId() > 0)
                    vals.put(NUGGET_ID, t.getId());
                vals.put(NUGGET_TYPE, t.getType());
                r.last_insert_id = (int) db.insert(TABLE_NAME, null, vals);
            } catch( Exception e ) {
                Log.e(this.getClass().getName(), e.getMessage());
                r.error = e.getMessage();
                r.success = false;
            }
            return r;
        }
    }

    @Override
    public Result insertOrUpdateArrayList(ArrayList<Nugget> t) {
        return null; //not implemented in this sample
    }

    public Result insertArrayList(ArrayList<Nugget> list) {
        Result r = new Result();
        db.beginTransaction();
        for (Nugget item : list) {
            try {
                insert(item);
            } catch(SQLException e ) {
                Log.e(this.getClass().getName(), e.getMessage());
                r.error = e.getMessage();
                r.success = false;
            }
        }
        db.setTransactionSuccessful();
        db.endTransaction();
        return r;
    }
   
    @Override
    public ArrayList<Nugget> getUploadItems() {
        return null; //not implemented in this sample
    }

    public ArrayList<Nugget> getAllEntries() {
        ArrayList<Nugget> al = new ArrayList<Nugget>();
        Cursor list = getAllEntriesCursor();
        if (list.moveToFirst()){
            do {
                al.add(getFromCursor(list));
            } while(list.moveToNext());
        }
        list.close();
        return al;
    }
   
    protected Cursor getAllEntriesCursor() {
        return db.query(TABLE_NAME, null, null, null, null, null, NUGGET_ID);
    }
   
    // ================------------> helpers <-----------==============\\

    /** returns a ContentValues object for database insertion
     * @return
     */
    public ContentValues getContentValues(Nugget item) {
        ContentValues vals = new ContentValues();
        //prepare info for db insert/update
        vals.put(NUGGET_ID, item.getId());
        vals.put(NUGGET_TYPE, item.getType());
        return vals;
    }
   
    /**
     * Get the data for the item currently pointed at by the database
     * @param record
     * @return
     */
    public Nugget getFromCursor(Cursor record) {
        Nugget c= new Nugget();
        c.setId(record.getInt(NUGGET_ID_COL));
        c.setType(record.getString(NUGGET_TYPE_COL));
        return c;
    }
   
    // ================------------> db helper class <-----------==============\\
    private static class NuggetDbHelper extends SQLiteOpenHelper {
        public NuggetDbHelper(Context context, String name,
                CursorFactory factory, int version) {
            super(context, name, factory, version);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL(DATABASE_CREATE);
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion,int newVersion){

            // called when the version of the existing db is less than the current
            Log.w(this.getClass().getName(), "Upgrading table from " + oldVersion + " to " + newVersion);        }
    }
   
}


Code for the main activity

To connect to the database, first establish a NuggetTable object in your app or activity onCreate method.  You may want to store this connection in a singleton for easy access by any other activity (and also to reduce memory usage and connection time).

public class NuggetDbActivity extends ActionBarActivity {
    NuggetTable nuggetTable;
    static final int NUM_ITEMS_TO_CREATE = 5;
    static final int NUM_THREADS_TO_RUN = 10;
   
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        nuggetTable = new NuggetTable(this);
        //Your activities setup code...
    }


Once the NuggetTable object is ready, then you use the TableManager to serialize access to the database.

    public void addRandomDataWorxForUs(NuggetTable table) {
        TableManager.acquireConnection(this, NuggetTable.DATABASE_NAME, table);
        Nugget nugget = new Nugget();

        Result r = table.insert(nugget);
        TableManager.releaseConnection(nuggetTable);
    }


The TableManager.acquireConnection does a number of things, it checks to see if your table has already been created or if you have marked it for upgrade (ie. increased the TABLE_VERSION and if so, will run your onUpgrade code).  Otherwise it just creates a new table and locks its use by only the current thread.

In addition to the table object we want to lock for our use, a context is passed that is needed to initialize the database connection.  The database name is also passed to the acquire method so that the method knows which database to use for storing the table meta information such as the current version of the table and sync information.

Finally when all the data operations are completed, you will need to release the connection held by the TableManager to allow other methods to access the database.  If you forget to release the connection, you will quickly realize it because your app will hang the next time you try to access the database.

That's all there is to it for simple database access that works in a thread safe manner.  Ok, it was a lot of code, but you get the point!

Upgrading Tables

Let's say you have released an app to the Google Play store and now you realize you need to add an index to speed up database access or maybe you need too add another field to store more data.

Add a new field to the existing database, modify your NuggetDbHelper.onUpgrade code:
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    // called when the version of the existing db is less than the current
    Log.w("SampleW4UsDb", "Upgrading table from "+oldVersion+" to "+newVersion);

    if (oldVersion < 2) { //this means the current version is 1 or less
        //EXAMPLE: add field and change the index
        db.execSQL("ALTER TABLE "+TABLE_NAME+" ADD COLUMN "+NEW_COLUMN+" "+NEW_COLUMN_TYPE);
        db.execSQL("DROP INDEX IF EXISTS "+INDEX_1_NAME); //remove old index
        db.execSQL(INDEX_1); //add a new index
        Log.d(
"SampleW4UsDb", "Adding new field and new index to "    + DATABASE_TABLE + " Table");
    }

}


And change the line for TABLE_VERSION to 2
    public static final int TABLE_VERSION = 2;
 

Now the next time TableManager.acquireConnection is run on this table, it will see that the existing table is version 1 (if it hasn't been updated yet) and proceed to run the update code which will change it to version 2.  In this case it will modify the table to include the new field and also add a new index into the table automatically.  You never have to worry about checking that a table was already created or checking the version, the framework handles all of that for you.

If you've found this post helpful, please take a moment to add a comment, +1, or a funny iguana picture.  Thanks!

Related Work

The source code to this example application can be found on github.
https://github.com/RightHandedMonkey/WorxForUsDb_Samples

The Checklists ToGo app uses this framework as a basis for database and network access.  For a complete example, check out the Checklists ToGo API which uses the more complicated features of the framework.
https://github.com/RightHandedMonkey/CTG_API


Friday, March 14, 2014

Introducing the WorxForUs Open Source Database & Network Helper for Android


WorxForUs Android Framework (Database and Network helper tools)


This article is an introductory guide to the Android helper library by WorxForUs.  The Android SDK has a lot of built in functionality, but there are certain things that it does not take care of for you.  The API guides only get you so far.  This framework is an attempt to pick up where the Android Samples leave off and address some of the trickier issues.  Hopefully you will find features in this library to help your code become more robust.

Major Features of the WorxForUs Android Library:


  • Database management (How To)
    • Serialized access framework for writing thread safe (multi-threaded) applications
    • Allows upgrade on a per table level instead of per database
    • Supports methods to keep track of data synchronization to a remote server

  • Network Tools (How To)
    • Allows easy detection and handling of network errors
    • Built in support for cookies
    • Allows easy use of network retry mode (ie. will attempt 5 times before returning an error)
    • Deals with authenticated and non-authenticated network requests 

  • Obscuring Shared Preferences Data (How To) - com.worxforus.ObscuredSharedPreferences
    • Shared data is normally stored in plaintext.  This tool easily encrypts your SharedPreferences data.
  • Object Pools - com.worxforus.Pool 
    • Put objects into a pool to keep memory usage low and minimize object creation

 

Installation

There are two ways to use the library.
1. Clone the source code at the github WorxForUs Library and import into your IDE.  Link the WorxForUs Library project to your project.
2. Download the jar file from github and include in your project/lib files.

If you are using the Network Tools, you may want to get the latest Apache HttpClient package from their download page here.  You will want the httpcore-4.x.x.jar, httpclient-4.x.x.jar, httpclient-cache-4.x.x.jar, and httpmime-4.x.x.jar.  Import these into your referenced jar library.  The latest binary release as of this writing is the 4.3.3 package.

Why was this Framework created? 

Aside from the typical utilities one generally collects or creates when writing software for a long time, I had a few specific needs for robust database access and network handling.

Database Management

A typical example of using a SQLite database in Android shows that you extend the SQLiteOpenHelper.  This is good, but what if you have several tables each with their own version?  Also, the sample projects show doing a dbHelper.getWritableDatabase(), but do not say anything about how to handle multiple threads contending for the same resource.  This may work fine on single core CPU devices, but suddenly your app may crash on a multi-Core Nexus 7 because your database access is not serialized. This package use a Singleton to provide access to the SQLite database and Semaphores to provide the locking which by default serializes the database access and prevents many common database exceptions from occurring.

Network Tools

I needed to handle cookies and detect when network connections were failing and identify what type of failure occurred.  Was the problem a server failure or a bad connection on the user device?  In a mobile network errors are the norm and it is important to handle them correctly.  I also wanted a way to easily retry a connection several times until it was successful.  This package is based on the Apache HttpClient because it easily supported the things I needed such as cookies and error detection.  Future versions are planned to use the Velocity framework.


*Additional Source of code, including but not limited to:
The Android Open Source Project - for the Base64Support code.
Obscuring Shared Preferences - much of the code for this came from help by emmby at http://stackoverflow.com/questions/785973/what-is-the-most-appropriate-way-to-store-user-settings-in-android-application/6393502#6393502