Download Developing Mobile Applications

Document related concepts
no text concepts found
Transcript
Developing Mobile Applications
Week 6
Preferences, Profiling, Debugging
Steve Jones
Department of Computer Science
University of Waikato
stevej@waikato.ac.nz
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