User Tools

Site Tools


android_learning:headfirst_android_development_notes:chapter_12

Chapter 12

This is just some text to get the section below to clear the Table of Contents. Otherwise the whole code block would appear very narrow.

Kludgey for sure, but it gets the job done.




p. 493: Code block

DrinkActivity.java
package com.hfad.starbuzz;
 
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
 
public class DrinkActivity extends AppCompatActivity {
 
    public static final String EXTRA_DRINKNO = "drinkNo";
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_drink);
 
        int drinkNo = (Integer)getIntent().getExtras().get(EXTRA_DRINKNO);
 
        //Create a cursor and populate Views from the cursor's data.
        try {
            // Gain access to our app's database:
            SQLiteOpenHelper starbuzzDatabaseHelper =
                new StarbuzzDatabaseHelper(this);
            SQLiteDatabase db = starbuzzDatabaseHelper.getReadableDatabase();
 
            // Get the name, description, and image for drinkNo:
            Cursor cursor = db.query ("DRINK",
                    new String[] {"NAME", "DESCRIPTION", "IMAGE_RESOURCE_ID"},
                    "_id = ?",
                    new String[] {Integer.toString(drinkNo)},
                    null, null,null);
 
            // Move to the first record in the Cursor
            if (cursor.moveToFirst()) {
                // Get the drink details from the cursor
                String nameText = cursor.getString(0);
                String descriptionText = cursor.getString(1);
                int photoId = cursor.getInt(2);
 
                // Populate the drink name
                TextView name = (TextView)findViewById(R.id.name);
                name.setText(nameText);
 
                // Populate the drink description
                TextView description = (TextView)findViewById(R.id.description);
                description.setText(descriptionText);
 
                // Populate the drink image
                ImageView photo = (ImageView)findViewById(R.id.photo);
                photo.setImageResource(photoId);
                photo.setContentDescription(nameText);
            }
 
            // Close up shop.
            cursor.close();
            db.close();
        } catch (SQLiteException e) {
            Toast toast = Toast.makeText(this, "Database unavailable", 
                Toast.LENGTH_SHORT);
            toast.show();
        }
    }
}

p. 504-505: Code block

Note the guards around objects I added in onDestroy().

DrinkCategoryActivity.java
package com.hfad.starbuzz;
 
import android.app.ListActivity;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.CursorAdapter;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.Toast;
 
public class DrinkCategoryActivity extends ListActivity {
 
    private SQLiteDatabase db;  // the app's db
    private Cursor cursor;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ListView listDrinks = getListView(); // i.e., this.getListView(); 
                                             // get the ListView used by 
                                             // this ListActivity
 
        // Use a cursor via an adapter to populate listDrinks
        try {
            // Gain access to our app's database:
            SQLiteOpenHelper starbuzzDatabaseHelper = 
                new StarbuzzDatabaseHelper(this);
            db = starbuzzDatabaseHelper.getReadableDatabase();
 
            // Set a cursor on the _id and name:
            Cursor cursor = db.query ("DRINK",
                    new String[] {"_id", "NAME"},
                    null, null, null, null,null);
 
            // Create a cursor adapter for the cursor
            CursorAdapter listAdapter = new SimpleCursorAdapter(this,
                    android.R.layout.simple_list_item_1,
                    cursor,
                    new String[]{"NAME"},
                    new int[]{android.R.id.text1},
                    0);
 
            // Set listDrinks' adapter:
            listDrinks.setAdapter(listAdapter);
        } catch (SQLiteException e) {
            Toast.makeText(this, "Database unavailable", 
                Toast.LENGTH_SHORT).show();
        }
    }
 
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (cursor != null) {cursor.close();}
        if (db != null) {db.close();}
    }
 
    // Start new activity when an item is clicked
    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);
        Intent intent = new Intent(DrinkCategoryActivity.this, 
            DrinkActivity.class);
        intent.putExtra(DrinkActivity.EXTRA_DRINKNO, (int) id);
        startActivity(intent);
    }
}

