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
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
Steps to use an
- Define a class
MyAsyncTaskthat extends the
AsyncTask<?, ?, ?>class with the three generics types being the
- Create an object of the
MyAsyncTaskclass. The constructor can be used to initialize fields of the
- In the main UI thread (and only here), call the
execute()method on the object. Provide a (possible empty) list of parameters of type
Paramsfor the input.
- Inside the
MyAsyncTaskclass, override at least the
doInBackground(Params... params)method. The return type of this function should be
paramsvariable is an array of objects of type
Paramsthat’s provided in the
- 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.
- 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.
- Additionally, before
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:
- Create a
new ServerSocket(port)on the server side to listen to incoming connections.
- Create a
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
Socket(String host, int port)constructor also connects the sockets automatically (if no exception is thrown).
- Sockets talk with each other with streams. To send something out from a
Socket, grab a reference
new PrintWriter(new BufferedOutputStream(socket.getOutputStream())), and use
pOut.println(str); pOut.flush()to put the string
- To read information from the peer
Socket, grab a reference
new BufferedReader(new InputStreamReader(new BufferedInputStream(socket.getInputStream()))), and read strings from
pInline by line using
pIn.readLine(). You can also read bit by bit if your custom communication protocol needs so.
- 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
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:
- Define a contract class that extends
BaseColumnsto hold string constants for table names, column names, etc.
- 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 + “)”;
- Define a helper class that extends
SQLiteOpenHelperand override the
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion), and
onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion)methods.
- Call the super constructor
super(context, DATABASE_NAME, null, DATABASE_VERSION)to setup the database helper.
- To obtain a reference to the actual SQLite database, instantiate a variable
dbHelperof the helper class and use
dbHelper.getWritableDatabase()to get an instance, say
Querying A Database
SELECT statement in SQL is performed by the
query method which takes eight parameters:
Stringfor the table name.
Stringarray of the column names that you want to query.
WHEREclause string, for example
ID LIKE 3. The number can be replaced by a question mark
?and replaced with the elements in the fourth parameter.
Stringarray with equal numbers of elements, each of which fills in a question mark
?in the previous string.
GROUP BYclause string.
ORDER BYclause string.
Once the above parameters are defined, a call to the
query methods would return a
cursor that contains the query result. Then you need to parse the object to obtain the information. The steps are:
- Check if
- Move to the first of the entry in
- Then iterate over
- 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 statement in SQL is performed by the
update method which takes four parameters:
Stringfor the table name.
ContentValuesobject holding maps from table column names to values you want to update.
- The arguments to the question marks in the
The execution of this method would return an integer representing the number of rows affected.
DELETE statement in SQL is performed by the
delete method which takes three parameters (number 1, 3, 4 of those of the
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:
db.beginTransaction();to start the transaction.
- Do the usual database operations, which may be too many and a slow process. Enclose this part in a
db.endTransaction()in the catch statement.
db.setTransactionSuccessful();to mark the successful end of database operations.
- After the
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
setTransactionSuccessful()is committed to the database.
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