Intro/tutorial-ex3
From Android中文网
目录 |
[编辑] 记事本练习 3
在本练习中,你将使用生命周期事件回调方法保存和恢复应用程序状态数据。本练习示范了如下内容:
- 什么是生命周期事件及如何使用它们
- 维护应用程序状态的技术
[编辑] 第一步
目前的例程还存在一个重大的问题:当编辑的时候按下返回键会引发一个致命错误,而发生了任何其他打断编辑的事件都会使当前编辑的内容丢失。
要修复这个错误,我们需要将大部分创建和编辑的功能转移到类NoteEdit中,并且赋予NoteEdit一个完整的生命周期。
将Notepadv3工程引入到Eclipse中。如果你发现AndroidManifest.xml有错误提示,或者任何与Android SDK包相关的错误提示,可以右击项目,选择Android Tools->Fix Project对项目进行修复。
Properties from the popup menu. The starting point for this exercise is exactly the same as the solution for Notepadv2.
- Remove the NoteEdit code to parse out the title and body from the extras bundle. We are going to use the DBHelper class to access the notes from the database directly. All we need passed into the activity is a rowId (if we are editing, if creating we don't need anything). We will get rid of the properties that were being passed in through the extras bundle that we were using to set the title and body text edit values in the UI.
- Remove the lines that read:
String title = extras.getString(Notepadv3.KEY_TITLE); String body = extras.getString(Notepadv3.KEY_BODY);and
if (title != null) { titleText.setText(title); } if (body != null) { bodyText.setText(body); }(Extra credit, you could replace the remaining if (extras != null) block with a ternary operator as well:
rowId = extras != null ? extras.getLong(Notepadv3.KEY_ROW_ID) : null;
当前的例子(译者:指的是练习(2)中的代码)有些问题——在处于编辑状态时,点击“返回”会是应用程序崩溃,并丢失为保存的编辑数据。
为了修复这个问题,我们将为NoteEdit类注入更多的创建和编辑记事功能,并介绍编辑记事的完整生命周期。
把Notepadv3导入Eclipse。如果你看到关于AndroidManifest.xml的错误,或相关的一些问题,请右键点击项目,并在弹出菜单中选择Android Tools -> Fix Project Properties。这个练习的起点正是Notepadv2解决方案。
- Remove the NoteEdit code to parse out the title and body from the extras bundle. We are going to use the DBHelper class to access the notes from the database directly. All we need passed into the activity is a rowId (if we are editing, if creating we don't need anything). We will get rid of the properties that were being passed in through the extras bundle that we were using to set the title and body text edit values in the UI.
- Remove the lines that read:
String title = extras.getString(Notepadv3.KEY_TITLE); String body = extras.getString(Notepadv3.KEY_BODY);and
if (title != null) { titleText.setText(title); } if (body != null) { bodyText.setText(body); }(Extra credit, you could replace the remaining if (extras != null) block with a ternary operator as well:
rowId = extras != null ? extras.getLong(Notepadv3.KEY_ROW_ID) : null;
[编辑] Step 2
Create a class field for a DBHelper at the top of the class:
private DBHelper dbHelper;
and an instance of DBHelper in the onCreate() method (right below the super.onCreate() call):
dbHelper = new DBHelper(this);
[编辑] Step 3
We need to check the icicle for a rowId in case the note editing has been frozen and thawed:
Replace the code that currently initializes the rowId:
rowId = null;
Bundle extras = getIntent().getExtras();
if (extras != null) {
rowId = extras.getLong(Notepadv3.KEY_ROW_ID);
}
with
rowId = icicle != null ? icicle.getLong(Notepadv3.KEY_ROW_ID) : null;
if (rowId == null) {
Bundle extras = getIntent().getExtras();
rowId = extras != null ? extras.getLong(Notepadv3.KEY_ROW_ID) : null;
}
Note the null check for icicle, and we still need to load up rowId from the extras bundle if it is not provided by the icicle. This is a ternary operator shorthand to safely either use the value or null if it is not present.
[编辑] Step 4
Next, we need to populate the fields based on the rowId if we have it:
populateFields(); before the confirmButton.setOnClickListener() line.
[编辑] Step 5
Get rid of the bundle creation and bundle value settings from the onClick() handler method. The Activity no longer needs to return any extra information to the caller. You can also use the shorter version of setResult():
public void onClick(View arg0) {
setResult(RESULT_OK);
finish();
}
We will take care of storing the updates or new notes in the database ourselves using the lifecycle methods.
The whole onCreate() method should now look like this:
super.onCreate(icicle);
dbHelper = new DBHelper(this);
setContentView(R.layout.note_edit);
titleText = (EditText) findViewById(R.id.title);
bodyText = (EditText) findViewById(R.id.body);
Button confirmButton = (Button) findViewById(R.id.confirm);
rowId = icicle != null ? icicle.getLong(Notepadv3.KEY_ROW_ID) : null;
if (rowId == null) {
Bundle extras = getIntent().getExtras();
if (extras != null) {
rowId = extras.getLong(Notepadv3.KEY_ROW_ID);
}
}
populateFields();
confirmButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0) {
setResult(RESULT_OK);
finish();
}
});
[编辑] Step 6
Define the populateFields() method.
private void populateFields() {
if (rowId != null) {
DBHelper.Row row = dbHelper.fetchRow(rowId);
if (row.rowId > -1) {
titleText.setText(row.title);
bodyText.setText(row.body);
}
}
}
[编辑] Step 7
Why handling lifecycle events is important If you are used to always having control in your applications, you might not understand why all this lifecycle work is necessary. The reason is that in Android, you are not in control of your activity, the operating system is!
As we have already seen, the Android model is based around Activities calling each other. When one Activity calls another, the current activity is paused at the very least, and may be killed altogether if the system starts to run low on resources. If this happens, your Activity will have to store enough state to come back up later, preferably in the same state it was in when it was killed.
Android has a well-defined lifecycle. Lifecycle events can happen even if you are not handing off control to another activity explicitly. For example, perhaps a call comes in to the handset. If this happens, and your activity is running, it will be swapped out while the call activity takes over. Override methods onFreeze(), onPause() and onResume()
These are our lifecycle methods (along with onCreate() which we already have).
onFreeze() is called by Android if the Activity is being stopped and may be killed before it is resumed! This means it should store any state necessary to re-initialize to the same condition when the activity is restarted. It is the counterpart to the onCreate() method, and in fact the icicle bundle passed in to onCreate() is the same bundle that you construct as outState in the onFreeze() method.
onPause() and onResume() are also complimentary methods. onPause() is always called when the Activity ends, even if we instigated that (with a finish call for example). We will use this to save the current note back to the database. Good practice is to release any resources that can be released during an onPause() as well, to take up less resources when in the passive state. For this reason we will close the DBHelper class and set the field to null so that it can be garbage collected if necessary. onResume() on the other hand, will re-create the dbHelper instance so we can use it, and then read the note out of the database again and populate the fields.
onFreeze():
@Override
protected void onFreeze(Bundle outState) {
super.onFreeze(outState);
outState.putLong(Notepadv3.KEY_ROW_ID, rowId);
}
onPause():
@Override
protected void onPause() {
super.onPause();
saveState();
dbHelper.close();
dbHelper = null;
}
onResume():
@Override
protected void onResume() {
super.onResume();
if (dbHelper == null) {
dbHelper = new DBHelper(this);
}
populateFields();
}
[编辑] Step 8
Define the saveState() method to put the data out to the database.
private void saveState() {
String title = titleText.getText().toString();
String body = bodyText.getText().toString();
if (rowId == null) {
dbHelper.createRow(title, body);
} else {
dbHelper.updateRow(rowId, title, body);
}
}
[编辑] Step 9
Now pull out the previous handling code from the onActivityResult() method in the Notepadv3 class
All of the note retrieval and updating now happens within the NoteEdit lifecycle, so all the onActivityResult() method needs to do is update its view of the data, no other work is necessary. The resulting method should look like this:
@Override
protected void onActivityResult(int requestCode, int resultCode,
String data, Bundle extras) {
super.onActivityResult(requestCode, resultCode, data, extras);
fillData();
}
Because the other class now does the work, all this has to do is refresh the data.
[编辑] Step 10
Also remove the lines which set the title and body from the onListItemClick() method (again they are no longer needed, only the rowId is):
i.putExtra(KEY_TITLE, rows.get(position).title);
i.putExtra(KEY_BODY, rows.get(position).body);
Run it! (use Run As -> Android Application on the project right click menu again)
[编辑] Solution and Next Steps
You can see the solution to this exercise in Notepadv3Solution from the zip file to compare with your own.
When you are ready, move on to the Tutorial Extra Credit exercise, where you can use the Eclipse debugger to examine the lifecycle events as they happen.
Back to the Tutorial main page...
