Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Developing Mobile Applications Week 6 Preferences, Profiling, Debugging Steve Jones Department of Computer Science University of Waikato [email protected] Preferences, Profiling, Debugging 1 User settings Most applications will need to provide some configuration by the user, for example • the number of properties to load from TradeMe Remember the place holder in the action bar <menu xmlns:android="http://schemas.android.com/apk/res/android" > <!– snip --> <item android:id="@+id/settings" android:icon="@android:drawable/ic_menu_preferences" android:showAsAction="never" android:title="@string/action_bar_settings"> </item> </menu> Preferences, Profiling, Debugging 2 User settings Use combination of an activity, fragments, and xml for layout and default setting values We’ll have • an activity for the settings – launched when user selects Settings from the action bar • XML that defines the groups that settings are organised into • a fragment for each group of settings • XML that defines the elements in each group and cope with single/multi-panel layouts Preferences, Profiling, Debugging 3 Android Preference framework Android has built in support in the android.preference package • • • • • PreferenceActivity PreferenceFragment handling different orientation layouts various preference UI widgets underlying system support for persisting and reading preference values Preferences, Profiling, Debugging 4 PreferenceHeader User clicks on a header to access a group of settings Each group exists in a fragment System takes care of orientation layouts Preferences, Profiling, Debugging 5 Preferences Similar to our PropertyFinder list + detail design But layout changes, fragment management etc handled automatically by the PreferenceActivity class etc Preferences, Profiling, Debugging 6 Preferences The logic that determines one-pane vs two-pane layout is basically hard-coded into the PreferenceActivity implementation • sw720dp smallest side is 720 px Preferences, Profiling, Debugging 7 PreferenceHeader Define in • res/xml/preference_headers.xml <?xml version="1.0" encoding="utf-8"?> <preference-headers xmlns:android="http://schemas.android.com/apk/res/android"> <header android:fragment="org.stevej.android.propertyfinder7.SettingsFragment" Use the same fragment class android:title="@string/prefs_group_location_title" This will create 2 instances: 1 android:summary="@string/prefs_group_location_summary" > to hold network settings, 1 to <extra android:name="settings_group" android:value=“location_settings" /> hold app start up settings </header> The SettingsFragment <header implementation is responsible android:fragment="org.stevej.android.propertyfinder7.SettingsFragment" for loading the correct layout android:title="@string/prefs_group_startup" from XML android:summary="@string/prefs_group_startup_summary" > <extra android:name="settings_group" android:value=”startup_settings" /> </header> </preference-headers> Preferences, Profiling, Debugging 8 PreferenceHeader Define in • res/xml/preference_headers.xml <?xml version="1.0" encoding="utf-8"?> <preference-headers xmlns:android="http://schemas.android.com/apk/res/android"> <header android:fragment="org.stevej.android.propertyfinder7.UserSettingsFragment" android:title="@string/prefs_group_location_title" android:summary="@string/prefs_group_location_summary" > Each header has its own instance of a settings fragment <extra android:name="settings_group" android:value=”location_settings" /> </header> If we were creating an instance in code we would provide a <header constructor argument to android:fragment="org.stevej.android.propertyfinder7.UserSettingsFragment" indicate the settings group to android:title="@string/prefs_group_startup" display android:summary="@string/prefs_group_startup_summary" > The <extra> element defines an <extra android:name="settings_group" android:value=”startup_settings" /> argument that is accessible in </header> </preference-headers> Java when the fragment is created from XML Preferences, Profiling, Debugging 9 PreferenceActivity Very simple… just implement onBuildHeaders() public class UserSettingsActivity extends PreferenceActivity { @Override public void onBuildHeaders(List<Header> target) { loadHeadersFromResource(R.xml.preference_headers, target); } } • all the standard activity lifecycle methods etc handled by PreferenceActivity class • which manages a list of settings group headers • onBuildHeaders invoked automatically at appropriate point in lifecycle • load header definitions from XML Preferences, Profiling, Debugging 10 PreferenceActivity Very simple… just implement onBuildHeaders() public class UserSettingsActivity extends PreferenceActivity { @Override public void onBuildHeaders(List<Header> target) { loadHeadersFromResource(R.xml.preference_headers, target); } } • all the standard activity lifecycle methods etc handled by PreferenceActivity class • which manages a list of settings group headers • onBuildHeaders invoked automatically at appropriate point • load header definitions from XML Preferences, Profiling, Debugging 11 PreferenceFragment We have a generic fragment implementation for "settings_group" holding a group of settings android:value=“location_settings" /> <extra android:name= public class UserSettingsActivity extends PreferenceActivity { @Override public void onBuildHeaders(List<Header> target) { loadHeadersFromResource(R.xml.preference_headers, target); } public static class UserSettingsFragment extends PreferenceFragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String settings = getArguments().getString("settings_group"); if (”location_settings".equals(settings)) { addPreferencesFromResource(R.xml.settings_location); } else if ("startup_settings".equals(settings)) { addPreferencesFromResource(R.xml.settings_startup); } } } }Preferences, Profiling, Debugging when the header XML is loaded a SettingsFragment instance is created for each header read the argument to identify the settings group the UI widgets, layout and default values are defined in XML 12 Preference XML One XML file for each group/header Entries can be organised by category root elements is always a PreferenceScreen <?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" > <PreferenceCategory android:title="@string/pref_previous_session_category" > <CheckBoxPreference android:key="pref_restore" categories are optional – we android:summary="@string/pref_restore_summary" could just have a list of android:title="@string/pref_restore_title" /> preference elements </PreferenceCategory> <PreferenceCategory android:title="@string/pref_startup_location_category" > <CheckBoxPreference android:key="pref_startup_location" android:summary="@string/pref_startup_location_summary" android:title="@string/pref_startup_location_title" /> </PreferenceCategory> key identifies this particular preference value </PreferenceScreen> Preferences, Profiling, Debugging 13 Preference XML One XML file for each group/header Entries can be organised by category <?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" > <PreferenceCategory android:title="@string/pref_previous_session_category" > <CheckBoxPreference android:key="pref_restore" label and descriptive text android:summary="@string/pref_restore_summary" android:title="@string/pref_restore_title" /> </PreferenceCategory> <PreferenceCategory android:title="@string/pref_startup_location_category" > <CheckBoxPreference android:key="pref_startup_location" android:summary="@string/pref_startup_location_summary" android:title="@string/pref_startup_location_title" /> </PreferenceCategory> </PreferenceScreen> Preferences, Profiling, Debugging 14 Preference XML One XML file for each group/header Entries can be organised by category <?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" > <PreferenceCategory android:title="@string/pref_previous_session_category" > <CheckBoxPreference android:key="pref_restore" label and descriptive text android:summary="@string/pref_restore_summary" android:title="@string/pref_restore_title" /> </PreferenceCategory> <PreferenceCategory android:title="@string/pref_startup_location_category" > <CheckBoxPreference android:key="pref_startup_location" android:summary="@string/pref_startup_location_summary" android:title="@string/pref_startup_location_title" /> </PreferenceCategory> </PreferenceScreen> Preferences, Profiling, Debugging 15 Preference dependencies A preference sometimes depends upon the value of another to be valid Preferences, Profiling, Debugging 16 Preference dependencies Simple to express <?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" > <PreferenceCategory android:title="@string/pref_location_category" > <CheckBoxPreference android:key="pref_allow_location" android:summary="@string/pref_allow_location_summary" android:title="@string/pref_allow_location_title" /> <CheckBoxPreference android:dependency="pref_allow_location" android:key="pref_location_network" android:summary="@string/pref_location_network_summary" android:title="@string/pref_location_network_title" /> <CheckBoxPreference android:dependency="pref_allow_location" android:key="pref_location_gps" android:summary="@string/pref_location_gps_summary" android:title="@string/pref_location_gps_title" /> </PreferenceCategory> </PreferenceScreen> Preferences, Profiling, Debugging 17 Preference types CheckBoxPreference • standard on/off tick box UI, boolean value SwitchPreference • two state switch UI, configurable on/off text, boolean value EditTextPreference • editable text box displayed in a dialog, string value ListPreference • single selection from a list displayed in a dialog, string value Preferences, Profiling, Debugging 18 Preference types MultiSelectListPreference • multiple selections for a list displayed in a dialog, value is a set of strings RingtonePreference • select from list of ringtones on device, value is URI of ringtone resource Preferences, Profiling, Debugging 19 Preference defaults We need sensible preference values for when the user first runs the app <?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" > <PreferenceCategory android:title="@string/pref_previous_session_category" > <CheckBoxPreference android:defaultValue="false" android:key="pref_restore" android:summary="@string/pref_restore_summary" android:title="@string/pref_restore_title" /> </PreferenceCategory> <PreferenceCategory android:title="@string/pref_startup_location_category" > <CheckBoxPreference android:defaultValue="true" android:key="pref_startup_location" android:summary="@string/pref_startup_location_summary" android:title="@string/pref_startup_location_title" /> </PreferenceCategory> </PreferenceScreen> Preferences, Profiling, Debugging 20 Handling preferences in code We now have the preference UI plus persistent storage of the settings values (automatically managed by Preference framework) Need to • ensure default values are loaded on first run • read values to affect behaviour • know when a preference has changed (eg to change summary text to reflect current value) PreferenceManager class provides the support Preferences, Profiling, Debugging 21 Preferences at run time The current state of preferences needs to be stored persistently on the device XML files are created/updated by the system in the app’s private data folder /data/data/org.stevej.android.propertyfinder/shared_prefs/ org.stevej.android.propertyfinder_preferences.xml _has_set_default_values.xml reflects whether the default values have been loaded and set at any point in the past Preferences, Profiling, Debugging current values of the preferences this is the default location of the preference value file 22 Preferences at run time XML files in the app’s private data folder org.stevej.android.propertyfinder_preferences.xml <?xml version='1.0' encoding='utf-8' standalone='yes'?> <map> <boolean name="pref_startup_location" value="false" /> <boolean name="pref_location_network" values of the preferences at the current value="true" /> point in time <boolean name="pref_allow_location" note that this is all preferences from the value="false" /> 2 preference XML files <boolean name="pref_restore" value="true" /> <boolean name="pref_location_gps" value="true" /> </map> Preferences, Profiling, Debugging 23 Preferences at run time XML files in the app’s private data folder _has_set_default_values.xml <?xml version='1.0' encoding='utf-8' standalone='yes'?> <map> <boolean name="_has_set_default_values" value="true" /> </map> Preferences, Profiling, Debugging 24 Preferences: load defaults Do this at each entry point into the app – just the PropertyFinderActivity for us public class PropertyFinderActivity extends Activity implements PropertySelectionListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); PreferenceManager.setDefaultValues(this, R.xml.settings_location, true); PreferenceManager.setDefaultValues(this, R.xml.settings_startup, true); although we provide an activity as an argument the prefs are for the whole application Preferences, Profiling, Debugging the file to load default value from whether to re-read default values from the given file depending upon whether or not they have been read in the past internal state represented in _has_set_default_values.xml 25 Preferences: load defaults setDefaultValues • setting re-read=TRUE does NOT mean ‘reset to defaults’ • somewhat unclear documentation Whether to re-read the default values. If false, this method sets the default values only if this method has never been called in the past (or if the KEY_HAS_SET_DEFAULT_VALUES in the default value shared preferences file is false). To attempt to set the default values again bypassing this check, set readAgain to true. Note: this will NOT reset preferences back to their default values. For that functionality, use getDefaultSharedPreferences(Context) and clear it followed by a call to this method with this parameter set to true. Preferences, Profiling, Debugging 26 Preferences: load defaults setDefaultValues actual behaviour • if it has never been called before: read given file and create/initialise preferences with the default values (value of re-read doesn’t matter) • if it has been called before and re-read is FALSE: do nothing • if it has been called before and re-read is TRUE: read given file and create/initialise preferences that don’t already exist with the default values Preferences, Profiling, Debugging 27 Preferences: load defaults if it has been called before and re-read is TRUE • read given file and create/initialise preferences that don’t already exist with the default values This may occur if we publish new version of our app that introduces new preferences So this is OK PreferenceManager.setDefaultValues(this, R.xml.settings_location, true); PreferenceManager.setDefaultValues(this, R.xml.settings_startup, true); Preferences, Profiling, Debugging 28 Preferences: reading in code Access anywhere in our app get application preferences from the default location (see slide 22) an activity if does NOT mean default values SharedPreferences shared_prefs = PreferenceManager.getDefaultSharedPreferences(this); boolean restore = shared_prefs.getBoolean("pref_restore", true); CheckBoxPreference has a Boolean value Preferences, Profiling, Debugging key we used in the preference XML value if the preference can’t be found 29 Preferences: defining custom types We need to let the user set the number of properties to retrieve from TradeMe Could use • an EditTextPreference and require user to type or • the Android NumberPicker UI component Second is a better user experience, but no NumberPickerPreference class Preferences, Profiling, Debugging 30 Preferences: defining custom types Implement NumberPickerPreference class • extends DialogPreference DialogPreference displays a pop up containing the UI for a preference and has the logic for managing the preference’s value This is how the built-in EditTextPreference is implemented, for example Preferences, Profiling, Debugging 31 Preferences: defining custom types NumberPickerPreference.java • UI widget and its current/default values as instance variables • load layout for dialog from XML layout definition • set buttons to ‘OK’ and ‘Cancel’, no special icon public class NumberPickerPreference extends DialogPreference { private NumberPicker number_picker; private final int default_value = 10; private int current_value; public NumberPickerPreference(Context context, AttributeSet attrs) { super(context, attrs); setDialogLayoutResource(R.layout.numberpicker_dialog); setPositiveButtonText(android.R.string.ok); setNegativeButtonText(android.R.string.cancel); setDialogIcon(null); Preferences, Profiling, Debugging } 32 Preferences: defining custom types numberpicker_dialog.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > android.widget.NumberPicker <NumberPicker android:id="@+id/num_to_load_picker" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout> Preferences, Profiling, Debugging 33 Preferences: defining custom types NumberPickerPreference.java • initialise UI automatically called when we are able to access UI elements (view exists) @Override protected void onBindDialogView(View view) { super.onBindDialogView(view); number_picker = (NumberPicker) view.findViewById(R.id.num_to_load_picker); number_picker.setMinValue(1); configure the NumberPicker number_picker.setMaxValue(50); UI widget number_picker.setValue(current_value); number_picker.setWrapSelectorWheel(true); number_picker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS); } a number picker contains an EditText we don’t want the user to click by accident and get keyboard input Preferences, Profiling, Debugging 34 Preferences: defining custom types NumberPickerPreference.java • deal with default value automatically called when the default value needs to be read @Override protected Object onGetDefaultValue(TypedArray attr, int index) { return attr.getInteger(index, default_value); } return an integer the default value from XML attributes or that defined in the Java class if no default value in XML Preferences, Profiling, Debugging index in the attributes of the defaultValue the attributes of the NumberPickerPreference object 35 Preferences: defining custom types NumberPickerPreference.java • set initial state when preference shown to user called automatically indicates if a value for the preference has already been stored the preference’s default value @Override protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) { if (restorePersistedValue) { attempt to get the stored value storeInitialValue(getPersistedInt(current_value)); if it’s not available use the } else { current_value storeInitialValue((Integer) defaultValue); } } if no stored value use the default value provided private void storeInitialValue(Integer value) { current_value = value; persistInt(current_value); will received either the stored or } built in method for explicitly storing a Preference value Preferences, Profiling, Debugging default value, update current value and store it 36 Preferences: defining custom types NumberPickerPreference.java • store value when user closes dialog called automatically indicates if user clicked OK @Override protected void onDialogClosed(boolean positiveResult) { if (positiveResult) { current_value = number_picker.getValue(); persistInt(current_value); } } Preferences, Profiling, Debugging only use value if user clicked OK retrieve UI widget value and store it as this preference’s value 37 Preferences: defining custom types NumberPickerPreference.java • save/restore state • need to do a bit more work than with Activities and Fragments • you can look at implementation of these methods @Override protected void onRestoreInstanceState(Parcelable state) { } @Override protected Parcelable onSaveInstanceState() { } private static class SavedState extends BaseSavedState { } Preferences, Profiling, Debugging 38 Preferences: defining custom types Add a TradeMe group to our preference headers <?xml version="1.0" encoding="utf-8"?> <preference-headers xmlns:android="http://schemas.android.com/apk/res/android" > <!-- snip --> <header android:fragment="org.stevej.android.propertyfinder7.UserSettingsActivity$UserSettingsFragment" android:summary="@string/pref_group_trademe_summary" android:title="@string/pref_group_trademe_title" > <extra android:name="settings_group" android:value="trademe_settings" /> </header> </preference-headers> Preferences, Profiling, Debugging 39 Preferences: defining custom types Define the TradeMe preferences screen • settings_trademe.xml <?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" > <PreferenceCategory android:title="@string/pref_trademe_request_category" > <org.stevej.android.propertyfinder7.NumberPickerPreference android:defaultValue="10" android:key="pref_num_to_load" android:summary="@string/pref_num_to_load_summary" android:title="@string/pref_num_to_load_title" /> </PreferenceCategory> use our custom preference type </PreferenceScreen> just like a normal built-in preference Preferences, Profiling, Debugging 40 Preferences: defining custom types Deal with new preference group in our preference fragment public static class UserSettingsFragment extends PreferenceFragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String settings = getArguments().getString("settings_group"); if ("location_settings".equals(settings)) { addPreferencesFromResource(R.xml.settings_location); } else if ("startup_settings".equals(settings)) { addPreferencesFromResource(R.xml.settings_startup); } else if ("trademe_settings".equals(settings)) { addPreferencesFromResource(R.xml.settings_trademe); } } } Preferences, Profiling, Debugging 41 Preferences: defining custom types Define the dialog layout <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <NumberPicker android:id="@+id/num_to_load_picker" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout> Preferences, Profiling, Debugging 42 Preferences: defining custom types Using the value (in PropertyListFragment) SharedPreferences shared_prefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); int num_to_load = shared_prefs.getInt("pref_num_to_load",10); Bundle params = new Bundle(); params.putString("rows", Integer.toString(num_to_load)); params.putString("suburb", "1241"); params.putString("district", "14"); params.putString("region", "16"); intent.putExtra(RESTClient.EXTRA_PARAMS, params); intent.putExtra(RESTClient.EXTRA_RESULT_RECEIVER, result_receiver); getActivity().startService(intent); Preferences, Profiling, Debugging 43 Preferences: reacting to changes We sometimes want to react immediately to changes • update ‘num to load’ summary to reflect current value Register the preference fragment as a listener public static class UserSettingsFragment extends PreferenceFragment implements OnSharedPreferenceChangeListener { @Override public void onResume() { super.onResume(); SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity()); sharedPref.registerOnSharedPreferenceChangeListener(this); } @Override public void onPause() { super.onPause(); SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity()); sharedPref.unregisterOnSharedPreferenceChangeListener(this); } Preferences, Profiling, Debugging 44 Preferences: reacting to changes Register the preference fragment as a listener @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { Preference changed_preference = findPreference(key); if (changed_preference != null) { if (key.equals(“pref_num_to_load”)) { int val = sharedPreferences.getInt(key, 10)); changed_preference.setSummary(“Maximum to load from TradeMe (" + val + ")"); } } } Preferences, Profiling, Debugging 45 Preferences: using resource constants A problem is that the key, getInt default value and summary string are hard coded Define/use resource values <?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" > <PreferenceCategory android:title="@string/pref_trademe_request_category" > <org.stevej.android.propertyfinder7.NumberPickerPreference android:defaultValue="@integer/default_num_to_load" android:key="@string/pref_num_to_load_key" android:summary="@string/pref_num_to_load_summary" android:title="@string/pref_num_to_load_title" /> </PreferenceCategory> </PreferenceScreen> Preferences, Profiling, Debugging 46 Preferences: using resource constants Define/use resource values • res/values/strings.xml <?xml version="1.0" encoding="utf-8"?> <resources> <!-- snip --> <string name="pref_num_to_load_key">pref_num_to_load</string> <string name="pref_num_to_load_title">Number of properties</string> <string name="pref_num_to_load_summary">Maximum number of properties retrieved when searching TradeMe</ string> </resources> • res/values/integers.xml <?xml version="1.0" encoding="utf-8"?> <resources> <integer name="default_num_to_load">10</integer> </resources> Preferences, Profiling, Debugging 47 Preferences: using resource constants Use resource values get the app’s resources get the value of the string resource with this id @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { Resources r = getActivity().getResources(); now when we read the pref value provide it with the integer resource Preference changed_preference = findPreference(key); constant as the default value if (changed_preference != null) { if (key.equals(r.getString(R.string.pref_num_to_load_key))) { int val = sharedPreferences.getInt(key, r.getInteger(R.integer.default_num_to_load)); String summary = r.getString(R.string.pref_num_to_load_summary); changed_preference.setSummary(summary + " (" + val + ")"); } } } get the default summary string from resources and modify it Preferences, Profiling, Debugging update the summary text 48 Profiling: memory analysis Three main tools Allocation Tracker in DDMS • see what memory allocations are occuring Heap dump/monitor • overview of heap size changes • summary of allocated objects Memory Analysis Tool plugin for Eclipse • detailed analysis including leak finding Preferences, Profiling, Debugging 49 Profiling: memory analysis Allocation Tracker • get app to state at which you want to start tracking • click Start Tracking • carry out ‘interesting’ operations in the app • click Get Allocations Preferences, Profiling, Debugging 50 Profiling: memory analysis Allocation Tracker • click Get Allocations (this is just for rotating from portrait to landscape) type and size of allocated objects call stack for the selected allocation including line in code that caused the allocation Preferences, Profiling, Debugging 51 Profiling: memory analysis Heap monitoring • select app process, click Update Heap button • click Cause GC to get first snapshot • snapshot updated automatically after every garbage collection Preferences, Profiling, Debugging 52 Profiling: memory analysis Heap monitoring • the allocated value might fluctuate • but we don’t want it to have a tendency to increase inexplicably over the lifetime of the app – memory leak, or some problem with our code logic • we have a problem in PropertyFinder Preferences, Profiling, Debugging 53 Profiling: memory analysis Install MAT plugin for Eclipse Preferences, Profiling, Debugging 54 Profiling: memory analysis Change Eclipse Android preferences to open HPROF dump files in Eclipse Preferences, Profiling, Debugging 55 Profiling: memory analysis In DDMS perspective • select your running app • click “Dump HPROF file” button • gets current heap state Preferences, Profiling, Debugging 56 Profiling: memory analysis Memory Analysis Tools (MAT) view opens with the profiling data FrameLayout ArrayList Bitmap everything else Preferences, Profiling, Debugging 57 Profiling: memory analysis Histogram • group into packages • sort by number of objects • sort by heap used why 119? we are only loading a few for Hillcrest Preferences, Profiling, Debugging 58 Profiling: memory analysis Do more stuff with app • eg rotate • dump again • select Compare • shows difference between the two heap dumps • rotation created more Property objects! Preferences, Profiling, Debugging 59 Profiling: memory analysis We can drill down into the heap data • reveals the issue is in PropertyListFragment • we are making another request to TradeMe every time the activity is created (eg on rotation) • and just adding the returned properties to the list, even though they are the same Preferences, Profiling, Debugging 60 Next Using locations sensors to get user’s location and then load nearby properties from TradeMe (and then we’ll address being cleverer about not reloading from TradeMe, storing data in an app database) Preferences, Profiling, Debugging 61