Lifecycle Android Programming
Logging
- public static final String TAG = “Lifecycle”;
- …
- Log.v(TAG, “Hello!”);
- There’s Log.e() for errors, etc. — http://developer.android.com/reference/android/util/Log.html
- In Eclipse/debug .. set Log/Tag filter to “Lifecycle” to just see your logs vs. all the others
- System.err.println(“Hello”); also works (without the tag)
- If the log stops updating in Eclipse, I have found that restarting Eclipse often reconnects to the emulator to get the logs going
Battery Life
- There’s no Moore’s law for batteries. We want to careful about not using CPU pointlessly, and your teacher is a stickler for this detail. Many parts of android are the way the are because of battery life matters.
Phone App Paradigm: no Quit
- List of phone app features: small, fast/instantaneous, personal, touch interface. Today add: there’s no “quit” command or state for the user to think about. The app should appear as it was left, even though the system may have done a kill/restart on it at some point; the user should not be able to tell.
- In a way this gets back to battery life — the system can pause or kill stuff off to save energy, and make it so the user can’t tell.
Activity Lifecycle Outline
Some aspects of the lifecycle are tricky, so I want to be careful that we get them right. Much of the behavior you want happens automatically, but you need to understand the underlying sequence.
Here’s the broad outline of the various onXXX() “notification” methods called on an activity, signaling its shift from one state to another. There are some other states omitted here, as they do not come up for the common code patterns.
- onCreate() — create and setup the activity object, load “static” resources like images, layouts, set up menus etc. After this, the Activity object exists.
- onResume() — about to start running, being on screen
- Running
- onPause() — going to background, so stop animations, stop running threads, so the activity sits quietly. After this, the object, its variables, views etc. will very likely continue to exist in ram. However, in some cases after this the activity will be killed and deleted, so we will have to think about saving un-saved state, see onSaveInstanceState() below. Code that runs here should be fast, since the startup of the next activity waits for it.
- onDestroy() — matching onCreate(), the app is being destroyed. May not be called in extreme cases.
- There’s also onStart() before onResume() and onStop() before onDestroy(), but IMHO those are rarely needed, so we’ll ignore them them for now.
1. Super-Common, No-Kill Case
- At first we will deal with this no-kill case — we have pausing, but the object still exists.
- onCreate
- onResume() .. onPause() many times, as switch activities, screen on/off
- onDestroy() happens eventually
- What your code needs to do: maybe nothing, the object is in RAM for the whole pause/resume cycle
1a. Add Animation
- Add animation, starting with the Caffeine Mode checkbox.
- Add Checkbox widget to layout
- Add checkbox listener: factor out a utility checkboxToAnimation() method on click. That method looks at checkbox, starts/stops animation.
1b. Make Animation work with onPause/onResume
- onPause: stop the animation (classic onPause/battery-life move)
- onResume: make the animation consistent with the checkbox, i.e. call our utility method
1c. Common Case Summary
- Very often you don’t have animation or threads — just a lot of widgets
- Therefore often no code is required then for the common onPause()/onResume() cycle. Just understand that the cycle probably happens a few times to your activity before it is destroyed.
2. The Kill Case
- The big jump is when the system kills and deletes the activity, say because memory is getting tight.
- Hard to debug, since most often the system does not do this, so your debugging sessions are just not seeing this case.
- In the emulator, check this option: run Dev Tools > Development Settings > Immediately Destroy Activities (this setting persists, even as you restart the emulator)
- Then when you click Home from your app, it is destroyed
- The destroy case can happen to any app in reality, this just makes it always happen for testing purposes. This is an invaluable setting for testing this case.
- In the kill/destroy case, the activity object is deleted. The user can still go back to the activity, and in that case the system calls onCreate() etc. to re-create the activity. The user should not be able to tell that the object was deleted.
- Demo: set up state in text field, then go Home so the app is killed. Then bring it back. How did this work?
2a. onSaveInstanceState
- onSaveInstanceState() is called for the activity to save RAM state to Bundle (basically a hash table).
- If the activity is re-created, this same bundle is passed in to onCreate(), so there you can look in the bundle to set things back the way they were.
- The built-in widgets (EditText, Checkbox, …) use the bundle to save their state automatically.
- We write no code, and the UI elements should have this basic save/restore quality when the activity goes through a kill/create cycle.
- Look at the logs carefully, see the bundle getting saved in onSaveInstanceState(), later passed in to onCreate().
- Experiment: comment out the call to super.onSaveInstanceState() to see how the kill/create cycle looks without the automatic state saving. (in lecture, I tried this by setting the bundle to null, but that did not break the automatic behavior.)
2c. Kill Case Summary
- The activity object is deleted, needs to be re-created if the user goes back to it. The built-in UI widgets automatically use Bundle to save and re-create their state.
- So if the “state” of your screen is just the state of its widgets: you don’t have to do anything. The fact that so much works automatically is probably and additional reason that some android programmers are a little fuzzy on the details of the activity lifecycle.
Got this far in lecture
3. The “State” Case
- Say we want the button to count how many times it has been clicked, e.g. private int mClickCount;
- That ivar will be cleared by the kill/create cycle .. the object is deleted from RAM after all.
- It is not a UI widget, so it is not saved that way.
- Add button listener to bump up mClickCount, change the text
- Demo: run the app with the kill cycle … see that mClickCount is 0
- The code to fix this case is controlled by the SAVE_CORRECT flag in the demo code
- 1. Save part of fix: save mClickCount int to bundle in onSaveInstanceState()
- 2. Create part of fix: in onCreate(), retrieve int and put it in mClickCount
- Demo: try kill cycle again .. app state / mClickCount is correct
package edu.stanford.nick; import android.app.Activity; import android.graphics.Color; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.view.animation.AnimationUtils; import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.TextView; public class LifecycleActivity extends Activity { public static final String TAG = "Lifecycle"; // Save mClickCount correctly or not. public static final boolean SAVE_CORRECT = false; private TextView mTextView; private Button mButton; private int mClickCount = 0; private CheckBox mCheckbox; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { Log.v(TAG, "onCreate:" + savedInstanceState); super.onCreate(savedInstanceState); if (savedInstanceState!=null && SAVE_CORRECT) { mClickCount = savedInstanceState.getInt("mClickCount"); } setContentView(R.layout.main); // Checkbox - Animation mCheckbox = (CheckBox) findViewById(R.id.checkBox1); mCheckbox.setOnCheckedChangeListener(new OnCheckedChangeListener() { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked){ checkboxToAnimation(); } }); // Looks like you maybe want to start the animation here. // However, best to do it in onResume() -- "bottleneck" strategy. // Button / clickCount - change the text depending on how many // times the button has been clicked (simple example of state to save). mTextView = (TextView) findViewById(R.id.textView1); mButton = (Button) findViewById(R.id.button1); mButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { mClickCount++; if (mClickCount == 1) { mTextView.setText("Please do not click this button again."); } else if (mClickCount >= 2) { mTextView.setTextColor(Color.parseColor("#FF0000")); // Can store color in res: // strings.xml:#FF0000 // then: mTextView.setTextColor(R.color.mycolor1); } } }); } @Override public void onResume() { Log.v(TAG, "onResume"); super.onResume(); checkboxToAnimation(); } /** Looks at the state of the checkbox, and start/stop the animation appropriately.*/ private void checkboxToAnimation() { if (mCheckbox.isChecked()) { mButton.startAnimation(AnimationUtils.loadAnimation(this, R.anim.pulse)); } else { mButton.clearAnimation(); } } @Override public void onPause() { Log.v(TAG, "onPause"); super.onPause(); mButton.clearAnimation(); } @Override protected void onSaveInstanceState(Bundle outState) { Log.v(TAG, "onSaveInstanceState:" + outState); super.onSaveInstanceState(outState); if (SAVE_CORRECT) { outState.putInt("mClickCount", mClickCount); } } @Override public void onDestroy() { Log.v(TAG, "onDestroy"); super.onDestroy(); } }
Homework 3
Add to your existing app or create a new one — you don’t need to turn this in, it’s just for practice.
- The Dev Tools app should be present in the emulator automatically. To install in a real phone, see http://developer.android.com/guide/developing/debugging/debugging-devtools.html
- Add a slider (SeekBar) and EditText to a new or existing app. Add a listener to the slider, that sees its current value. For now just log the value; we’ll use it for the next homework.
- Override and add logging for onCreate(), onResume(), onPause(), onDestroy(), and onSaveInstanceState(). Play around with your app, going to the home screen, with and without the “destroy immediately” option, see when the widgets keep their state.
- Here’s what we’ll add, among other things, next week:
- Add a “Undo” button that resets the seek bar to its previous setting
- Do state saving correctly, so the Undo works correctly, even after a kill cycle.
Regards
Owntutorials | Cameron Thomas
MaximeTech | Best IT solutions Provider | Web Development |Mobile Applications
No comments:
Post a Comment