This is Part II of my notes on what I’ve learned on Android programming. See Part I here for a set of things that are more essential to a simple Android program.

Networking With Java Sockets

AsyncTask

Android has a design rule that some tasks cannot be performed in the main UI thread. One of such cases is networking, which I happen to have to used a lot in my project. One solution provided by the Android development framework is to use AsyncTask.

Steps to use an AsyncTask:

  1. Define a class MyAsyncTask that extends the AsyncTask<?, ?, ?> class with the three generics types being the Params, Progress, and Return.
  2. Create an object of the MyAsyncTask class. The constructor can be used to initialize fields of the MyAsyncTask class.
  3. In the main UI thread (and only here), call the execute() method on the object. Provide a (possible empty) list of parameters of type Params for the input.
  4. Inside the MyAsyncTask class, override at least the doInBackground(Params... params) method. The return type of this function should be Return. The params variable is an array of objects of type Params that’s provided in the execute() method.
  5. In the doInBackground(Params... params) method, the publishProgress(Progress...) method can be called so that another overridden method onProgressUpdate(Progress...) is called automatically to perform some UI feedback on the main UI thread. For example, you can grab a reference to a view of the calling activity and make some updates in this thread.
  6. If code is needed for finishing up the execution of the doInBackground(Params...) method, then another method onPostExecute(Result) can be overridden. What I use in my project is often to perform some potentially long running background tasks and then show the user an update of something. Here is the place to do so.
  7. Additionally, before doInBackground(Params...) executes, onPreExecute() can be used to set up some field variables or preparation code.

Using Java Sockets

Java sockets in Android are supported almost identically as other Java programs.

Steps of using a Java socket in Android:

  1. Create a ServerSocket with new ServerSocket(port) on the server side to listen to incoming connections.
  2. Create a Socket with new Socket(hostname, port) on the client side, where the hostname is the IP address of the server (as a String), and the port is the one used to create the ServerSocket. The Socket(String host, int port) constructor also connects the sockets automatically (if no exception is thrown).
  3. Sockets talk with each other with streams. To send something out from a Socket, grab a reference pOut to new PrintWriter(new BufferedOutputStream(socket.getOutputStream())), and use pOut.println(str); pOut.flush() to put the string str out.
  4. To read information from the peer Socket, grab a reference pIn to new BufferedReader(new InputStreamReader(new BufferedInputStream(socket.getInputStream()))), and read strings from pIn line by line using pIn.readLine(). You can also read bit by bit if your custom communication protocol needs so.
  5. Always a good habit to close the sockets with the close() methods first on input and output streams and then on the sockets.

Database with SQLite

Database Creation

Android has full support for the SQLite database. Actually using SQLite databases in Android is one of the easiest part with enough caution.

Steps for creating a SQL database:

  1. Define a contract class that extends BaseColumns to hold string constants for table names, column names, etc.
  2. Define some SQL strings that would be executed by the execSQL(String sql) methods for creating, updating, and downgrading the database. One such string would be like the following: <pre class="brush: java; title: ; notranslate" title="">private static final String SQL_CREATE_MEATINFO_TABLE = “CREATE TABLE “ + DatabaseContract.MetaInfoColumns.TABLE_NAME + “(“ +
    DatabaseContract.MetaInfoColumns._ID + “ INTEGER PRIMARY KEY” + COMMA_SEP +
    DatabaseContract.MetaInfoColumns.COLUMN_NAME_START_TIME + LONG_TYPE + COMMA_SEP + DatabaseContract.MetaInfoColumns.COLUMN_NAME_END_TIME + LONG_TYPE + “)”;

</pre>

  1. Define a helper class that extends SQLiteOpenHelper and override the onCreate(SQLiteDatabase db), onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion), and onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) methods.
  2. Call the super constructor super(context, DATABASE_NAME, null, DATABASE_VERSION) to setup the database helper.
  3. To obtain a reference to the actual SQLite database, instantiate a variable dbHelper of the helper class and use dbHelper.getWritableDatabase() to get an instance, say db.

Querying A Database

The usual SELECT statement in SQL is performed by the query method which takes eight parameters:

  1. A String for the table name.
  2. A String array of the column names that you want to query.
  3. A WHERE clause string, for example ID LIKE 3. The number can be replaced by a question mark ? and replaced with the elements in the fourth parameter.
  4. A String array with equal numbers of elements, each of which fills in a question mark ? in the previous string.
  5. The GROUP BY clause string.
  6. The HAVING clause string.
  7. The ORDER BY clause string.
  8. The LIMIT clause string.

Once the above parameters are defined, a call to the query methods would return a Cursor object cursor that contains the query result. Then you need to parse the object to obtain the information. The steps are:

  1. Check if cursor is null.
  2. Move to the first of the entry in cursor with cursor.moveToFirst().
  3. Then iterate over cursor until cursor.moveToNext() gives false.
  4. For each iteration, use something like cursor.getString(cursor.getColumnIndex(DatabaseContract.EntryColumns.COLUMN_NAME_NAME))) to obtain the necessary information. Note that the return types would depend on the corresponding column types when you create the table.

Update Entries

The UPDATE statement in SQL is performed by the update method which takes four parameters:

  1. A String for the table name.
  2. A ContentValues object holding maps from table column names to values you want to update.
  3. A WHERE clause string.
  4. The arguments to the question marks in the WHERE clause.

The execution of this method would return an integer representing the number of rows affected.

Deleting Entries

The DELETE statement in SQL is performed by the delete method which takes three parameters (number 1, 3, 4 of those of the update method).

Database Transactions

There are several reasons transactions should be used for Android SQLite operations. The reason for me is the need for speed. Using transactions is actually very simple. The steps are:

  1. Use db.beginTransaction(); to start the transaction.
  2. Do the usual database operations, which may be too many and a slow process. Enclose this part in a try-catch statement. Put db.endTransaction() in the catch statement.
  3. Use db.setTransactionSuccessful(); to mark the successful end of database operations.
  4. After the try-catch statement, call db.setTransactionSuccessful(); again. The magic of database transactions are that, if endTransaction() is called before setTransactionSuccessful(), then the transaction is considered erroneous and rolled back; otherwise, every database operation between beginTransaction() and setTransactionSuccessful() is committed to the database.

Using the sqlite3 Utility

For debugging purposes, it is sometimes handy to see what’s actually stored in the SQLite database file. The file is usually located at /data/data/<App-Package>/databases/<db-name> on the Android device. However, the folder /data is only accessible to root users. It cannot directly be copied out by the File Explorer of the Eclipse IDE. adb pull does not help either. I have rooted my device so it is possible to make a copy of the file to some other places on the device and then use the Eclipse File Explorer to get the file. Though a little inconvenient, but it works.

Once you’ve got the file, there is a utility called sqlite3 that can be used to look at the data in the database. Simply use sqlite3 database.db to load the file, and there are a handy list of commands you can use. Get information on the commands with a .help command inside sqlite3.