p. 513-514: Code block

DrinkActivity.java
package com.hfad.starbuzz;
 
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
 
public class DrinkActivity extends AppCompatActivity {
 
    public static final String EXTRA_DRINKNO = "drinkNo";
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_drink);
 
        int drinkNo = (Integer)getIntent().getExtras().get(EXTRA_DRINKNO);
 
        //Create a cursor and populate Views from the cursor's data.
        try {
            // Gain access to our app's database:
            SQLiteOpenHelper starbuzzDatabaseHelper = 
                new StarbuzzDatabaseHelper(this);
            SQLiteDatabase db = starbuzzDatabaseHelper.getWritableDatabase();
 
            // Get the name, description, and image for drinkNo:
            Cursor cursor = db.query ("DRINK",
                    new String[]
                        {"NAME", "DESCRIPTION", "IMAGE_RESOURCE_ID", "FAVORITE"},
                    "_id = ?",
                    new String[] {Integer.toString(drinkNo)},
                    null, null,null);
 
            // Move to the first record in the Cursor
            if (cursor.moveToFirst()) {
                // Get the drink details from the cursor
                String nameText = cursor.getString(0);
                String descriptionText = cursor.getString(1);
                int photoId = cursor.getInt(2);
                boolean isFavorite = (cursor.getInt(3) == 1);
 
                // Populate the drink name
                TextView name = (TextView)findViewById(R.id.name);
                name.setText(nameText);
 
                // Populate the drink description
                TextView description = (TextView)findViewById(R.id.description);
                description.setText(descriptionText);
 
                // Populate the drink image
                ImageView photo = (ImageView)findViewById(R.id.photo);
                photo.setImageResource(photoId);
                photo.setContentDescription(nameText);
 
                // Populate the favorite checkbox
                CheckBox favorite = (CheckBox)findViewById(R.id.favorite);
                favorite.setChecked(isFavorite);
            }
 
            // Close up shop.
            cursor.close();
            db.close();
        } catch (SQLiteException e) {
            Toast toast = Toast.makeText(this, "Database unavailable",
                Toast.LENGTH_SHORT);
            toast.show();
        }
    }
 
    // Update the DB's FAVORITES field for this drink.
    public void onFavoriteClicked(View view) {
        int drinkNo = (Integer)getIntent().getExtras().get("drinkNo");
        CheckBox favorite = (CheckBox)findViewById(R.id.favorite);
        ContentValues drinkValues = new ContentValues();
 
        // Stage new FAVORITE value
        drinkValues.put("FAVORITE", favorite.isChecked());
 
        SQLiteOpenHelper starbuzzDatabaseHelper = 
            new StarbuzzDatabaseHelper(DrinkActivity.this);
 
        try {
            SQLiteDatabase db = starbuzzDatabaseHelper.getWritableDatabase();
            db.update("DRINK", drinkValues, "_id = ?",
                new String[] {Integer.toString(drinkNo)});
            db.close();
        } catch(SQLiteException e) {
            Toast.makeText(DrinkActivity.this, "Database unavailable",
                Toast.LENGTH_SHORT).show();
        }
    }
}

p. 518-520: Code block

TopLevelActivity.java
package com.hfad.starbuzz;
 
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.CursorAdapter;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.Toast;
 
public class TopLevelActivity extends AppCompatActivity {
 
