Gourmet Java technology for Android applications

Implement concurrency, networking, and database access in Android

Java™ language is the tool of choice for Android developers. The Android runtime uses its own virtual machine, Dalvik, which is not the usual Java virtual machine that most Java developers are used to. Dalvik supports most of the features in the Java programming language—but not all of them. In this article you will learn advanced Java features and how they are implemented on Android. This includes features such as concurrency, networking, and database access.

Share:

Michael Galpin, Software architect, eBay

Michael Galpin's photoMichael Galpin is an architect at eBay. He is a frequent contributor to developerWorks. He has spoken at various technical conferences, including JavaOne, EclipseCon, and AjaxWorld. To get a preview of what he is working on next, follow @michaelg on Twitter.



15 July 2010

Also available in Chinese Russian Japanese

Getting started

This article covers some of the Android SDK tools for dealing with tricky situations. To develop Android applications, you will need the latest Android SDK which requires a Java Development Kit (JDK). I used Android 2.2 and JDK 1.6.0_17 (see Resources for links to these tools). It is not required that you have a physical device; all of the code in this article will run fine on the Android emulator that comes with the SDK. You should be familiar with Android programming as this article will not cover basic Android development, but you can probably follow along if you have knowledge of the Java programming language.


Concurrency and networking

Frequently used acronyms

  • API: Application Programming Interface
  • SQL: Structured Query Language
  • SDK: Software Developer Kit
  • UI: User Interface
  • XML: Extensible Markup Language

One of the most common tasks for Android applications is to retrieve or send data over the network to a remote server. Often the result of this operation is some new data that you want to display to the user. That means you need to modify the user interface. Most developers know that you should not perform a potentially long-running task, such as accessing data over the network (especially on mobile phones that might have a very slow network connection), on the main UI thread. Doing so will freeze your application until the long-running task completes. In fact, if this task takes more than five seconds, the Android operating system will reward your application with the infamous Application Not Responding dialog in Figure 1.

Figure 1. Android's infamous Application Not Responding dialog
Screen capture of Android's infamous Application Not Responding dialog

You never know just how slow the user's network connection might be. To avoid tempting fate, you must perform tasks like these on a different thread, or at least not on the main UI thread. Many, if not most, Android applications need to deal with multiple threads and thus concurrency. Applications often need to persist data locally, and Android's local database is an appealing option. In all three of these scenarios (different threads, concurrency, and persisting data locally), there are some standard ways to do these things in a Java environment. However, as you will see, Android offers some different options. Let's take a look at each of these, as well as their pros and cons.


The Android network

Making a call over the network in Java programming is straightforward. The familiar java.net package contains several classes for doing this. Most of these classes are also present in Android, and, in fact, you can use classes like java.net.URL and java.net.URLConnection just like you would in any other Java application. However, Android includes the Apache HttpClient library. This is the preferred way to do networking on Android. Even if you use the usual Java classes, Android's implementations will still use the HttpClient. Listing 1 shows an example of using this essential library. (See Downloads for all of the source code.)

Listing 1. Using the Http Client library on Android
private ArrayList<Stock> fetchStockData(Stock[] oldStocks) 
    throws ClientProtocolException, IOException{
    StringBuilder sb = new StringBuilder();
    for (Stock stock : oldStocks){
        sb.append(stock.getSymbol());
        sb.append('+');
    }
    sb.deleteCharAt(sb.length() - 1);
    String urlStr = 
        "http://finance.yahoo.com/d/quotes.csv?f=sb2n&s=" + 
                sb.toString();
    HttpClient client = new DefaultHttpClient();
    HttpGet request = new HttpGet(urlStr.toString());
    HttpResponse response = client.execute(request);
    BufferedReader reader = new BufferedReader(
            new InputStreamReader(response.getEntity().getContent()));
    String line = reader.readLine();
    int i = 0;
    ArrayList<Stock> newStocks = new ArrayList<Stock>(oldStocks.length);
    while (line != null){
        String[] values = line.split(",");
        Stock stock = new Stock(oldStocks[i], oldStocks[i].getId());
        stock.setCurrentPrice(Double.parseDouble(values[1]));
        stock.setName(values[2]);
        newStocks.add(stock);
        line = reader.readLine();
        i++;
    }
    return newStocks;
}

In this code, you take in an array of Stock objects. These are basic data structure objects that hold information about a stock that a user owns (such as symbol, price, and so on) along with more personal information such as how much the user paid for it. You retrieve dynamic data from Yahoo Finance (for example, the current price of the stock) using the HttpClient class. The HttpClient takes an HttpUriRequest and, in this case, you use HttpGet, a subclass of HttpUriRequest. Similarly, there is an HttpPost class for times when you need to post data to a remote server. Once you get the HttpResponse from the client, you can get to the underlying InputStream of the response, buffer it, and parse it to get the stock data.

