Debugging

You can debug code on the emulator or phone. The Android Studio environment offers the Android DDMS which represents a sophisticated tool for debugging code. Hopefully, most of the time this will suffice. Andriod is quite hard to program and debug (for me anyway) because of the large number of new APIs. If you are experienced with Java you will catch many of the obvious bugs from desk checking and looking for the obvious edge issues and API problems.

We also briefly discuss how to set breakpoints and look at variables in the code and run time.

What this lecture will teach you

Resources

There are a number of good resources for debugging in Android:

Read the very good Debugging with Android Studio from Android Developers guide. Many of you have debugged Java in CS10. For Android you do almost exactly the same. Make sure your phone is plugged in and debug perspective is shown.

Tips

There are a number of tips when debugging -- here are some relevant to the type of coding we are doing:

Dump the stack trace

We show you how to log the stack trace.

Use debugging helper classes

Android provides debug helper classes such as util.Log and Debug for your convenience.

Display useful info on phone screen

The device can display useful information such as CPU usage or highlights around redrawn areas. Turn these features on and off in the developer settings window as described in Debugging with the Dev Tools App.

Log trace data

You can use the Logcat to log debug data and look at data. You can also log method calls and other tracing data in an activity by calling startMethodTracing(). See Profiling with Traceview and dmtracedump for details.

Logging - a debug tool

Before we get started note that the strange looking Log.d(TAG, "loadUserData()") method. This is associated with debugging. The Andriod system with keep a log of these Log.d() calls embedded in the code -- if you are C programmer these are like printf() but not to the screen (you would need toast to do that). Log.d printout (which could include data structures and member variable state) is directed to the LogCat. The logging system provides a mechanism for collecting and viewing system debug output. Logcat dumps a log of system messages and potentially much more. We will come back to logging. Right now you should know that you can run LogCat through the Dalvik Debug Monitor Server (DDMS) allowing you to read the messages in real time. Check out the screen dump of DDMS below. To display log messages (and BTW, the d in log.d refers to debug messages) you have to switch to the DDMS view from the default Java view. Then set up a filter in this case for the TAG CS65. This is set up at the start of the activity

    private static final String TAG = "CS65";

Then run the app. For example, start the SharedPreference activity from the main menu and hit the Save. You should see the two log message shown in the screen dump below. Please do this and play with the log system. More on debugging later. We will need debugging tools. Note, this is all from the phone. I did not run this under the emulation mode -- it's too darn slow.

Printing out program data using Log.d()

You can print out the value of member variables in your code using Log.d(); for example:

 Log.d(TAG, "loadUserData() email is " + mValue);

 Log.d(TAG, "loadUserData(): number of the radioButton is " + mIntValue);

The equivalent using toast is for the radio button is:

    Toast.makeText(getApplicationContext(),
                    "number of the radioButton is : " + mIntValue,
                    Toast.LENGTH_SHORT).show();

But remember Toast is not for debugging -- the toast is fleeting, pops up and disappears but the log.d is always there afterwards to study.

If I run the layouts code and click on SharedPreferences I get this in LogCat.

This relates to this code in SharedPreferencesActivity.java in the layouts project:

    private void loadUserData() {

        // We can also use log.d to print to the LogCat

        Log.d(TAG, "loadUserData()");

        // Load and update all profile views

        // Get the shared preferences - create or retrieve the activity
        // preference object

        String mKey = getString(R.string.preference_name);
        SharedPreferences mPrefs = getSharedPreferences(mKey, MODE_PRIVATE);

        // Load the user email

        mKey = getString(R.string.preference_key_profile_email);
        String mValue = mPrefs.getString(mKey, " ");
        ((EditText) findViewById(R.id.editEmail)).setText(mValue);
        
        Log.d(TAG, "loadUserData() email is " + mValue);


        // Please Load gender info and set radio box

        mKey = getString(R.string.preference_key_profile_gender);

        int mIntValue = mPrefs.getInt(mKey, -1);
        // In case there isn't one saved before:
        if (mIntValue >= 0) {
            // Find the radio button that should be checked.
            RadioButton radioBtn = (RadioButton) ((RadioGroup) findViewById(R.id.radioGender))
                    .getChildAt(mIntValue);
            // Check the button.
            radioBtn.setChecked(true);
            Toast.makeText(getApplicationContext(),
                    "number of the radioButton is : " + mIntValue,
                    Toast.LENGTH_SHORT).show();
            Log.d(TAG, "loadUserData(): number of the radioButton is " + mIntValue);
        //  Log.d(TAG, Log.getStackTraceString(new Exception()));
        }

    }

