教程:Notepad练习3
这个练习中,你将会使用生命周期事件回调来保存和取回应用程序状态数据。这个练习说明:
1、生命周期事件和你的程序怎么能使用生命周期事件
2、维持应用程序状态的技术
第一步
导入Notepadv3进入Eclipse。这个练习的开始点正好是Notepadv2最后停止的地方。
目前这个程序有一些问题——当编辑导致崩溃时点击回退按钮,其他在编辑期间发生的任何事情将会导致编辑数据丢失。
为了修正这个问题,我们将会把创建和编辑便签的大部分功能移到NoteEdit类,针对编辑便签功能介绍一个完整的生命周期。
1、删除NoteEdit中从额外Bundle分析标题和内容的代码
作为替换,我们打算使用DBHelper类来从数据库中直接访问便签。我们需要传入到NoteEdit活动的参数只有mRowId(但是仅仅在编辑时,创建时我们不传入任何参数)。删除下面的行:
String title = extras.getString(NotesDbAdapter.KEY_TITLE);
String body = extras.getString(NotesDbAdapter.KEY_BODY);
2、我们也会除去在额外Bundle中传入的属性,这些属性用于设置UI中标题和内容文本编辑框的值。因此删除:
if (title != null) {
mTitleText.setText(title);
}
if (body != null) {
mBodyText.setText(body);
}
第二步
在NoteEdit类的顶部,创建NotesDbAdapter的私有属性:
private NotesDbAdapter mDbHelper;
也在onCreate()方法中增加NotesDbAdapter的实例(在super.onCreate()调用的右下面)
mDbHelper = new NotesDbAdapter(this);
mDbHelper.open();
第三步
在NoteEdit中,我们需要为了mRowId检查savedInstanceState,万一正在编辑的便签包含Bundle的一个已经保存的状态,我们应该恢复这个状态(如果Activity失去焦点然后重新启动,恢复动作将会发生)
1、替换目前的初始化mRowId的代码:
mRowId = null;
Bundle extras = getIntent().getExtras();
if (extras != null) {
mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
}
为:
mRowId = savedInstanceState != null ? savedInstanceState.getLong(NotesDbAdapter.KEY_ROWID)
: null;
if (mRowId == null) {
Bundle extras = getIntent().getExtras();
mRowId = extras != null ? extras.getLong(NotesDbAdapter.KEY_ROWID)
: null;
}
2、注意检查savedInstanceState的null值,我们仍然需要从额外Bundle中加载mRowID,如果mRowID没有被savedInstanceState提供。这是一个三元操作符的简写,以便保证不是使用这个值就是null,不管它是否满足条件。
第四步
下面,我们需要填充基于mRowID的字段:
populateFields();
这行代码在confirmButton.setOnClickListener()之前运行。我们会待会儿定义这个方法。
第五步
从onClick()处理函数中除去Bundle创建和Bundle值设置.Activity不再需要返回任何附加信息到调用者。因为我们不再有返回的Intent,我们会使用setResult()的更简版本:
public void onClick(View view) {
setResult(RESULT_OK);
finish();
}
我们会实现功能:保存修改或者新建的便签到数据库中,使用生命周期方法。
onCreate()的代码如下:
super.onCreate(savedInstanceState);
mDbHelper = new NotesDbAdapter(this);
mDbHelper.open();
setContentView(R.layout.note_edit);
mTitleText = (EditText) findViewById(R.id.title);
mBodyText = (EditText) findViewById(R.id.body);
Button confirmButton = (Button) findViewById(R.id.confirm);
mRowId = savedInstanceState != null ? savedInstanceState.getLong(NotesDbAdapter.KEY_ROWID)
: null;
if (mRowId == null) {
Bundle extras = getIntent().getExtras();
mRowId = extras != null ? extras.getLong(NotesDbAdapter.KEY_ROWID)
: null;
}
populateFields();
confirmButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
setResult(RESULT_OK);
finish();
}
});
第六步
定义populateFields()方法
private void populateFields() {
if (mRowId != null) {
Cursor note = mDbHelper.fetchNote(mRowId);
startManagingCursor(note);
mTitleText.setText(note.getString(
note.getColumnIndexOrThrow(NotesDbAdapter.KEY_TITLE)));
mBodyText.setText(note.getString(
note.getColumnIndexOrThrow(NotesDbAdapter.KEY_BODY)));
}
}
这个方法使用NotesDbAdapter.fetchNote()方法来找到编辑的便签,然后调用startManagingCursor(),这个方法是Android共用程序,负责Cursor的生命周期。在Activity生命周期指示下这个方法会释放和重建资源,因此,我们不必担心这些,做自己该做的事情。之后,我们只从游标中查找标题和内容的值,并用来填充View元素。
第七步
处理生命周期事件为什么是重要的?
如果你过去习惯于总是控制你的程序,那么你可能不理解生命周期所有事情为什么必须的。原因是,在Android中,你不能控制你的Activity,而是操作系统控制。
正如我们已经看到的那样,Android模型基于可以互相调用的活动。当一个活动调用另外一个时,目前的活动暂停,如果系统运行在低资源环境下,可能被遗弃杀掉。如果被杀掉,你的活动将不得不保存充足的状态,以便将来恢复,我们希望恢复时的状态和被杀掉的状态相同。
Android有一个定义明确的生命周期。即使你不显式控制另外一个活动,生命周期时间也能发生。例如,也许一个电话到达到话筒,如果电话到达,你的Activity正在运行,那么这个活动会被换出,而由呼叫活动替换。
仍旧在NoteEdit类中,我们重载onSaveInstanceState()、onPause() 和 onResume()方法。这些是我们自己的生命周期方法(连同我们已经有的onCreate())。
如果Activity被停止,onSaveInstanceState()被Android自动调用,在重新开始以前可以被杀掉。这意味着,应该保存任何必须的状态以保证Activity重新启动时重新初始化到同样的环境。这是到onCreate()方法的副本,实际上,传入到onCreate()的savedInstanceState Bundle是和onSaveInstanceState()的outState相同的Bundle.
onPause() 和 onResume()是免费赠送的方法。当Activity结束的时候,onPause()总是被调用,及时我们主动(例如调用finish())。我们会使用来保存当前的标签到数据库。最佳办法是释放在onPause()内部能不释放的任何资源,在被动状态占用更少的资源。基于这个原因,我们将会关闭DBHelper类,设置属性到空,以便能进行垃圾回收。另外一个方面,onResume()会重建mDbHelper实例,因此我们能使用它,然后又读取数据库的便签数据,重新填充这些字段。
因此,在populateFields()方法后增加一些空间,并且增加下面的生命周期方法:
1. onSaveInstanceState():
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putLong(NotesDbAdapter.KEY_ROWID, mRowId);
}
2. onPause():
@Override
protected void onPause() {
super.onPause();
saveState();
}
We'll define saveState() next.
3. onResume():
@Override
protected void onResume() {
super.onResume();
populateFields();
}
第八步
定义saveState()方法,从数据库中取出数据
private void saveState() {
String title = mTitleText.getText().toString();
String body = mBodyText.getText().toString();
if (mRowId == null) {
long id = mDbHelper.createNote(title, body);
if (id > 0) {
mRowId = id;
}
} else {
mDbHelper.updateNote(mRowId, title, body);
}
}
注意:我们从creaeNote()捕获返回值,如果返回了一个有效的行号,我们保存在mRowID属性中,以便我们将来能修改便签,而不是创建一个新的便签(另外,如果生命周期时间被触发,那么创建可能发生)
第九步
现在,在Notepadv3类中从onActivityResult()中剥离出先前的处理代码
在NoteEdit生命周期内发生所有便签查询和修改操作,因此,onActivityResult()方法需要做的事情是修改数据视图,其他工作不需要。最终的代码如下
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
fillData();
}
因为其他类做这个工作,必须做的事情是刷新数据。
第十步
也要从onListItemClick()中移去设置标题和内容的行(他们不再需要,仅仅需要mRowID):
Cursor c = mNotesCursor;
c.moveToPosition(position);
同样要移去:
i.putExtra(NotesDbAdapter.KEY_TITLE, c.getString(
c.getColumnIndex(NotesDbAdapter.KEY_TITLE)));
i.putExtra(NotesDbAdapter.KEY_BODY, c.getString(
c.getColumnIndex(NotesDbAdapter.KEY_BODY)));
因此剩下的代码应该是:
super.onListItemClick(l, v, position, id);
Intent i = new Intent(this, NoteEdit.class);
i.putExtra(NotesDbAdapter.KEY_ROWID, id);
startActivityForResult(i, ACTIVITY_EDIT);
现在,你也能移去mNotesCursor属性,使用fillData()方法中的本地变量:
Cursor notesCursor = mDbHelper.fetchAllNotes();
注意:mNotesCursor中的m表示一个成员变量,因此当我们产生本地变量notesCursor的时候,我们销毁m。
运行程序!(右键菜单中,点击Run As -> Android Application)
没有评论:
发表评论