Now that you have seen how to retrieve data over the network, look at how to use this data to smartly update the Android UI using multi-threading.


Android concurrency in practice

If you run the code in Listing 1 on the main UI thread of an application, it might cause an Application Not Responding dialog to appear, depending upon the speed of the user's network. So be sure to spawn a thread to fetch this data. Listing 2 shows one way to do this.

Listing 2. Naïve multithreading (don't do this, it won't work!)
private void refreshStockData(){
    Runnable task = new Runnable(){
        public void run() {
            try {
                ArrayList<Stock> newStocks = 
                    fetchStockData(stocks.toArray(
                                  new Stock[stocks.size()]));
                for (int i=0;i<stocks.size();i++){
                    Stock s = stocks.get(i);
                    s.setCurrentPrice(
                                  newStocks.get(i).getCurrentPrice());
                    s.setName(newStocks.get(i).getName());
                    refresh();
                }
            } catch (Exception e) {
                Log.e("StockPortfolioViewStocks", 
                            "Exception getting stock data", e);
            }
        }
    };
    Thread t = new Thread(task);
    t.start();
}

The caption on Listing 2 states that it is naïve code, and indeed it is. In this simple example, you call the fetchStockData method from Listing 1 by wrapping it in a Runnable object and executing it on a new thread. In this new thread, you then access stocks, a member variable of the enclosing Activity (the class that creates the UI). As the name suggests, this is a data structure (in this case, a java.util.ArrayList) of Stock objects. To put it another way, you are sharing data between two threads, the main UI thread and the spawned thread (called in the code Listing 2). Once you modify the shared data in the spawned thread, you update the UI by calling the refresh method on the Activity object.

If you have programmed Java Swing applications, you might have followed a pattern like this. However, this will not work in Android. The spawned thread will not be able to modify the UI at all. So how do you retrieve data without freezing the UI, but in a way that allows you to modify the UI once that data has been received? The android.os.Handler class allows you to coordinate and communicate between threads. Listing 3 shows an updated refreshStockData method that uses a Handler.

Listing 3. Multithreading that actually works—by using a Handler
private void refreshStockData(){
    final ArrayList<Stock> localStocks = 
          new ArrayList<Stock>(stocks.size());
    for (Stock stock : stocks){
        localStocks.add(new Stock(stock, stock.getId()));
    }
    final Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            for (int i=0;i<stocks.size();i++){
                stocks.set(i, localStocks.get(i));
            }
            refresh();
        }
    };
    Runnable task = new Runnable(){
        public void run() {
            try {
                ArrayList<Stock> newStocks = 
                    fetchStockData(localStocks.toArray(
                                  new Stock[localStocks.size()]));
                for (int i=0;i<localStocks.size();i++){
                    Stock ns = newStocks.get(i);
                    Stock ls = localStocks.get(i);
                    ls.setName(ns.getName());
                    ls.setCurrentPrice(ns.getCurrentPrice());
                }
                handler.sendEmptyMessage(RESULT_OK);
            } catch (Exception e) {
                Log.e("StockPortfolioViewStocks", 
                            "Exception getting stock data", e);
            } 
        }
    };
    Thread dataThread = new Thread(task);
    dataThread.start();
}

There are two major differences between the code in Listing 2 and Listing 3. The obvious difference is the presence of the Handler. The second is that, in the spawned thread, you do not modify the UI. Instead you send a message to the Handler, and the Handler then modifies the UI. Also, notice that, in the thread, you do not modify the stocks member variable, as you did previously. Instead you modify a local copy of the data. This is not strictly necessary, but it is safer.

Listing 3 demonstrates what turns out to be a very common pattern in concurrent programming: copying data, passing it to a new thread that performs some long task, passing the resulting data back to the main UI thread, and then updating the main UI thread with said data. Handlers are the main communication mechanism for this in Android, and they make this pattern easier to implement. However, Listing 3 still has a lot of boilerplate code. Luckily, Android provides a way to encapsulate and eliminate most of this boilerplate code. Listing 4 demonstrates this.

Listing 4. Easier multithreading with an AsyncTask
private void refreshStockData() {
    new AsyncTask<Stock, Void, ArrayList<Stock>>(){
        @Override
        protected void onPostExecute(ArrayList<Stock> result) {
            ViewStocks.this.stocks = result;
            refresh();
        }

        @Override
        protected ArrayList<Stock> doInBackground(Stock... stocks){
            try {
                return fetchStockData(stocks);
            } catch (Exception e) {
                Log.e("StockPortfolioViewStocks", "Exception getting stock data", e);
            }
            return null;
        }
    }.execute(stocks.toArray(new Stock[stocks.size()]));
}