    private SQLiteDatabase db;
    private Cursor favoritesCursor;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_top_level);
 
        AdapterView.OnItemClickListener itemClickListener =
            new AdapterView.OnItemClickListener() {
 
            @Override
            public void onItemClick (AdapterView<?> parent, View view,
                int position, long id) {
                    if (position == 0) {
                        Intent intent = new Intent(TopLevelActivity.this, 
                            DrinkCategoryActivity.class);
                        startActivity(intent);
                    }
            }
        };
 
        // Set the top-level ListView's item click listener to OnItemClickListener.
        ListView listView = (ListView) findViewById(R.id.list_options);
        listView.setOnItemClickListener(itemClickListener);
 
        // Populate the list_favorites ListView from a cursor.
        ListView listFavrotites = (ListView)findViewById(R.id.list_favorites);
        try {
            SQLiteOpenHelper starbuzzDatabaseHelper = 
                new StarbuzzDatabaseHelper(this);
            db = starbuzzDatabaseHelper.getReadableDatabase();
 
            // Get the data into a cursor
            favoritesCursor = db.query("DRINK",
                    new String[]{"_id", "NAME"},
                    "FAVORITE = 1",
                    null, null, null, null);
 
            // Map data to list via adapter
            CursorAdapter favoriteAdapter = new SimpleCursorAdapter(
                    TopLevelActivity.this,
                    android.R.layout.simple_list_item_1,
                    favoritesCursor,
                    new String[] {"NAME"},
                    new int[] {android.R.id.text1}, 0);
 
            listFavrotites.setAdapter(favoriteAdapter);
 
            // Handle click events on list items - show favorite item's activity
            listFavrotites.setOnItemClickListener(
                new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(
                    AdapterView<?> parent, View view, int position, long id) {
                        Intent intent = 
                            new Intent(TopLevelActivity.this, DrinkActivity.class);
                        intent.putExtra(DrinkActivity.EXTRA_DRINKNO, (int)id);
                        startActivity(intent);
                    }
                }
            );
 
 
        } catch(SQLiteException e) {
            Toast.makeText(TopLevelActivity.this, "Database unavailable",
                Toast.LENGTH_SHORT).show();
        }
    }
 
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (favoritesCursor != null) {favoritesCursor.close();}
        if (db != null) {db.close();}
    }
}

p. 524: Code block

    @Override
    protected void onRestart() {
        super.onRestart();
        try{
            StarbuzzDatabaseHelper starbuzzDatabaseHelper =
                new StarbuzzDatabaseHelper(this);
            db = starbuzzDatabaseHelper.getReadableDatabase();
 
            // Generate a new Cursor with the updated DB state
            Cursor newCursor = db.query("DRINK",
                    new String[] { "_id", "NAME"},
                    "FAVORITE = 1",
                    null, null, null, null);
 
            ListView listFavorites = (ListView)findViewById(R.id.list_favorites);
            CursorAdapter adapter = (CursorAdapter) listFavorites.getAdapter();
 
            // change only the Cursor in the Adapter
            adapter.changeCursor(newCursor);
            favoritesCursor = newCursor;
        } catch(SQLiteException e) {
            Toast toast = Toast.makeText(this, "Database unavailable",
                Toast.LENGTH_SHORT);
            toast.show();
        }
    }

pp. 526+: Threads

Don't neglect the material on Android threads!

Threading in Android is important enough that it should be in a section all its own. In addition to “user threads” (a.k.a., AsycTasks) being used with databases, they are also frequently used for accessing network resources so that your app's UI doesn't grind to a halt waiting for the resource to respond.

From a programmer's point of view, Android is by default a single-thread environment: the “main thread” is where your code runs unless you tell Android different. The code is run synchronously, meaning that the next thing in your code won't be executed until the previous thing completes. So if you tell your app to do something that takes a long time, your app will lock up until that completes.

The idea of an AsycTask is that you can start a process that run in the background (in a different thread) and then notify the main thread when it's done so it can respond to the new state of things. This leaves the main thread available to, say, update the UI and handle user events while the process, say, waits for Flickr to respond to an API request. All that downtime the app would have spent waiting for the network to do something is instead available for user interaction.