Printing out the full stack trace

When your app crashes for some reason (e.g., NullPointerException, NetworkOnMainThreadException, ActivityNotFoundException, etc.) the Android Studio prints out the full stack trace.

If you want to print out the full stack trace without any bugs in your code, inserting the following code into your program will force a checkpoint of the stack to be dumped to the Logcat.


Log.d(TAG, Log.getStackTraceString(new Exception()));

If I uncomment this line in my code above SharedPreferencesActivity.java then I get the following stack dump in the Logcat


01-20 11:44:00.783  30497-30497/edu.dartmouth.cs.layouts D/CS65﹕ loadUserData()
01-20 11:44:00.785  30497-30497/edu.dartmouth.cs.layouts D/CS65﹕ loadUserData() email is  campbell@cs.dartmouth.edu
01-20 11:44:00.785  30497-30497/edu.dartmouth.cs.layouts D/CS65﹕ loadUserData(): number of the radioButton is 1
01-20 11:44:00.786  30497-30497/edu.dartmouth.cs.layouts D/CS65﹕ java.lang.Exception
            at edu.dartmouth.cs.layouts.SharedPreferencesActivity.loadUserData(SharedPreferencesActivity.java:99)
            at edu.dartmouth.cs.layouts.SharedPreferencesActivity.onCreate(SharedPreferencesActivity.java:28)
            at android.app.Activity.performCreate(Activity.java:5933)
            at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1105)
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2251)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2360)
            at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3912)
            at android.app.ActivityThread.access$900(ActivityThread.java:144)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1284)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:135)
            at android.app.ActivityThread.main(ActivityThread.java:5221)
            at java.lang.reflect.Method.invoke(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:372)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)

Buggy code

Let's go over the ActionTabs example again this time to illustrate the idea of how to interpret the error messages in the Logcat. I've missed something in the code, and when I launch it, the app crashes without showing the main screen.

Here is how the bug does down. We use the LogCat on errors. Let's look through them after the crash.

From the lengthy log stack, let's focus this specific line which begins with a "Caused by:".


 Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.util.ArrayList.size()' on a null object reference
            at edu.dartmouth.cs.actiontabs.ActionTabsViewPagerAdapter.getCount(ActionTabsViewPagerAdapter.java:35)
            at android.support.v4.view.ViewPager.setAdapter(ViewPager.java:439)
            at edu.dartmouth.cs.actiontabs.MainActivity.onCreate(MainActivity.java:46)
            at android.app.Activity.performCreate(Activity.java:5933)

When we click the highlighted line it can bring us to the line of buggy code.

        myViewPageAdapter =new ActionTabsViewPagerAdapter(getFragmentManager(),
                fragments);
        viewPager.setAdapter(myViewPageAdapter);

Ah, fragments list is not yet initialized and the viewPager is trying to set an adapter with nothing actual fragments in it. Bad. We need to add all the fragments in order to this list for a fix.

        // create a fragment list in order.
        fragments = new ArrayList<Fragment>();
        fragments.add(new FindFragment());
        fragments.add(new ChatFragment());
        fragments.add(new MeetFragment());
        fragments.add(new PartyFragment());

        // use FragmentPagerAdapter to bind the slidingTabLayout (tabs with different titles)
        // and ViewPager (different pages of fragment) together.
        myViewPageAdapter =new ActionTabsViewPagerAdapter(getFragmentManager(),
                fragments);
        viewPager.setAdapter(myViewPageAdapter);

OK. Let's buid and see what we get.

Setting Breakpoints

Many times you will need to run your code in debug mode in Android Studio to set breakpoints and inspect variables to work out exactly what happened before you program crashed; Let's still use the ActionBars bug for example. We know it crashes at line 46, but we don't know why exactly the exception occured there. So we set a breakpoint at line 44 -- the line before the exception and then look at the variables such as fragments and getFragmentManager() to see if they are OK. If they are then we single step to the next line 46. We notice, however, once line 44 is executed, the variable fragments is NULL, which is a bug.

Debugging steps

Typically you want to do the following when you have a bug in your code and want to get to the bottom of it:

It is good to just set a breakpoint at the start of your code e.g., onCreate() or in a helper function or many methods and functions and just step therough the code and look variables in the program. This is good practice and can help you fine problems.

But there is nothing as good as desk checking your code -- that is, reading closely through your code and "executing it" with a pen and paper.