As you can see, Listing 4 has much less boilerplate code than Listing 3. You do not create any threads or Handlers. You use the AsyncTask to encapsulate all of that. To create an AsyncTask, you must implement the doInBackground method. This method will always be executed in a separate thread, so you are free to call long-running tasks. Its input type comes from the type parameter of the AsyncTask that you create. In this case, the first type parameter was Stock, so doInBackground gets an array of Stock objects passed into it. Similarly, it returns an ArrayList<Stock> because that is the third type parameter of the AsyncTask. In this example, I also chose to override the onPostExecute method. This is an optional method that you will want to implement if you need to do anything with the data that comes back from doInBackground. This method will always be executed on the main UI thread, so it is ideal for modifying the UI.

With AsyncTask, you can really simplify multithreaded code. It removes many of the concurrency pitfalls from your development path. However, you still might find some potential problems with AsyncTask such as what happens when the orientation changes on the device while the doInBackground method in an AsyncTask object is executing. See the links in Resources for some techniques on how to deal with cases such as this.

Now let's move on to another common task where Android diverges significantly from the usual Java way—working with databases.


Android database connectivity

One very useful feature in Android is the presence of a local relational database. Sure you can store your data in local files, but often it is more useful to store it using a Relational Database Management System (RDBMS). Android gives you the popular SQLite database to work with, as it is highly optimized for embedded systems like Android. It is used by many core applications on Android. For example, the user's address book is stored in a SQLite database. Now, given Android's Java implementation, you might expect to access these databases using JDBC. Surprisingly, Android even includes the java.sql and javax.sql packages that constitute the bulk of the JDBC API. However, these turn out to be of no use when it comes to working with local Android databases. Instead, you will want to use the android.database and android.database.sqlite packages. Listing 5 shows an example of using these classes to store and retrieve data.

Listing 5. Database access with Android
public class StocksDb {
    private static final String DB_NAME = "stocks.db";
    private static final int DB_VERSION = 1;
    private static final String TABLE_NAME = "stock";
    private static final String CREATE_TABLE = "CREATE TABLE " + 
        TABLE_NAME + " (id INTEGER PRIMARY KEY, symbol TEXT, max_price DECIMAL(8,2), " +
            "min_price DECIMAL(8,2), price_paid DECIMAL(8,2), " +
            "quantity INTEGER)";
    private static final String INSERT_SQL = "INSERT INTO " + TABLE_NAME +
            " (symbol, max_price, min_price, price_paid, quantity) " +
            "VALUES (?,?,?,?,?)";
    private static final String READ_SQL = "SELECT id, symbol, max_price, " +
            "min_price, price_paid, quantity FROM " + TABLE_NAME;
    private final Context context;
    private final SQLiteOpenHelper helper;
    private final SQLiteStatement stmt;
    private final SQLiteDatabase db;
    public StocksDb(Context context){
        this.context = context;
        helper = new SQLiteOpenHelper(context, DB_NAME, null, 
                DB_VERSION){
            @Override
            public void onCreate(SQLiteDatabase db) {
                db.execSQL(CREATE_TABLE);
            }

            @Override
            public void onUpgrade(SQLiteDatabase db, int oldVersion, 
                    int newVersion) {
                throw new UnsupportedOperationException();
            }
        };
        db = helper.getWritableDatabase();
        stmt = db.compileStatement(INSERT_SQL);
    }
    public Stock addStock(Stock stock){
        stmt.bindString(1, stock.getSymbol());
        stmt.bindDouble(2, stock.getMaxPrice());
        stmt.bindDouble(3, stock.getMinPrice());
        stmt.bindDouble(4, stock.getPricePaid());
        stmt.bindLong(5, stock.getQuantity());
        int id = (int) stmt.executeInsert();
        return new Stock (stock, id);
    }
    public ArrayList<Stock> getStocks() {
        Cursor results = db.rawQuery(READ_SQL, null);
        ArrayList<Stock> stocks = 
                 new ArrayList<Stock>(results.getCount());
        if (results.moveToFirst()){
            int idCol = results.getColumnIndex("id");
            int symbolCol = results.getColumnIndex("symbol");
            int maxCol = results.getColumnIndex("max_price");
            int minCol = results.getColumnIndex("min_price");
            int priceCol = results.getColumnIndex("price_paid");
            int quanitytCol = results.getColumnIndex("quantity");
            do {
                Stock stock = new Stock(results.getString(symbolCol), 
                        results.getDouble(priceCol), 
                        results.getInt(quanitytCol), 
                                    results.getInt(idCol));
                stock.setMaxPrice(results.getDouble(maxCol));
                stock.setMinPrice(results.getDouble(minCol));
                stocks.add(stock);
            } while (results.moveToNext());
        }
        if (!results.isClosed()){
            results.close();
        }
        return stocks;
    }
    public void close(){
        helper.close();
    }    
}

The class in Listing 5 completely encapsulates a SQLite database used for storing stock information. Because you are working with an embedded database that will not only be used by your application, but will also be created by it, you have to provide code for creating the database. Android provides a useful abstract helper class for this called SQLiteOpenHelper. To implement this, you extend this abstract class and supply code to create your database in its onCreate method. Once you have an instance of this helper, you can get an instance of SQLiteDatabase which you can use to execute arbitrary SQL statements.

Your database class has a couple of convenience methods. The first one is addStock for saving a new stock to the database. Notice that you use a SQLiteStatement instance. This is similar to a java.sql.PreparedStatement. Notice how it is compiled in your class's constructor, so it can be reused each time addStock is called. On each addStock invocation, the SQLiteStatement's variables (the question marks in the INSERT_SQL string), are bound to the data passed in to addStock. Again, this is very similar to the PreparedStatement that you are probably familiar with from JDBC.

The other convenience method is getStocks. As the name suggests, this retrieves all of the stocks from the database. Notice that you once again use a SQL string for this, just as you would in JDBC. You do this using the rawQuery method on the SQLiteDatabase class. This class also has several query methods that let you query databases without using SQL directly. All of these various methods return a Cursor object which is very similar to a java.sql.ResultSet. You can move the Cursor over the rows of data that are returned from your query. On each row, you can use the getInt, getString, and other methods to retrieve the values associated with the various columns of the database table that you query. Again, this is very similar to a ResultSet. Also similar to a ResultSet, it is important to close a Cursor once you are done with it. If you do not close your Cursors, then you might quickly run out of memory and cause your application to crash.

Querying a local database can be a slow process, especially if you have many rows of data or you need to start running complex queries that join multiple tables. While it is unlikely that any database queries or insertions will take more than five seconds and cause an Application Not Responding dialog to appear, it is still not advisable to potentially freeze your UI while your code is busy reading and writing data. Naturally, the easiest way to avoid this situation is to use AsyncTask. Listing 6 shows an example of this.

Listing 6. Inserting to the database on a separate thread
Button button = (Button) findViewById(R.id.btn);
button.setOnClickListener(new OnClickListener(){
    public void onClick(View v) {
        String symbol = symbolIn.getText().toString();
        symbolIn.setText("");
        double max = Double.parseDouble(maxIn.getText().toString());
        maxIn.setText("");
        double min = Double.parseDouble(minIn.getText().toString());
        minIn.setText("");
        double pricePaid = 
                Double.parseDouble(priceIn.getText().toString());
        priceIn.setText("");
        int quantity = Integer.parseInt(quantIn.getText().toString());
        quantIn.setText("");
        Stock stock = new Stock(symbol, pricePaid, quantity);
        stock.setMaxPrice(max);
        stock.setMinPrice(min);
        new AsyncTask<Stock,Void,Stock>(){
            @Override
            protected Stock doInBackground(Stock... newStocks) {
                // There can be only one!
                return db.addStock(newStocks[0]);
            }
            @Override
            protected void onPostExecute(Stock s){
                addStockAndRefresh(s);
            }
        }.execute(stock);
    }
});

You start by creating an event listener for a button. When the user clicks the button, you read the stock data from various widgets (EditText widgets to be exact) and populate a new Stock object. You create an AsyncTask and invoke the addStock method from Listing 5 through the doInBackground method. Thus, addStock will execute on a background thread, not on the main UI thread. Once it completes, you pass the new Stock object from the database to the addStockAndRefresh method that executes on the main UI thread.


Conclusion

This article has shown how, even though Android only supports a subset of the many APIs in the Java environment, it is definitely not lacking in terms of capabilities. In some cases, such as networking, it fully implements familiar APIs but provides some more convenient ways to do things as well. In other cases, such as with concurrency, Android adds extra APIs and conventions that must be followed. Finally, in the case of database access, Android provides a completely different way to access databases, but with many familiar concepts. These aren't just arbitrary differences between standard Java and Android's Java technologies: They form some of the fundamental building blocks of Android development.


Download

DescriptionNameSize
Article source codeStockPortfolio.zip18KB

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into XML on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=XML, Java technology
ArticleID=500602
ArticleTitle=Gourmet Java technology for Android applications
publish-date=07152010