In creating subclasses of AsycTask, you must override the doInBackground method (it's abstract). Overriding any of the other methods–onPreExecute, onProgressUpdate, and onPostExecute– is optional. These methods let you use AyncTasks to encapsulate general tasks, not just the parts of a task that run in the background.

p. 530

The UpdateDrinkTask discussed here will be added as an inner class to DrinkActivity later on. So don't create a new UpdateDrinkTask.java file, ok?

pp. 537-538

I've added some Toasts to mark when some things are happening. You wouldn't want to do this in production.

DrinkActivity.java
package com.hfad.starbuzz;
 
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
 
public class DrinkActivity extends AppCompatActivity {
 
    public static final String EXTRA_DRINKNO = "drinkNo";
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_drink);
 
        int drinkNo = (Integer)getIntent().getExtras().get(EXTRA_DRINKNO);
 
        //Create a cursor and populate Views from the cursor's data.
        try {
            // Gain access to our app's database:
            SQLiteOpenHelper starbuzzDatabaseHelper =
                new StarbuzzDatabaseHelper(this);
            SQLiteDatabase db = starbuzzDatabaseHelper.getWritableDatabase();
 
            // Get the name, description, and image for drinkNo:
            Cursor cursor = db.query ("DRINK",
                    new String[]
                        {"NAME", "DESCRIPTION", "IMAGE_RESOURCE_ID", "FAVORITE"},
                    "_id = ?",
                    new String[] {Integer.toString(drinkNo)},
                    null, null,null);
 
            // Move to the first record in the Cursor
            if (cursor.moveToFirst()) {
                // Get the drink details from the cursor
                String nameText = cursor.getString(0);
                String descriptionText = cursor.getString(1);
                int photoId = cursor.getInt(2);
                boolean isFavorite = (cursor.getInt(3) == 1);
 
                // Populate the drink name
                TextView name = (TextView)findViewById(R.id.name);
                name.setText(nameText);
 
                // Populate the drink description
                TextView description = (TextView)findViewById(R.id.description);
                description.setText(descriptionText);
 
                // Populate the drink image
                ImageView photo = (ImageView)findViewById(R.id.photo);
                photo.setImageResource(photoId);
                photo.setContentDescription(nameText);
 
                // Populate the favorite checkbox
                CheckBox favorite = (CheckBox)findViewById(R.id.favorite);
                favorite.setChecked(isFavorite);
            }
 
            // Close up shop.
            cursor.close();
            db.close();
        } catch (SQLiteException e) {
            Toast toast = Toast.makeText(this, "Database unavailable",
                Toast.LENGTH_SHORT);
            toast.show();
        }
    }
 
    // Update the DB's FAVORITES field for this drink.
    public void onFavoriteClicked(View view) {
        int drinkNo = (Integer)getIntent().getExtras().get("drinkNo");
        new UpdateDrinkTask().execute(drinkNo);
    }
 
    // Inner class: a task to update the drink's DB record.
    private class UpdateDrinkTask extends AsyncTask <Integer, Void, Boolean> {
 
        ContentValues drinkValues;
 
        @Override
        protected void onPreExecute() {
            CheckBox favorite = (CheckBox)findViewById(R.id.favorite);
            drinkValues = new ContentValues();
            drinkValues.put("FAVORITE", favorite.isChecked());
            Toast.makeText(DrinkActivity.this, "Backgrounding DB operation...", 
                Toast.LENGTH_SHORT).show();
        }
 
        @Override
        protected Boolean doInBackground(Integer... drinks) {
            int drinkNo = drinks[0];
            SQLiteOpenHelper starbuzzDatabaseHelper = 
                new StarbuzzDatabaseHelper(DrinkActivity.this);
            try {
                SQLiteDatabase db = starbuzzDatabaseHelper.getWritableDatabase();
                db.update("DRINK", drinkValues, "_id = ?",
                    new String[]{Integer.toString(drinkNo)});
                db.close();
                return true;
            } catch(SQLiteException e) {
                return false;
            }
        }
 
        @Override
        protected void onPostExecute(Boolean success) {
            if (!success) {
                Toast.makeText(DrinkActivity.this, "Database unavailable", 
                    Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(DrinkActivity.this, "DB update complete",
                    Toast.LENGTH_SHORT).show();
            }
        }
    }
}

Debug with the system log

