Saturday, October 30, 2010

Android Activity Basics

This tutorial will cover the fundamentals of the Android Activity component. This is not as basic as a simple HelloWorld and not as complicated as a full blown App. The goal is to show some simple but useful usecases.

The tutorial will cover the following topics:
  • Activity component callbacks during launching an App and quitting an App
  • App State persistence across re-starts
  • Activity component callbacks for Menu creation and event handling
    • Navigation from the main screen of the Activity to another screen within the Activity

    For sake of simplicity, the code is all present in one 'SampleActivity' class. I fought the serious Enterprise Java OCD urge to refactor it into a thousand very small beautiful tightly knit classes communicating with each other via some serious architectural layers (ahem...wrappers) ;)

    App Launch
    When the App is launched, it fires up the registered Activity. This is registered in the AndroidManifest.xml file. According to the Activity component lifecycle, onCreate is invoked during initial activation. The main responsibility of this phase is to do some state management. This could be loading the state of the App from a local cache, inflating Layouts from the various screens, etc.
    Source Code:
    protected void onCreate(Bundle savedInstanceState)
    {
      try
      {
         //This must be invoked..otherwise the Android runtime
        //will throw an exception
         super.onCreate(savedInstanceState);
                
         //Load some App state information that is stored in a
        //simple cache. This information survives App re-starts
         this.loadCache();
                
         //Get the layout information for the home screen
         Integer homeScreenLayoutId  = R.layout.home;
                
         //Get the layout information for the next screen
         Integer nextScreenLayoutId  = R.layout.nextscreen;
                
         this.cache.put("home", homeScreenLayoutId);
         this.cache.put("nextscreen", nextScreenLayoutId);
     }
     catch(Exception e)
     {
        //Show an error screen and exit
        this.getErrorDialog("System Error",
        e.toString()).show();
     }
    }

    When the App state is fully loaded it should display the UI associated with the activity. This happens during the onResume invocation on the component.
    Source Code:
    protected void onResume()
    {
       try
       {
          //This must be invoked..otherwise the Android runtime
          //will throw an exception
          super.onResume();
                
          //Find and render last active screen
          String active = (String)this.cache.get("active");
          if(active != null)
          {
             if(active.equals("home"))
             {
                this.renderHomeScreen();
              }
              else
              {
                 this.renderNextScreen();
               }
            }
            else
            {
                    //render the home view
                    this.renderHomeScreen();
             }
        }
        catch(Exception e)
        {
                //Show an error screen and exit
                this.getErrorDialog("System Error",
                e.toString()).show();
        }
    } 
    

    Here the onResume method decides which one of the Activity's screen it should render.

    Quitting an App

    When the Activity is closed, the Android runtime invokes the onDestroy method to allow the App to
    do some state management and cleanup.
    Source Code:
    protected void onDestroy()
    {
       try
       {
          //This must be invoked..otherwise the Android runtime
          //will throw an exception
          super.onDestroy();
                
           //Saving the App State
           this.saveCache();
        }
        catch(Exception e)
        {
                //Handle Exception..App is being destroyed...
                //in case of an error, let the platform
                //handle it with a system message
                throw new RuntimeException(e);
        }
    } 
    
    Here, its saves the Cache instance so that when the App is launched again, the currently active screen is the one that is rendered.

    Note: Besides the onCreate, onResume, and onDestroy invocations covered in this tutorial, there are other callback invocations as well. A detailed explanation of the entire Activity lifecycle is covered here

    App State Persistence across restarts

    This activity remembers the currently active screen upon closing. When the App is launched again, its resumes the Activity at this point.

    It does this by storing a pojo Cache instance inside the SharedPreferences object. The SharedPreferences class in Android provides a general framework that allows you to save and retrieve persistent key-value pairs of primitive data types.
     Source Code:

    private void loadCache() throws Exception
    {
      SharedPreferences local = this.getPreferences(0);
      this.cache = new Cache();
      String home = local.getString("home", "");
      String nextscreen = local.getString("nextscreen", "");
      String active = local.getString("active", "");
            
      if(home != null && home.trim().length()>0)
      {
        cache.put("home", new Integer(Integer.parseInt(home)));
      }
            
       if(nextscreen != null && nextscreen.trim().length()>0)
       {
         cache.put("nextscreen",
         new Integer(Integer.parseInt(nextscreen)));
       }
            
       if(active != null && active.trim().length()>0)
       {
         cache.put("active", active);
       }
    } 
    

    private void saveCache() throws Exception
    {
      //Load the SharedPreferences Editor
      SharedPreferences local = this.getPreferences(0);
      SharedPreferences.Editor editor = local.edit();
            
      //Store the 'home' layout id
      Integer home = (Integer)cache.get("home");
       if(home != null)
       {
          editor.putString("home", ""+home);
        }
            
        //Store the 'nextscreen' layout id
        Integer nextscreen = (Integer)cache.get("nextscreen");
        if(nextscreen != null)
        {
          editor.putString("nextscreen", ""+nextscreen);
        }
            
        //Store the 'active' screen, this can be used
        //to display this screen when the
        //App is launched again
        String active = (String)cache.get("active");
         if(active != null)
         {
           editor.putString("active", active);
         }
            
         //commit the changes
         editor.commit();
    }
    


    App Menu 
    The Android runtime invokes onCreateOptionsMenu to give the Activity a chance to create the App Menu. The menu is launched in response to the 'Menu' key on the device.
    Source Code:
    public boolean onCreateOptionsMenu(Menu menu)
    {
      try
      {
         //Inflate the layout for the menu. 
        //The xml is stored in res/menu/app_menu.xml
         MenuInflater inflater = getMenuInflater();
         Integer app_menu_id  = R.menu.app_menu;
         inflater.inflate(app_menu_id, menu);
           
         return true;
       }
       catch(Exception e)
       {
         return false;
       }
    } 
    
    It invokes onOptionsItemSelected as an event handler in response to the selection of one of the 'Menu' items.
    Source Code:
    public boolean onOptionsItemSelected(MenuItem item)
    {
      // Handle item selection
      switch (item.getItemId()) 
      {
         case R.id.hello:
           Toast.makeText(this, "Hello World!!!", 
    Toast.LENGTH_SHORT).show();
           return true;
         case R.id.quit:
            this.finish();
            return true;
          default:
            return super.onOptionsItemSelected(item);
       }
    } 
    


    Configuration
    Full Source Code

    Enjoy!!!
      ShareThis

      3 comments:

      1. the call to getString( "home", "" ), returns a empty string (default param) if "home" is not defined previously. Why you check home != null?

        Same with the nextscreen and active strings.

        ReplyDelete
      2. you are correct. The 'null' check is not necessary.

        ReplyDelete
      3. If you're using Eclipse you can just make a "new Android App" and copy in the files above, but you will also need the Cache.java file! You can unzip the Maven file and grab it from there.

        ReplyDelete