This isn't covered until Chapter 13 in the book, but it's worth using here.

In this final version of DrinkActivity, I use the system log to output debug information to the console instead of using Toasts for debug messages.

DrinkActivity.java
package com.hfad.starbuzz;
 
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
 
public class DrinkActivity extends AppCompatActivity {
 
    public static final String EXTRA_DRINKNO = "drinkNo";
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_drink);
 
        int drinkNo = (Integer)getIntent().getExtras().get(EXTRA_DRINKNO);
 
        //Create a cursor and populate Views from the cursor's data.
        try {
            // Gain access to our app's database:
            SQLiteOpenHelper starbuzzDatabaseHelper = 
                new StarbuzzDatabaseHelper(this);
            SQLiteDatabase db = starbuzzDatabaseHelper.getWritableDatabase();
 
            // Get the name, description, and image for drinkNo:
            Cursor cursor = db.query ("DRINK",
                    new String[]
                        {"NAME", "DESCRIPTION", "IMAGE_RESOURCE_ID", "FAVORITE"},
                    "_id = ?",
                    new String[] {Integer.toString(drinkNo)},
                    null, null,null);
 
            // Move to the first record in the Cursor
            if (cursor.moveToFirst()) {
                // Get the drink details from the cursor
                String nameText = cursor.getString(0);
                String descriptionText = cursor.getString(1);
                int photoId = cursor.getInt(2);
                boolean isFavorite = (cursor.getInt(3) == 1);
 
                // Populate the drink name
                TextView name = (TextView)findViewById(R.id.name);
                name.setText(nameText);
 
                // Populate the drink description
                TextView description = (TextView)findViewById(R.id.description);
                description.setText(descriptionText);
 
                // Populate the drink image
                ImageView photo = (ImageView)findViewById(R.id.photo);
                photo.setImageResource(photoId);
                photo.setContentDescription(nameText);
 
                // Populate the favorite checkbox
                CheckBox favorite = (CheckBox)findViewById(R.id.favorite);
                favorite.setChecked(isFavorite);
            }
 
            // Close up shop.
            cursor.close();
            db.close();
        } catch (SQLiteException e) {
            Toast toast = Toast.makeText(this, "Database unavailable",
                Toast.LENGTH_SHORT);
            toast.show();
        }
    }
 
    // Update the DB's FAVORITES field for this drink.
    public void onFavoriteClicked(View view) {
        int drinkNo = (Integer)getIntent().getExtras().get("drinkNo");
        new UpdateDrinkTask().execute(drinkNo);
    }
 
    // Inner class: a task to update the drink's DB record.
    private class UpdateDrinkTask extends AsyncTask <Integer, Void, Boolean> {
 
        ContentValues drinkValues;
 
        @Override
        protected void onPreExecute() {
            CheckBox favorite = (CheckBox)findViewById(R.id.favorite);
            drinkValues = new ContentValues();
            drinkValues.put("FAVORITE", favorite.isChecked());
            Log.d("DB", "Backgrounding DB operation...");
        }
 
        @Override
        protected Boolean doInBackground(Integer... drinks) {
            int drinkNo = drinks[0];
            SQLiteOpenHelper starbuzzDatabaseHelper =
                new StarbuzzDatabaseHelper(DrinkActivity.this);
            try {
                SQLiteDatabase db = starbuzzDatabaseHelper.getWritableDatabase();
                db.update("DRINK", drinkValues, "_id = ?",
                    new String[]{Integer.toString(drinkNo)});
                db.close();
                return true;
            } catch(SQLiteException e) {
                return false;
            }
        }
 
        @Override
        protected void onPostExecute(Boolean success) {
            if (!success) {
                Toast.makeText(DrinkActivity.this, "Database unavailable",
                    Toast.LENGTH_SHORT).show();
            } else {
                Log.d("DB", "DB update complete");
            }
        }
    }
}
android_learning/headfirst_android_development_notes/chapter_12.txt · Last modified: 2016/04/24 04:08 by mithat

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki