本练习中,你将会使用调试器来查看你在练习3中做的工作。本练习证明:
1、怎么设置断点来观察执行情况
2.怎么运行你的应用程序在调试模式下。
第一步
使用加工过的Notepadv3,在NoteEdit类的onCreate(), onPause(), onSaveInstanceState() 和 onResume()方法的开头处,设置断点。(如果不熟悉Eclipse,在你想设置断点的行上,对应编辑窗口的左边的窄窄的灰色边框里面右击,选择Toggle Breakpoint,你应该会看到蓝点显示)
第二步
现在,在调试模式下启动notepad示例程序
1、在Notepadv3项目上右击,从调试菜单选择Debug As -> Android Application
2.Android模拟器应该简单地说“waiting for debugger to connect(等待调试器链接)”,然后运行应用程序
3、如果一直停留在“waiting...”屏幕下,退出模拟器和Eclipse,从命令行使用adb销毁服务器,然后重新启动。
第三步
当你编辑或者创建新标签的时候,你应该看到断点命中,执行停止。
第四步
点击Resume按钮让执行继续(在顶部的Eclipse工具栏右边带有绿色三角形的黄色矩形)
第五步
实验确认和回退按钮,试着按Home键,改为其他模式。观察生命周期时间产生了什么,什么时候触发。
ADT插件不仅提供优良的调试支持,而且卓越的剖析能力支持。你能试着使用TraceView来剖析你的应用程序。如果你的应用成运行很慢,这能帮助你发现瓶颈,并纠正问题。
2008年12月30日星期二
教程:Notepad练习3
教程: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)
这个练习中,你将会使用生命周期事件回调来保存和取回应用程序状态数据。这个练习说明:
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)
2008年12月28日星期日
教程:Notepad练习2
Tutorial: Notepad Exercise 2
教程:Notepad练习2
In this exercise, you will add a second Activity to your notepad application, to let the user create, edit, and delete notes. The new Activity assumes responsibility for creating new notes by collecting user input and packing it into a return Bundle provided by the intent. This exercise demonstrates:
在这个练习之中,你会增加第二个活动进入你的notepad程序,让用户创建、编辑、删除便签。新的Activity呈现的职责是创建新的便签来收集用户输入、打包进入一个返回的由intent提供的Bundle,这个练习证明了:
* Constructing a new Activity and adding it to the Android manifest
1、构造新Activity,增加进入Android manifext
* Invoking another Activity asynchronously using startActivityForResult()
2、使用startActivityForResult()异步调用另外一个Activity
* Passing data between Activity in Bundle objects
3、在Activity间使用Bundle对象传递数据
* How to use a more advanced screen layout
4、怎么使用更多的高级界面布局
Step 1
第一步
Create a new Android project using the sources from Notepadv2 under the NotepadCodeLab folder, just like you did for the first exercise. If you see an error about AndroidManifest.xml, or some problems related to an android.zip file, right click on the project and select Android Tools > Fix Project Properties.
创建新Android项目,使用NotepadCodeLab文件夹的Notepadv2源代码,和练习1的做法相同。如果你看到AndroidManifest.xml的错误,或者android.zip的问题,在project上右击,选择Android Tools > Fix Project Properties
Open the Notepadv2 project and take a look around:
打开Notepadv2项目,全部看一看:
* Open and look at the strings.xml file under res/values — there are several new strings which we will use for our new functionality
1、打开strings.xml 并查看——新功能会用到几个新的字符串
* Also, open and take a look at the top of the Notepadv2 class, you will notice several new constants have been defined along with a new mNotesCursor field used to hold the cursor we are using.
2、打开 Notepadv2类,看看顶部的代码,你会注意到定义了几个新常量,和新的mNotesCursor属性一起用于存储我们正在使用的游标。
* Note also that the fillData() method has a few more comments and now uses the new field to store the notes Cursor. The onCreate() method is unchanged from the first exercise. Also notice that the member field used to store the notes Cursor is now called mNotesCursor. The m denotes a member field and is part of the Android coding style standards.
3、fillData()方法有更多的注释,使用新的字段来保存便签游标。onCreate()方法没有改变。用于保存便签游标的成员变量现在称为mNotesCursor。m指示一个成员属性,是Android代码风格标准的一部分。
* There are also a couple of new overridden methods (onListItemClick() and onActivityResult()) which we will be filling in below.
4、还有一对新的重载方法:onListItemClick() 和onActivityResult(),下面我们会填充它们。
Step 2
第二步
Add an entry to the menu for deleting a note:
增加删除便签的菜单入口:
1. In the onCreateOptionsMenu() method, add a new line:
1、在方法onCreateOptionsMenu()中增加新行:
menu.add(0, DELETE_ID, 0, R.string.menu_delete);
2. The whole method should now look like this:
2、整个方法的代码如下:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
menu.add(0, INSERT_ID, 0, R.string.menu_insert);
menu.add(0, DELETE_ID, 0, R.string.menu_delete);
return true;
}
Step 3
第三步
In the onMenuItemSelected() method, add a new case for DELETE_ID:
onMenuItemSelected()增加新代码,关联DELETE_ID:
mDbHelper.deleteNote(getListView().getSelectedItemId());
fillData();
return true;
1. Here, we use the deleteNote method to remove the note specified by ID. In order to get the ID, we call getListView().getSelectedItemId().
1、使用deleteNote方法,通过ID移除便签。为了获取ID,我们调用getListView().getSelectedItemId()
2. Then we fill the data to keep everything up to date.
2、我们填充数据以保证每件事情最新
The whole method should now look like this:
整个方法的代码:
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
switch(item.getItemId()) {
case INSERT_ID:
createNote();
return true;
case DELETE_ID:
mDbHelper.deleteNote(getListView().getSelectedItemId());
fillData();
return true;
}
return super.onMenuItemSelected(featureId, item);
}
Step 4
第四步
Starting Other Activities
启动其他的Activity
In this example our Intent uses a class name specifically. As well as starting intents in classes we already know about, be they in our own application or another application, we can also create Intents without knowing exactly which application will handle it.
这个例子中,Intent使用一个特定的类名。又在类中启动intent,正如我们知道的那样,是他们在我们自己的程序或者其他程序中,我们也能创建Intent而不知道哪个程序处理它。
For example, we might want to open a page in a browser, and for this we still use an Intent. But instead of specifying a class to handle it, we use a predefined Intent constant, and a content URI that describes what we want to do. See android.content.Intent for more information.
例如,我们可能想在浏览器中打开一个页面,为了这个目的,我们人就使用Intent。但不是指定一个类来处理它,我们使用一个预定义的Intent常量,一个内容URI(描述我们想做什么)。欲知更多信息,请看“android.content.Intent ”。
Fill in the body of the createNote() method:
填写createNode()方法的代码:
Create a new Intent to create a note (ACTIVITY_CREATE) using the NoteEdit class. Then fire the Intent using the startActivityForResult() method call:
创建新的Intent来创建一个便签(ACTIVITY_CREATE),使用NoteEdit类。然后,调用startActivityForResult(),触发Intent:
Intent i = new Intent(this, NoteEdit.class);
startActivityForResult(i, ACTIVITY_CREATE);
This form of the Intent call targets a specific class in our Activity, in this case NoteEdit. Since the Intent class will need to communicate with the Android operating system to route requests, we also have to provide a Context (this).
这种Intent调用的形式瞄准Activity中的特定类,这个例子中指NoteEdit。因为Intent类会需要与Android操作系统通讯,目的是路由请求消息,我们也不得不提供Context(this)。
The startActivityForResult() method fires the Intent in a way that causes a method in our Activity to be called when the new Activity is completed. The method in our Activity that receives the callback is called onActivityResult() and we will implement it in a later step. The other way to call an Activity is using startActivity() but this is a "fire-and-forget" way of calling it — in this manner, our Activity is not informed when the Activity is completed, and there is no way to return result information from the called Activity with startActivity().
startActivityForResult()方法触发Intent,这种方法促使Activity中的方法在新Activity完成时被调用。Activity中接受回调的方法是onActivityResult(),在稍后步骤中会实现它。调用Activity的其他方式是使用startActivity(),但是这种方法是"fire-and-forget"(发射后忘记)——使用这种方法,Activity完成时Activity消息并不灵通,startActivity()调用的Activity没有办法返回结果信息。
Don't worry about the fact that NoteEdit doesn't exist yet, we will fix that soon.
不要担心NoteEdit,我们会校正这个问题。
Step 5
第五步
Fill in the body of the onListItemClick() override.
填写onListItemClick()重载代码。
onListItemClick() is a callback method that we'll override. It is called when the user selects an item from the list. It is passed four parameters: the ListView object it was invoked from, the View inside the ListView that was clicked on, the position in the list that was clicked, and the mRowId of the item that was clicked. In this instance we can ignore the first two parameters (we only have one ListView it could be), and we ignore the mRowId as well. All we are interested in is the position that the user selected. We use this to get the data from the correct row, and bundle it up to send to the NoteEdit Activity.
onListItemClick()是一个回调函数,我们将会重载它。当用户从列表中选择一项时该函数自动被系统调用。四个参数:ListView对象、View(点击的ListView里面)、点击的列表位置、nRowId(点击的条目的记录ID)。这个例子中,我们忽略前面两个参数,也忽略mRowId。我们感兴趣的是用户选择的位置。我们使用这个方法获得正确行的数据,捆扎在一起送到NoteEdit活动。
In our implementation of the callback, the method creates an Intent to edit the note using the NoteEdit class. It then adds data into the extras Bundle of the Intent, which will be passed to the called Activity. We use it to pass in the title and body text, and the mRowId for the note we are editing. Finally, it will fire the Intent using the startActivityForResult() method call. Here's the code that belongs in onListItemClick():
在回调函数的实现中,创建Intent,以便使用NoteEdit类编辑便签。然后,增加数据进入Intent的附加Bundle中。新建的Intent对象传送给被调用的活动。我们使用它传入标题和内容文本,以及我们编辑的便签的mRowId。最后,使用startActivityForResult()方法发射Intent。下面的代码是onListItemClick()的实现:
super.onListItemClick(l, v, position, id);
Cursor c = mNotesCursor;
c.moveToPosition(position);
Intent i = new Intent(this, NoteEdit.class);
i.putExtra(NotesDbAdapter.KEY_ROWID, id);
i.putExtra(NotesDbAdapter.KEY_TITLE, c.getString(
c.getColumnIndexOrThrow(NotesDbAdapter.KEY_TITLE)));
i.putExtra(NotesDbAdapter.KEY_BODY, c.getString(
c.getColumnIndexOrThrow(NotesDbAdapter.KEY_BODY)));
startActivityForResult(i, ACTIVITY_EDIT);
* putExtra() is the method to add items into the extras Bundle to pass in to intent invocations. Here, we are using the Bundle to pass in the title, body and mRowId of the note we want to edit.
1、putExtra()方法增加条目到附加的Bundle中,传入参数intent的引用。此处,我们使用Bundle来传入标题、内容、我们想编辑的便签的记录ID
* The details of the note are pulled out from our query Cursor, which we move to the proper position for the element that was selected in the list, with the moveToPosition() method.
2、从查询游标中读出便签的详细信息,游标的位置移动到列表中选中的元素的位置,使用moveToPosition()方法移动。
* With the extras added to the Intent, we invoke the Intent on the NoteEdit class by passing startActivityForResult() the Intent and the request code. (The request code will be returned to onActivityResult as the requestCode parameter.)
3、附加信息加入Intent中,我们在NoteEdit类型中调用Intent,吧Intent和请求代码传递给startActivityForResult()。(请求代码作为requestCode参数在onActivityResult中返回。)
Note: We assign the mNotesCursor field to a local variable at the start of the method. This is done as an optimization of the Android code. Accessing a local variable is much more efficient than accessing a field in the Dalvik VM, so by doing this we make only one access to the field, and five accesses to the local variable, making the routine much more efficient. It is recommended that you use this optimization when possible.
注意:我们把mNotesCursor属性赋值到本地变量,这是对Android代码的优化。访问本地变量比访问DalvikVM的一个属性更有效率,因此,这样做了后我们访问了这个属性一次,访问本地变量5次,使程序有效得多。推荐尽可能的使用这种优化方法。
Step 6
第六步
The above createNote() and onListItemClick() methods use an asynchronous Intent invocation. We need a handler for the callback, so here we fill in the body of the onActivityResult().
上面提到的createNote()和onListItemClick()方法使用了异步Intent引用。我们需要处理回调行数,因此,此处我们填充onActivityResult()代码。
onActivityResult() is the overridden method which will be called when an Activity returns with a result. (Remember, an Activity will only return a result if launched with startActivityForResult.) The parameters provided to the callback are:
onActivityResult()是一个重载方法,当Activity返回结果的时候被调用。(记住,如果使用startActivityForResult触发,则Activity仅仅会返回一个结果。) 回调函数提供的参数如下:
* requestCode — the original request code specified in the Intent invocation (either ACTIVITY_CREATE or ACTIVITY_EDIT for us).
1、requestCode——在Intent引用中指定的原始请求代码(要么是ACTIVITY_CREATE,要么是ACTIVITY_EDIT)
* resultCode — the result (or error code) of the call, this should be zero if everything was OK, but may have a non-zero code indicating that something failed. There are standard result codes available, and you can also create your own constants to indicate specific problems.
2、resultCode——调用的结果或者错误代码,如果一切都没有问题,应该是0,但是可以是非0值,则表示有错误发生。有标准的结果代码获得,你也可以创建你自己的常量来指示特定错误。
* intent — this is an Intent created by the Activity returning results. It can be used to return data in the Intent "extras."
3、intent——通过有返回结果的Activty创建的Intent。他能用于返回数据,数据存放在Intent的“extras”中。
The combination of startActivityForResult() and onActivityResult() can be thought of as an asynchronous RPC (remote procedure call) and forms the recommended way for an Activity to invoke another and share services.
startActivityForResult()和onActivityResult()的结合能作为异步RPC,提供活动相互调用和共享服务的推荐方法。
Here's the code that belongs in your onActivityResult():
下面是onActivityResult()的代码:
super.onActivityResult(requestCode, resultCode, intent);
Bundle extras = intent.getExtras();
switch(requestCode) {
case ACTIVITY_CREATE:
String title = extras.getString(NotesDbAdapter.KEY_TITLE);
String body = extras.getString(NotesDbAdapter.KEY_BODY);
mDbHelper.createNote(title, body);
fillData();
break;
case ACTIVITY_EDIT:
Long mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
if (mRowId != null) {
String editTitle = extras.getString(NotesDbAdapter.KEY_TITLE);
String editBody = extras.getString(NotesDbAdapter.KEY_BODY);
mDbHelper.updateNote(mRowId, editTitle, editBody);
}
fillData();
break;
}
* We are handling both the ACTIVITY_CREATE and ACTIVITY_EDIT activity results in this method.
1、在这个方法中,我们处理 ACTIVITY_CREATE 和 ACTIVITY_EDIT两种活动返回结果。
* In the case of a create, we pull the title and body from the extras (retrieved from the returned Intent) and use them to create a new note.
2、在创建的代码中,我们从extras获取标题和内容,然后用来创建新的便签。
* In the case of an edit, we pull the mRowId as well, and use that to update the note in the database.
3、在编辑的代码中,我们还是获取mRowId,然后用来修改数据库的便签数据。
* fillData() at the end ensures everything is up to date .
4、fillData()保证所有信息都是最新的。
Step 7
第七步
The Art of Layout
布局的艺术
The provided note_edit.xml layout file is the most sophisticated one in the application we will be building, but that doesn't mean it is even close to the kind of sophistication you will be likely to want in real Android applications.
note_edit.xml布局文件是我们建立的应用程序中最成熟的一个文件,但是那不并意味着接近你在真实Android程序中可能要求的那种成熟。
Creating a good UI is part art and part science, and the rest is work. Mastering Android layout is an essential part of creating a good looking Android application.
创建好的UI部分(参见“Android layout”)是艺术部分是科学,剩余的就是工作。掌握Android布局是创建好看的Android程序的基本部分。
Take a look at the View Gallery for some example layouts and how to use them. The ApiDemos sample project is also a great resource from which to learn how to create different layouts.
仔细看看一些例子的布局,研究怎么使用他们,参见“View Gallery”。ApiDemos例子项目也是好资源,可以学习了解怎么创建不懂的布局。
Open the file note_edit.xml that has been provided and take a look at it. This is the UI code for the Note Editor.
打开已经提供的note_edit.xml文件,仔细看看。这是Note Editor的UI代码。
This is the most sophisticated UI we have dealt with yet. The file is given to you to avoid problems that may sneak in when typing the code. (The XML is very strict about case sensitivity and structure, mistakes in these are the usual cause of problems with layout.)
这是我们已经处理的最成熟的UI。提供这个文件为了避免问题,这些问题可能由于键入代码而悄悄潜入。(XML是非常严格的,在大小写和结构等方面的错误是布局问题的最常见原因。)
There is a new parameter used here that we haven't seen before: android:layout_weight (in this case set to use the value 1 in each case).
这里有一个以前没有见过的新参数:android:layout_weight(本例中在所有情况下设置为值1)
layout_weight is used in LinearLayouts to assign "importance" to Views within the layout. All Views have a default layout_weight of zero, meaning they take up only as much room on the screen as they need to be displayed. Assigning a value higher than zero will split up the rest of the available space in the parent View, according to the value of each View's layout_weight and its ratio to the overall layout_weight specified in the current layout for this and other View elements.
layout_weight用在LinearLayouts中,在布局中赋值"importance"给View。所有View默认layout_weight值为0,意味着他们仅仅占据他们需要的显示空间。赋值大于0将会在父View中分割可获得的剩余空间,分割空间的依据是每个View的layout_weight的值,还有在View元素的现有布局中指定的全部layout_weight的比率。
To give an example: let's say we have a text label and two text edit elements in a horizontal row. The label has no layout_weight specified, so it takes up the minimum space required to render. If the layout_weight of each of the two text edit elements is set to 1, the remaining width in the parent layout will be split equally between them (because we claim they are equally important). If the first one has a layout_weight of 1 and the second has a layout_weight of 2, then one third of the remaining space will be given to the first, and two thirds to the second (because we claim the second one is more important).
举一个例子:假设我们在一行中有一个文本标签和两个文本编辑框。标签没有指定layout_weight,因此他占据必须的最小空间。如果两个文本编辑框的layout_weight都设置为1,可在父窗口中的剩余宽度被他们平分(因为我们声称他们同等重要)。如果第一个是1,第二个是2,那么剩余空间的三分之一分配给第一个,三分之二分配给第二个(因为我们要求第二个更重要)
This layout also demonstrates how to nest multiple layouts inside each other to achieve a more complex and pleasant layout. In this example, a horizontal linear layout is nested inside the vertical one to allow the title label and text field to be alongside each other, horizontally.
这个布局文件也证明怎么互相嵌套多个布局元素,以便获得更多复杂和合适的布局界面。本例中,水平线布局嵌套进入垂直线,以便允许标题标签和文本字段相互并排。
Step 8
第八步
Create a NoteEdit class that extends android.app.Activity.
创建NoteEdit类,继承android.app.Activity
This is the first time we will have created an Activity without the Android Eclipse plugin doing it for us. When you do so, the onCreate() method is not automatically overridden for you. It is hard to imagine an Activity that doesn't override the onCreate() method, so this should be the first thing you do.
我们第一次没有在ADT帮助下创建Activity。onCreate()方法没有自动重载。想像没有重载onCreate()方法的Activity是困难的,因此,首先要做的事情是重载onCreate()
1. Right click on the com.android.demo.notepad2 package in the Package Explorer, and select New > Class from the popup menu.
1、右击com.android.demo.notepad2包,从弹出菜单中选择 New > Class
2. Fill in NoteEdit for the Name: field in the dialog.
2、在对话框的“Name:”标签后填入“NoteEdit”
3. In the Superclass: field, enter android.app.Activity (you can also just type Activity and hit Ctrl-Space on Windows and Linux or Cmd-Space on the Mac, to invoke code assist and find the right package and class).
3、在“Superclass: ”标签中,键入“android.app.Activity”(你也能输入Activity,按 Ctrl-Space,启用代码助手找到正确的包和类)
4. Click Finish.
4、点击Finish
5. In the resulting NoteEdit class, right click in the editor window and select Source > Override/Implement Methods...
5、在NoteEdit类中,右击编辑窗口,选择菜单Source > Override/ImplementMethods...
6. Scroll down through the checklist in the dialog until you see onCreate(Bundle) — and check the box next to it.
6、滚动对话框中的清单,找到onCreate(Bundle)——选中前面的检查框
7. Click OK.
7、点击OK
The method should now appear in your class.
这个方法应该出现在你的类中。
Step 9
第九步
Fill in the body of the onCreate() method for NoteEdit.
填写onCreate()的代码
This will set the title of our new Activity to say "Edit Note" (one of the strings defined in strings.xml). It will also set the content view to use our note_edit.xml layout file. We can then grab handles to the title and body text edit views, and the confirm button, so that our class can use them to set and get the note title and body, and attach an event to the confirm button for when it is pressed by the user.
这里将会设置新活动的题目为“Edit Note”(在strings.xml文件中定义的一个字符串)。也会设置内容视图以便使用note_edit.xml布局文件。然后,处理标题和内容文本编辑框、确认按钮,因此,我们的类能使用他们来获取或者设置便签的标题和内容,附着事件到确认按钮,这个事件在用户按确认按钮时触发。
We can then unbundle the values that were passed in to the Activity with the extras Bundle attached to the calling Intent. We'll use them to pre-populate the title and body text edit views so that the user can edit them. Then we will grab and store the mRowId so we can keep track of what note the user is editing.
然后,我们对传入到Activity的值进行分类,传入值使用Activity的extras Bundle,extras Bundle附着到调用的Intent上。我们会使用他们来预先填充标题和内容文本编辑框,因此,用户能编辑他们。然后,我们会获取和存储mRowId,因此我们能留意用户编辑的什么便签。
1. Inside onCreate(), set up the layout:
1、在onCreate()内部,设置布局:
setContentView(R.layout.note_edit);
2. Find the edit and button components we need:
2、找到我们需要的编辑框和按钮组件:
These are found by the IDs associated to them in the R class, and need to be cast to the right type of View (EditText for the two text views, and Button for the confirm button):
在R类中,通过ID找到他们,需要强制转换到正确的视图类型(两个文本视图是EditText,确认按钮时Button)
mTitleText = (EditText) findViewById(R.id.title);
mBodyText = (EditText) findViewById(R.id.body);
Button confirmButton = (Button) findViewById(R.id.confirm);
Note that mTitleText and mBodyText are member fields (you need to declare them at the top of the class definition).
注意:mTitleText 和 mBodyText是成员变量(你需要在顶部声明他们)
3. At the top of the class, declare a Long mRowId private field to store the current mRowId being edited (if any).
3、在类的顶部,生命一个Long类型的mRowId私有变量,用来存储当前编辑的mRowId
4. Continuing inside onCreate(), add code to initialize the title, body and mRowId from the extras Bundle in the Intent (if it is present):
4、继续在onCreate()中增加代码来初始化标题、内容、mRowId,从Intent的extras Bundle(附加包)中获取初始化数据。
mRowId = null;
Bundle extras = getIntent().getExtras();
if (extras != null) {
String title = extras.getString(NotesDbAdapter.KEY_TITLE);
String body = extras.getString(NotesDbAdapter.KEY_BODY);
mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
if (title != null) {
mTitleText.setText(title);
}
if (body != null) {
mBodyText.setText(body);
}
}
* We are pulling the title and body out of the extras Bundle that was set from the Intent invocation.
(1)从来自Intent引用的extras Bundle中获取标题和内容
* We also null-protect the text field setting (i.e., we don't want to set the text fields to null accidentally).
(2)零保护文本框设置(例如,我们不想意外地设置文本框为null)
5. Create an onClickListener() for the button:
5、针对按钮创建onClickListener()
Listeners can be one of the more confusing aspects of UI implementation, but what we are trying to achieve in this case is simple. We want an onClick() method to be called when the user presses the confirm button, and use that to do some work and return the values of the edited note to the Intent caller. We do this using something called an anonymous inner class. This is a bit confusing to look at unless you have seen them before, but all you really need to take away from this is that you can refer to this code in the future to see how to create a listener and attach it to a button. (Listeners are a common idiom in Java development, particularly for user interfaces.) Here's the empty listener:
Listener使UI实现更混乱的一个方面,但是,本例中试图完成的事情是简单的。当用户按确认按钮时,调用onClieck()方法,用来完成一些工作并且然会被编辑的便签的值给Intent调用者。我们利用称为匿名内嵌类的东西来做这些事情。如果你以前没有见到过,那么看起来这有点混乱,但是,你实际需要取走(完成)的所有事情是可以在未来引用这里的代码,注意怎样创建一个Listener,并附着到一个按钮(Listener是Java开发的一个习惯用语,尤其是在UI方面)。下面是空的Listener:
confirmButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
}
});
Step 10
第十步
Fill in the body of the onClick() method in our listener.
填充侦听器里面的onClick()代码。
This is the code that will be run when the user clicks on the confirm button. We want this to grab the title and body text from the edit text fields, and put them into the return Bundle so that they can be passed back to the Activity that invoked this NoteEdit Activity. If the operation is an edit rather than a create, we also want to put the mRowId into the Bundle so that the Notepadv2 class can save the changes back to the correct note.
这些代码在用户点击确认按钮时运行。我们使用这些代码从编辑框中获取标题和内容文本串,然后把它们送给Bundle,以便他们能传回到Activity(NoteEdit Activity调用)。如果操作是编辑而不是创建,那么传送mRowId进入Bundle,以便Notepav2类能保存变化到正确的便签。
1. Create a Bundle and put the title and body text into it using the constants defined in Notepadv2 as keys:
1、创建Bundle,传递标题和内容文本到Bundle,使用在Notepadv2中定义的常量作为关键码:
Bundle bundle = new Bundle();
bundle.putString(NotesDbAdapter.KEY_TITLE, mTitleText.getText().toString());
bundle.putString(NotesDbAdapter.KEY_BODY, mBodyText.getText().toString());
if (mRowId != null) {
bundle.putLong(NotesDbAdapter.KEY_ROWID, mRowId);
}
2. Set the result information (the Bundle) in a new Intent and finish the Activity:
2、在新的Intent中设置结果信息(Bundle),完成Activity:
Intent mIntent = new Intent();
mIntent.putExtras(bundle);
setResult(RESULT_OK, mIntent);
finish();
* The Intent is simply our data carrier that carries our Bundle (with the title, body and mRowId).
(1)Intent是简单的数据运载工具,运载Bundle(携带标题、内容、mRowId)
* The setResult() method is used to set the result code and return Intent to be passed back to the Intent caller. In this case everything worked, so we return RESULT_OK for the result code.
(2)setResult()方法用于设置结果代码,返回Intent为了传回到Intent调用者。本例中,每件事情都正常工作,因此我们返回RESULT_OK作为结果代码
* The finish() call is used to signal that the Activity is done (like a return call). Anything set in the Result will then be returned to the caller, along with execution control.
(3)finish()调用用于发信号表明Activity被激发(像一个回调)。然后,在Result中设置的所有东西会返回给调用者,连同执行控制一起
The full onCreate() method (plus supporting class fields) should now look like this:
完整的onCreate()如下:
private EditText mTitleText;
private EditText mBodyText;
private Long mRowId;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
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 = null;
Bundle extras = getIntent().getExtras();
if (extras != null) {
String title = extras.getString(NotesDbAdapter.KEY_TITLE);
String body = extras.getString(NotesDbAdapter.KEY_BODY);
mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
if (title != null) {
mTitleText.setText(title);
}
if (body != null) {
mBodyText.setText(body);
}
}
confirmButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Bundle bundle = new Bundle();
bundle.putString(NotesDbAdapter.KEY_TITLE, mTitleText.getText().toString());
bundle.putString(NotesDbAdapter.KEY_BODY, mBodyText.getText().toString());
if (mRowId != null) {
bundle.putLong(NotesDbAdapter.KEY_ROWID, mRowId);
}
Intent mIntent = new Intent();
mIntent.putExtras(bundle);
setResult(RESULT_OK, mIntent);
finish();
}
});
}
Step 11
第11步
The All-Important Android Manifest File
非常重要的Android Manifest文件
The AndroidManifest.xml file is the way in which Android sees your application. This file defines the category of the application, where it shows up (or even if it shows up) in the launcher or settings, what activities, services, and content providers it defines, what intents it can receive, and more.
AndroidManifest.xml文件是Android领会你的程序的方式。这个文件定义了程序的类型,在发射器和设置里面露面的地方,定义了活动、服务、内容提供器是什么,能接收的intent是什么,等等。
For more information, see the reference document AndroidManifest.xml
欲知更多信息参见文档“AndroidManifest.xml”
Finally, the new Activity has to be defined in the manifest file:
最后,新Activity必须定义在manifest文件中:
Before the new Activity can be seen by Android, it needs its own Activity entry in the AndroidManifest.xml file. This is to let the system know that it is there and can be called. We could also specify which IntentFilters the activity implements here, but we are going to skip this for now and just let Android know that the Activity is defined.
在新Activity能被Android了解之前,需要在AndroidManifest.xml中定义自己的Activity入口。这是让系统知道它在那儿且能被调用。我们也能指定Activity实现哪个IntentFilters,但是我们打算暂时跳过这个,只是让Android知道Activity被定义了。
There is a Manifest editor included in the Eclipse plugin that makes it much easier to edit the AndroidManifest file, and we will use this. If you prefer to edit the file directly or are not using the Eclipse plugin, see the box at the end for information on how to do this without using the new Manifest editor.
在Eclipse插件中包含了一个Manifest编辑器,使AndroidManifest文件编辑更加容易,我们将会使用这个编辑器。
1. Double click on the AndroidManifest.xml file in the package explorer to open it.
1、在AndroidManifest.xml文件上双击打开它
2. Click the Application tab at the bottom of the Manifest editor.
2、在Manifest编辑器底部点击Application便签
3. Click Add... in the Application Nodes section.
3、在Application Nodes段中点击 Add...
If you see a dialog with radiobuttons at the top, select the top radio button: "Create a new element at the top level, in Application".
如果你在顶部看到一个带有单选按钮的对话框,那么选择顶部的单选按钮"Create a new element at the top level, in Application"
4. Make sure "(A) Activity" is selected in the selection pane of the dialog, and click OK.
4、保证 "(A) Activity"在对话框的选择面板中被选中,然后点击OK
5. Click on the new "Activity" node, in the Application Nodes section, then type .NoteEdit into the Name* field to the right. Press Return/Enter.
5、在 Application Nodes段中,点击新的"Activity"节点,然后在“Name*”标签右边输入.NoteEdit,按Return/Enter
The Android Manifest editor helps you add more complex entries into the AndroidManifest.xml file, have a look around at some of the other options available (but be careful not to select them otherwise they will be added to your Manifest). This editor should help you understand and alter the AndroidManifest.xml file as you move on to more advanced Android applications.
Android Manifest编辑器帮助你增加更多复杂的条目到AndroidManifest.xml文件中,仔细看看可获得的其他选项(但是小心不要选择他们否则他们会被加入到你的Manifest文件中)。这个编辑器应该帮助你理解和修改AndroidManifest.xml文件,当你移到更多高级的Android程序时会很有用处。
If you prefer to edit this file directly, simply open the AndroidManifest.xml file and look at the source (use the AndroidManifest.xml tab in the eclipse editor to see the source code directly). Then edit the file as follows:
如果你宁愿直接编辑这个文件,简单打开 AndroidManifest.xml文件,然后了解资源(使用eclipse编辑器的AndroidManifest.xml标签直接领悟源代码)。然后,按照下面这样编辑文件:
This should be placed just below the line that reads:
这应该表示:
for the .Notepadv2 activity.
针对.Notepadv2活动
Step 12
第12步
Now Run it!
现在运行。
You should now be able to add real notes from the menu, as well as delete an existing one. Notice that in order to delete, you must first use the directional controls on the device to highlight the note. Furthermore, selecting a note title from the list should bring up the note editor to let you edit it. Press confirm when finished to save the changes back to the database.
现在,你应该能从菜单增加真实的便签,也能删除存在的便签。注意:为了删除,你必须首先使用设备方向控制来选中便签。此外,从列表中选择便签标题应该被提出到便签编辑器,以便你能编辑他。按确认按钮保证保存修改到数据库中。
教程:Notepad练习2
In this exercise, you will add a second Activity to your notepad application, to let the user create, edit, and delete notes. The new Activity assumes responsibility for creating new notes by collecting user input and packing it into a return Bundle provided by the intent. This exercise demonstrates:
在这个练习之中,你会增加第二个活动进入你的notepad程序,让用户创建、编辑、删除便签。新的Activity呈现的职责是创建新的便签来收集用户输入、打包进入一个返回的由intent提供的Bundle,这个练习证明了:
* Constructing a new Activity and adding it to the Android manifest
1、构造新Activity,增加进入Android manifext
* Invoking another Activity asynchronously using startActivityForResult()
2、使用startActivityForResult()异步调用另外一个Activity
* Passing data between Activity in Bundle objects
3、在Activity间使用Bundle对象传递数据
* How to use a more advanced screen layout
4、怎么使用更多的高级界面布局
Step 1
第一步
Create a new Android project using the sources from Notepadv2 under the NotepadCodeLab folder, just like you did for the first exercise. If you see an error about AndroidManifest.xml, or some problems related to an android.zip file, right click on the project and select Android Tools > Fix Project Properties.
创建新Android项目,使用NotepadCodeLab文件夹的Notepadv2源代码,和练习1的做法相同。如果你看到AndroidManifest.xml的错误,或者android.zip的问题,在project上右击,选择Android Tools > Fix Project Properties
Open the Notepadv2 project and take a look around:
打开Notepadv2项目,全部看一看:
* Open and look at the strings.xml file under res/values — there are several new strings which we will use for our new functionality
1、打开strings.xml 并查看——新功能会用到几个新的字符串
* Also, open and take a look at the top of the Notepadv2 class, you will notice several new constants have been defined along with a new mNotesCursor field used to hold the cursor we are using.
2、打开 Notepadv2类,看看顶部的代码,你会注意到定义了几个新常量,和新的mNotesCursor属性一起用于存储我们正在使用的游标。
* Note also that the fillData() method has a few more comments and now uses the new field to store the notes Cursor. The onCreate() method is unchanged from the first exercise. Also notice that the member field used to store the notes Cursor is now called mNotesCursor. The m denotes a member field and is part of the Android coding style standards.
3、fillData()方法有更多的注释,使用新的字段来保存便签游标。onCreate()方法没有改变。用于保存便签游标的成员变量现在称为mNotesCursor。m指示一个成员属性,是Android代码风格标准的一部分。
* There are also a couple of new overridden methods (onListItemClick() and onActivityResult()) which we will be filling in below.
4、还有一对新的重载方法:onListItemClick() 和onActivityResult(),下面我们会填充它们。
Step 2
第二步
Add an entry to the menu for deleting a note:
增加删除便签的菜单入口:
1. In the onCreateOptionsMenu() method, add a new line:
1、在方法onCreateOptionsMenu()中增加新行:
menu.add(0, DELETE_ID, 0, R.string.menu_delete);
2. The whole method should now look like this:
2、整个方法的代码如下:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
menu.add(0, INSERT_ID, 0, R.string.menu_insert);
menu.add(0, DELETE_ID, 0, R.string.menu_delete);
return true;
}
Step 3
第三步
In the onMenuItemSelected() method, add a new case for DELETE_ID:
onMenuItemSelected()增加新代码,关联DELETE_ID:
mDbHelper.deleteNote(getListView().getSelectedItemId());
fillData();
return true;
1. Here, we use the deleteNote method to remove the note specified by ID. In order to get the ID, we call getListView().getSelectedItemId().
1、使用deleteNote方法,通过ID移除便签。为了获取ID,我们调用getListView().getSelectedItemId()
2. Then we fill the data to keep everything up to date.
2、我们填充数据以保证每件事情最新
The whole method should now look like this:
整个方法的代码:
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
switch(item.getItemId()) {
case INSERT_ID:
createNote();
return true;
case DELETE_ID:
mDbHelper.deleteNote(getListView().getSelectedItemId());
fillData();
return true;
}
return super.onMenuItemSelected(featureId, item);
}
Step 4
第四步
Starting Other Activities
启动其他的Activity
In this example our Intent uses a class name specifically. As well as starting intents in classes we already know about, be they in our own application or another application, we can also create Intents without knowing exactly which application will handle it.
这个例子中,Intent使用一个特定的类名。又在类中启动intent,正如我们知道的那样,是他们在我们自己的程序或者其他程序中,我们也能创建Intent而不知道哪个程序处理它。
For example, we might want to open a page in a browser, and for this we still use an Intent. But instead of specifying a class to handle it, we use a predefined Intent constant, and a content URI that describes what we want to do. See android.content.Intent for more information.
例如,我们可能想在浏览器中打开一个页面,为了这个目的,我们人就使用Intent。但不是指定一个类来处理它,我们使用一个预定义的Intent常量,一个内容URI(描述我们想做什么)。欲知更多信息,请看“android.content.Intent ”。
Fill in the body of the createNote() method:
填写createNode()方法的代码:
Create a new Intent to create a note (ACTIVITY_CREATE) using the NoteEdit class. Then fire the Intent using the startActivityForResult() method call:
创建新的Intent来创建一个便签(ACTIVITY_CREATE),使用NoteEdit类。然后,调用startActivityForResult(),触发Intent:
Intent i = new Intent(this, NoteEdit.class);
startActivityForResult(i, ACTIVITY_CREATE);
This form of the Intent call targets a specific class in our Activity, in this case NoteEdit. Since the Intent class will need to communicate with the Android operating system to route requests, we also have to provide a Context (this).
这种Intent调用的形式瞄准Activity中的特定类,这个例子中指NoteEdit。因为Intent类会需要与Android操作系统通讯,目的是路由请求消息,我们也不得不提供Context(this)。
The startActivityForResult() method fires the Intent in a way that causes a method in our Activity to be called when the new Activity is completed. The method in our Activity that receives the callback is called onActivityResult() and we will implement it in a later step. The other way to call an Activity is using startActivity() but this is a "fire-and-forget" way of calling it — in this manner, our Activity is not informed when the Activity is completed, and there is no way to return result information from the called Activity with startActivity().
startActivityForResult()方法触发Intent,这种方法促使Activity中的方法在新Activity完成时被调用。Activity中接受回调的方法是onActivityResult(),在稍后步骤中会实现它。调用Activity的其他方式是使用startActivity(),但是这种方法是"fire-and-forget"(发射后忘记)——使用这种方法,Activity完成时Activity消息并不灵通,startActivity()调用的Activity没有办法返回结果信息。
Don't worry about the fact that NoteEdit doesn't exist yet, we will fix that soon.
不要担心NoteEdit,我们会校正这个问题。
Step 5
第五步
Fill in the body of the onListItemClick() override.
填写onListItemClick()重载代码。
onListItemClick() is a callback method that we'll override. It is called when the user selects an item from the list. It is passed four parameters: the ListView object it was invoked from, the View inside the ListView that was clicked on, the position in the list that was clicked, and the mRowId of the item that was clicked. In this instance we can ignore the first two parameters (we only have one ListView it could be), and we ignore the mRowId as well. All we are interested in is the position that the user selected. We use this to get the data from the correct row, and bundle it up to send to the NoteEdit Activity.
onListItemClick()是一个回调函数,我们将会重载它。当用户从列表中选择一项时该函数自动被系统调用。四个参数:ListView对象、View(点击的ListView里面)、点击的列表位置、nRowId(点击的条目的记录ID)。这个例子中,我们忽略前面两个参数,也忽略mRowId。我们感兴趣的是用户选择的位置。我们使用这个方法获得正确行的数据,捆扎在一起送到NoteEdit活动。
In our implementation of the callback, the method creates an Intent to edit the note using the NoteEdit class. It then adds data into the extras Bundle of the Intent, which will be passed to the called Activity. We use it to pass in the title and body text, and the mRowId for the note we are editing. Finally, it will fire the Intent using the startActivityForResult() method call. Here's the code that belongs in onListItemClick():
在回调函数的实现中,创建Intent,以便使用NoteEdit类编辑便签。然后,增加数据进入Intent的附加Bundle中。新建的Intent对象传送给被调用的活动。我们使用它传入标题和内容文本,以及我们编辑的便签的mRowId。最后,使用startActivityForResult()方法发射Intent。下面的代码是onListItemClick()的实现:
super.onListItemClick(l, v, position, id);
Cursor c = mNotesCursor;
c.moveToPosition(position);
Intent i = new Intent(this, NoteEdit.class);
i.putExtra(NotesDbAdapter.KEY_ROWID, id);
i.putExtra(NotesDbAdapter.KEY_TITLE, c.getString(
c.getColumnIndexOrThrow(NotesDbAdapter.KEY_TITLE)));
i.putExtra(NotesDbAdapter.KEY_BODY, c.getString(
c.getColumnIndexOrThrow(NotesDbAdapter.KEY_BODY)));
startActivityForResult(i, ACTIVITY_EDIT);
* putExtra() is the method to add items into the extras Bundle to pass in to intent invocations. Here, we are using the Bundle to pass in the title, body and mRowId of the note we want to edit.
1、putExtra()方法增加条目到附加的Bundle中,传入参数intent的引用。此处,我们使用Bundle来传入标题、内容、我们想编辑的便签的记录ID
* The details of the note are pulled out from our query Cursor, which we move to the proper position for the element that was selected in the list, with the moveToPosition() method.
2、从查询游标中读出便签的详细信息,游标的位置移动到列表中选中的元素的位置,使用moveToPosition()方法移动。
* With the extras added to the Intent, we invoke the Intent on the NoteEdit class by passing startActivityForResult() the Intent and the request code. (The request code will be returned to onActivityResult as the requestCode parameter.)
3、附加信息加入Intent中,我们在NoteEdit类型中调用Intent,吧Intent和请求代码传递给startActivityForResult()。(请求代码作为requestCode参数在onActivityResult中返回。)
Note: We assign the mNotesCursor field to a local variable at the start of the method. This is done as an optimization of the Android code. Accessing a local variable is much more efficient than accessing a field in the Dalvik VM, so by doing this we make only one access to the field, and five accesses to the local variable, making the routine much more efficient. It is recommended that you use this optimization when possible.
注意:我们把mNotesCursor属性赋值到本地变量,这是对Android代码的优化。访问本地变量比访问DalvikVM的一个属性更有效率,因此,这样做了后我们访问了这个属性一次,访问本地变量5次,使程序有效得多。推荐尽可能的使用这种优化方法。
Step 6
第六步
The above createNote() and onListItemClick() methods use an asynchronous Intent invocation. We need a handler for the callback, so here we fill in the body of the onActivityResult().
上面提到的createNote()和onListItemClick()方法使用了异步Intent引用。我们需要处理回调行数,因此,此处我们填充onActivityResult()代码。
onActivityResult() is the overridden method which will be called when an Activity returns with a result. (Remember, an Activity will only return a result if launched with startActivityForResult.) The parameters provided to the callback are:
onActivityResult()是一个重载方法,当Activity返回结果的时候被调用。(记住,如果使用startActivityForResult触发,则Activity仅仅会返回一个结果。) 回调函数提供的参数如下:
* requestCode — the original request code specified in the Intent invocation (either ACTIVITY_CREATE or ACTIVITY_EDIT for us).
1、requestCode——在Intent引用中指定的原始请求代码(要么是ACTIVITY_CREATE,要么是ACTIVITY_EDIT)
* resultCode — the result (or error code) of the call, this should be zero if everything was OK, but may have a non-zero code indicating that something failed. There are standard result codes available, and you can also create your own constants to indicate specific problems.
2、resultCode——调用的结果或者错误代码,如果一切都没有问题,应该是0,但是可以是非0值,则表示有错误发生。有标准的结果代码获得,你也可以创建你自己的常量来指示特定错误。
* intent — this is an Intent created by the Activity returning results. It can be used to return data in the Intent "extras."
3、intent——通过有返回结果的Activty创建的Intent。他能用于返回数据,数据存放在Intent的“extras”中。
The combination of startActivityForResult() and onActivityResult() can be thought of as an asynchronous RPC (remote procedure call) and forms the recommended way for an Activity to invoke another and share services.
startActivityForResult()和onActivityResult()的结合能作为异步RPC,提供活动相互调用和共享服务的推荐方法。
Here's the code that belongs in your onActivityResult():
下面是onActivityResult()的代码:
super.onActivityResult(requestCode, resultCode, intent);
Bundle extras = intent.getExtras();
switch(requestCode) {
case ACTIVITY_CREATE:
String title = extras.getString(NotesDbAdapter.KEY_TITLE);
String body = extras.getString(NotesDbAdapter.KEY_BODY);
mDbHelper.createNote(title, body);
fillData();
break;
case ACTIVITY_EDIT:
Long mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
if (mRowId != null) {
String editTitle = extras.getString(NotesDbAdapter.KEY_TITLE);
String editBody = extras.getString(NotesDbAdapter.KEY_BODY);
mDbHelper.updateNote(mRowId, editTitle, editBody);
}
fillData();
break;
}
* We are handling both the ACTIVITY_CREATE and ACTIVITY_EDIT activity results in this method.
1、在这个方法中,我们处理 ACTIVITY_CREATE 和 ACTIVITY_EDIT两种活动返回结果。
* In the case of a create, we pull the title and body from the extras (retrieved from the returned Intent) and use them to create a new note.
2、在创建的代码中,我们从extras获取标题和内容,然后用来创建新的便签。
* In the case of an edit, we pull the mRowId as well, and use that to update the note in the database.
3、在编辑的代码中,我们还是获取mRowId,然后用来修改数据库的便签数据。
* fillData() at the end ensures everything is up to date .
4、fillData()保证所有信息都是最新的。
Step 7
第七步
The Art of Layout
布局的艺术
The provided note_edit.xml layout file is the most sophisticated one in the application we will be building, but that doesn't mean it is even close to the kind of sophistication you will be likely to want in real Android applications.
note_edit.xml布局文件是我们建立的应用程序中最成熟的一个文件,但是那不并意味着接近你在真实Android程序中可能要求的那种成熟。
Creating a good UI is part art and part science, and the rest is work. Mastering Android layout is an essential part of creating a good looking Android application.
创建好的UI部分(参见“Android layout”)是艺术部分是科学,剩余的就是工作。掌握Android布局是创建好看的Android程序的基本部分。
Take a look at the View Gallery for some example layouts and how to use them. The ApiDemos sample project is also a great resource from which to learn how to create different layouts.
仔细看看一些例子的布局,研究怎么使用他们,参见“View Gallery”。ApiDemos例子项目也是好资源,可以学习了解怎么创建不懂的布局。
Open the file note_edit.xml that has been provided and take a look at it. This is the UI code for the Note Editor.
打开已经提供的note_edit.xml文件,仔细看看。这是Note Editor的UI代码。
This is the most sophisticated UI we have dealt with yet. The file is given to you to avoid problems that may sneak in when typing the code. (The XML is very strict about case sensitivity and structure, mistakes in these are the usual cause of problems with layout.)
这是我们已经处理的最成熟的UI。提供这个文件为了避免问题,这些问题可能由于键入代码而悄悄潜入。(XML是非常严格的,在大小写和结构等方面的错误是布局问题的最常见原因。)
There is a new parameter used here that we haven't seen before: android:layout_weight (in this case set to use the value 1 in each case).
这里有一个以前没有见过的新参数:android:layout_weight(本例中在所有情况下设置为值1)
layout_weight is used in LinearLayouts to assign "importance" to Views within the layout. All Views have a default layout_weight of zero, meaning they take up only as much room on the screen as they need to be displayed. Assigning a value higher than zero will split up the rest of the available space in the parent View, according to the value of each View's layout_weight and its ratio to the overall layout_weight specified in the current layout for this and other View elements.
layout_weight用在LinearLayouts中,在布局中赋值"importance"给View。所有View默认layout_weight值为0,意味着他们仅仅占据他们需要的显示空间。赋值大于0将会在父View中分割可获得的剩余空间,分割空间的依据是每个View的layout_weight的值,还有在View元素的现有布局中指定的全部layout_weight的比率。
To give an example: let's say we have a text label and two text edit elements in a horizontal row. The label has no layout_weight specified, so it takes up the minimum space required to render. If the layout_weight of each of the two text edit elements is set to 1, the remaining width in the parent layout will be split equally between them (because we claim they are equally important). If the first one has a layout_weight of 1 and the second has a layout_weight of 2, then one third of the remaining space will be given to the first, and two thirds to the second (because we claim the second one is more important).
举一个例子:假设我们在一行中有一个文本标签和两个文本编辑框。标签没有指定layout_weight,因此他占据必须的最小空间。如果两个文本编辑框的layout_weight都设置为1,可在父窗口中的剩余宽度被他们平分(因为我们声称他们同等重要)。如果第一个是1,第二个是2,那么剩余空间的三分之一分配给第一个,三分之二分配给第二个(因为我们要求第二个更重要)
This layout also demonstrates how to nest multiple layouts inside each other to achieve a more complex and pleasant layout. In this example, a horizontal linear layout is nested inside the vertical one to allow the title label and text field to be alongside each other, horizontally.
这个布局文件也证明怎么互相嵌套多个布局元素,以便获得更多复杂和合适的布局界面。本例中,水平线布局嵌套进入垂直线,以便允许标题标签和文本字段相互并排。
Step 8
第八步
Create a NoteEdit class that extends android.app.Activity.
创建NoteEdit类,继承android.app.Activity
This is the first time we will have created an Activity without the Android Eclipse plugin doing it for us. When you do so, the onCreate() method is not automatically overridden for you. It is hard to imagine an Activity that doesn't override the onCreate() method, so this should be the first thing you do.
我们第一次没有在ADT帮助下创建Activity。onCreate()方法没有自动重载。想像没有重载onCreate()方法的Activity是困难的,因此,首先要做的事情是重载onCreate()
1. Right click on the com.android.demo.notepad2 package in the Package Explorer, and select New > Class from the popup menu.
1、右击com.android.demo.notepad2包,从弹出菜单中选择 New > Class
2. Fill in NoteEdit for the Name: field in the dialog.
2、在对话框的“Name:”标签后填入“NoteEdit”
3. In the Superclass: field, enter android.app.Activity (you can also just type Activity and hit Ctrl-Space on Windows and Linux or Cmd-Space on the Mac, to invoke code assist and find the right package and class).
3、在“Superclass: ”标签中,键入“android.app.Activity”(你也能输入Activity,按 Ctrl-Space,启用代码助手找到正确的包和类)
4. Click Finish.
4、点击Finish
5. In the resulting NoteEdit class, right click in the editor window and select Source > Override/Implement Methods...
5、在NoteEdit类中,右击编辑窗口,选择菜单Source > Override/ImplementMethods...
6. Scroll down through the checklist in the dialog until you see onCreate(Bundle) — and check the box next to it.
6、滚动对话框中的清单,找到onCreate(Bundle)——选中前面的检查框
7. Click OK.
7、点击OK
The method should now appear in your class.
这个方法应该出现在你的类中。
Step 9
第九步
Fill in the body of the onCreate() method for NoteEdit.
填写onCreate()的代码
This will set the title of our new Activity to say "Edit Note" (one of the strings defined in strings.xml). It will also set the content view to use our note_edit.xml layout file. We can then grab handles to the title and body text edit views, and the confirm button, so that our class can use them to set and get the note title and body, and attach an event to the confirm button for when it is pressed by the user.
这里将会设置新活动的题目为“Edit Note”(在strings.xml文件中定义的一个字符串)。也会设置内容视图以便使用note_edit.xml布局文件。然后,处理标题和内容文本编辑框、确认按钮,因此,我们的类能使用他们来获取或者设置便签的标题和内容,附着事件到确认按钮,这个事件在用户按确认按钮时触发。
We can then unbundle the values that were passed in to the Activity with the extras Bundle attached to the calling Intent. We'll use them to pre-populate the title and body text edit views so that the user can edit them. Then we will grab and store the mRowId so we can keep track of what note the user is editing.
然后,我们对传入到Activity的值进行分类,传入值使用Activity的extras Bundle,extras Bundle附着到调用的Intent上。我们会使用他们来预先填充标题和内容文本编辑框,因此,用户能编辑他们。然后,我们会获取和存储mRowId,因此我们能留意用户编辑的什么便签。
1. Inside onCreate(), set up the layout:
1、在onCreate()内部,设置布局:
setContentView(R.layout.note_edit);
2. Find the edit and button components we need:
2、找到我们需要的编辑框和按钮组件:
These are found by the IDs associated to them in the R class, and need to be cast to the right type of View (EditText for the two text views, and Button for the confirm button):
在R类中,通过ID找到他们,需要强制转换到正确的视图类型(两个文本视图是EditText,确认按钮时Button)
mTitleText = (EditText) findViewById(R.id.title);
mBodyText = (EditText) findViewById(R.id.body);
Button confirmButton = (Button) findViewById(R.id.confirm);
Note that mTitleText and mBodyText are member fields (you need to declare them at the top of the class definition).
注意:mTitleText 和 mBodyText是成员变量(你需要在顶部声明他们)
3. At the top of the class, declare a Long mRowId private field to store the current mRowId being edited (if any).
3、在类的顶部,生命一个Long类型的mRowId私有变量,用来存储当前编辑的mRowId
4. Continuing inside onCreate(), add code to initialize the title, body and mRowId from the extras Bundle in the Intent (if it is present):
4、继续在onCreate()中增加代码来初始化标题、内容、mRowId,从Intent的extras Bundle(附加包)中获取初始化数据。
mRowId = null;
Bundle extras = getIntent().getExtras();
if (extras != null) {
String title = extras.getString(NotesDbAdapter.KEY_TITLE);
String body = extras.getString(NotesDbAdapter.KEY_BODY);
mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
if (title != null) {
mTitleText.setText(title);
}
if (body != null) {
mBodyText.setText(body);
}
}
* We are pulling the title and body out of the extras Bundle that was set from the Intent invocation.
(1)从来自Intent引用的extras Bundle中获取标题和内容
* We also null-protect the text field setting (i.e., we don't want to set the text fields to null accidentally).
(2)零保护文本框设置(例如,我们不想意外地设置文本框为null)
5. Create an onClickListener() for the button:
5、针对按钮创建onClickListener()
Listeners can be one of the more confusing aspects of UI implementation, but what we are trying to achieve in this case is simple. We want an onClick() method to be called when the user presses the confirm button, and use that to do some work and return the values of the edited note to the Intent caller. We do this using something called an anonymous inner class. This is a bit confusing to look at unless you have seen them before, but all you really need to take away from this is that you can refer to this code in the future to see how to create a listener and attach it to a button. (Listeners are a common idiom in Java development, particularly for user interfaces.) Here's the empty listener:
Listener使UI实现更混乱的一个方面,但是,本例中试图完成的事情是简单的。当用户按确认按钮时,调用onClieck()方法,用来完成一些工作并且然会被编辑的便签的值给Intent调用者。我们利用称为匿名内嵌类的东西来做这些事情。如果你以前没有见到过,那么看起来这有点混乱,但是,你实际需要取走(完成)的所有事情是可以在未来引用这里的代码,注意怎样创建一个Listener,并附着到一个按钮(Listener是Java开发的一个习惯用语,尤其是在UI方面)。下面是空的Listener:
confirmButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
}
});
Step 10
第十步
Fill in the body of the onClick() method in our listener.
填充侦听器里面的onClick()代码。
This is the code that will be run when the user clicks on the confirm button. We want this to grab the title and body text from the edit text fields, and put them into the return Bundle so that they can be passed back to the Activity that invoked this NoteEdit Activity. If the operation is an edit rather than a create, we also want to put the mRowId into the Bundle so that the Notepadv2 class can save the changes back to the correct note.
这些代码在用户点击确认按钮时运行。我们使用这些代码从编辑框中获取标题和内容文本串,然后把它们送给Bundle,以便他们能传回到Activity(NoteEdit Activity调用)。如果操作是编辑而不是创建,那么传送mRowId进入Bundle,以便Notepav2类能保存变化到正确的便签。
1. Create a Bundle and put the title and body text into it using the constants defined in Notepadv2 as keys:
1、创建Bundle,传递标题和内容文本到Bundle,使用在Notepadv2中定义的常量作为关键码:
Bundle bundle = new Bundle();
bundle.putString(NotesDbAdapter.KEY_TITLE, mTitleText.getText().toString());
bundle.putString(NotesDbAdapter.KEY_BODY, mBodyText.getText().toString());
if (mRowId != null) {
bundle.putLong(NotesDbAdapter.KEY_ROWID, mRowId);
}
2. Set the result information (the Bundle) in a new Intent and finish the Activity:
2、在新的Intent中设置结果信息(Bundle),完成Activity:
Intent mIntent = new Intent();
mIntent.putExtras(bundle);
setResult(RESULT_OK, mIntent);
finish();
* The Intent is simply our data carrier that carries our Bundle (with the title, body and mRowId).
(1)Intent是简单的数据运载工具,运载Bundle(携带标题、内容、mRowId)
* The setResult() method is used to set the result code and return Intent to be passed back to the Intent caller. In this case everything worked, so we return RESULT_OK for the result code.
(2)setResult()方法用于设置结果代码,返回Intent为了传回到Intent调用者。本例中,每件事情都正常工作,因此我们返回RESULT_OK作为结果代码
* The finish() call is used to signal that the Activity is done (like a return call). Anything set in the Result will then be returned to the caller, along with execution control.
(3)finish()调用用于发信号表明Activity被激发(像一个回调)。然后,在Result中设置的所有东西会返回给调用者,连同执行控制一起
The full onCreate() method (plus supporting class fields) should now look like this:
完整的onCreate()如下:
private EditText mTitleText;
private EditText mBodyText;
private Long mRowId;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
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 = null;
Bundle extras = getIntent().getExtras();
if (extras != null) {
String title = extras.getString(NotesDbAdapter.KEY_TITLE);
String body = extras.getString(NotesDbAdapter.KEY_BODY);
mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
if (title != null) {
mTitleText.setText(title);
}
if (body != null) {
mBodyText.setText(body);
}
}
confirmButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Bundle bundle = new Bundle();
bundle.putString(NotesDbAdapter.KEY_TITLE, mTitleText.getText().toString());
bundle.putString(NotesDbAdapter.KEY_BODY, mBodyText.getText().toString());
if (mRowId != null) {
bundle.putLong(NotesDbAdapter.KEY_ROWID, mRowId);
}
Intent mIntent = new Intent();
mIntent.putExtras(bundle);
setResult(RESULT_OK, mIntent);
finish();
}
});
}
Step 11
第11步
The All-Important Android Manifest File
非常重要的Android Manifest文件
The AndroidManifest.xml file is the way in which Android sees your application. This file defines the category of the application, where it shows up (or even if it shows up) in the launcher or settings, what activities, services, and content providers it defines, what intents it can receive, and more.
AndroidManifest.xml文件是Android领会你的程序的方式。这个文件定义了程序的类型,在发射器和设置里面露面的地方,定义了活动、服务、内容提供器是什么,能接收的intent是什么,等等。
For more information, see the reference document AndroidManifest.xml
欲知更多信息参见文档“AndroidManifest.xml”
Finally, the new Activity has to be defined in the manifest file:
最后,新Activity必须定义在manifest文件中:
Before the new Activity can be seen by Android, it needs its own Activity entry in the AndroidManifest.xml file. This is to let the system know that it is there and can be called. We could also specify which IntentFilters the activity implements here, but we are going to skip this for now and just let Android know that the Activity is defined.
在新Activity能被Android了解之前,需要在AndroidManifest.xml中定义自己的Activity入口。这是让系统知道它在那儿且能被调用。我们也能指定Activity实现哪个IntentFilters,但是我们打算暂时跳过这个,只是让Android知道Activity被定义了。
There is a Manifest editor included in the Eclipse plugin that makes it much easier to edit the AndroidManifest file, and we will use this. If you prefer to edit the file directly or are not using the Eclipse plugin, see the box at the end for information on how to do this without using the new Manifest editor.
在Eclipse插件中包含了一个Manifest编辑器,使AndroidManifest文件编辑更加容易,我们将会使用这个编辑器。
1. Double click on the AndroidManifest.xml file in the package explorer to open it.
1、在AndroidManifest.xml文件上双击打开它
2. Click the Application tab at the bottom of the Manifest editor.
2、在Manifest编辑器底部点击Application便签
3. Click Add... in the Application Nodes section.
3、在Application Nodes段中点击 Add...
If you see a dialog with radiobuttons at the top, select the top radio button: "Create a new element at the top level, in Application".
如果你在顶部看到一个带有单选按钮的对话框,那么选择顶部的单选按钮"Create a new element at the top level, in Application"
4. Make sure "(A) Activity" is selected in the selection pane of the dialog, and click OK.
4、保证 "(A) Activity"在对话框的选择面板中被选中,然后点击OK
5. Click on the new "Activity" node, in the Application Nodes section, then type .NoteEdit into the Name* field to the right. Press Return/Enter.
5、在 Application Nodes段中,点击新的"Activity"节点,然后在“Name*”标签右边输入.NoteEdit,按Return/Enter
The Android Manifest editor helps you add more complex entries into the AndroidManifest.xml file, have a look around at some of the other options available (but be careful not to select them otherwise they will be added to your Manifest). This editor should help you understand and alter the AndroidManifest.xml file as you move on to more advanced Android applications.
Android Manifest编辑器帮助你增加更多复杂的条目到AndroidManifest.xml文件中,仔细看看可获得的其他选项(但是小心不要选择他们否则他们会被加入到你的Manifest文件中)。这个编辑器应该帮助你理解和修改AndroidManifest.xml文件,当你移到更多高级的Android程序时会很有用处。
If you prefer to edit this file directly, simply open the AndroidManifest.xml file and look at the source (use the AndroidManifest.xml tab in the eclipse editor to see the source code directly). Then edit the file as follows:
如果你宁愿直接编辑这个文件,简单打开 AndroidManifest.xml文件,然后了解资源(使用eclipse编辑器的AndroidManifest.xml标签直接领悟源代码)。然后,按照下面这样编辑文件:
This should be placed just below the line that reads:
这应该表示:
for the .Notepadv2 activity.
针对.Notepadv2活动
Step 12
第12步
Now Run it!
现在运行。
You should now be able to add real notes from the menu, as well as delete an existing one. Notice that in order to delete, you must first use the directional controls on the device to highlight the note. Furthermore, selecting a note title from the list should bring up the note editor to let you edit it. Press confirm when finished to save the changes back to the database.
现在,你应该能从菜单增加真实的便签,也能删除存在的便签。注意:为了删除,你必须首先使用设备方向控制来选中便签。此外,从列表中选择便签标题应该被提出到便签编辑器,以便你能编辑他。按确认按钮保证保存修改到数据库中。
2008年12月16日星期二
教程:Notepad练习1
教程:Notepad练习1
在这个练习中,你将会构建一个简单的便签列表,让用户增加新便签,而不能编辑。这个练习展示了:
* ListActivities的基本用法,创建和处理菜单选项
* 怎样使用SQLite数据库来存储便签
* 怎样使用SimpleCursorAdapter来绑定数据库游标上的数据到ListView上
* 界面布局的基本用法,包括怎样摆放一个列表视图,怎样增加条目到活动菜单中,活动怎样处理菜单选择事件。
第一步
在Eclipse中打开Notepadv1项目
Notepadv1是作为起始点提供的项目。该工程项目关注一些样板工作,正如你在“Hello Android tutorial”中看到的一样。
1、通过点击菜单File > New > Android Project进入一个新的Android项目
2、在New Android Project对话框中,选择Create project from existing source
3、点击Browse,然后选择你存放NotepadCodeLab的目录。选择Notepadv1,然后点击Choose。
4、你会在Project name中看到Notepadv1,也会看到填充了你选择的路径的Location。
5、点击Finish。Notepadv1项目应该打开了,并在Eclipse package explorer(包浏览器)中可见
如果你看到关于AndroidManifest.xml的错误,或者关于Android zip文件的问题,在项目上右键,选择菜单Android Tools > Fix Project Properties。(这个项目看起来库文件处于错误的位置,这种办法会替你纠正它。)
第二步
看看NotesDbAdapter类——这个类提供SQLite数据库访问的封装,将会保存我们的便签数据,允许我们修改它。
这个类顶部是一些常量定义,程序使用这些常量来查找数据库中合适字段的数据。也定义了一个数据库创建字串,用于创建新数据库表格,如果不存在的话。
我们的数据库的名称为data,还有一个成为notes的表,按次序有三个字段:_id,title,body。_id命名使用下划线规则,这个规则用于许多地方,包括AndroidSDK,有助于保持状态跟踪。通常,当查询或者修改数据库(在列预测等等)时_id必须定义。其他两个字段是存储数据的简单的文本字段。
NotesDbAdapter的构造函数使用了Context,Context允许和Android操作系统的各个方面通讯。对于需要以某种方式和Android系统接触的类,这是很常见的。Activity类重新实现了Context类,因此,当你需要Context时,通常你只需要从Activity传递Context。
open()方法调用一个DatabaseHelper实例,DatabaseHelper是SQLiteOpenHelper类的本地实现。它调用getWritableDatabase()来处理创建、打开数据库的操作。
close()仅仅关闭数据库,释放和连接相关的资源。
createNote()携带标题文本字串和新便签的内容,然后再数据库中创建那个便签。假如新便签成功创建,这个方法返回新创建的便签所在行的_id的值。
deleteNote()携带指向一个特定便签的rowId,然后从数据库中删除那条便签。
fetchAllNotes()发出一个查询,返回数据库中所有便签的游标。query()调用是值得研究和理解。第一个字段是查询的数据表的名称(这个例子DATABASE_TABLE是“notes”)。下面是我们想返回的列的列表,这个例子就是_id、title和列的数据体,因此被指定放到String数组中。剩下的字段依次是:selection、selectionArgs、groupBy、having和orderBy。这些字段都允许空意味著我们允许存放所有的数据,不必分组,采用默认的排序方式。更多细节请参见“SQLiteDatabase”。
注意:返回的是游标而不是记录集。这允许Android有效使用资源——避免直接加载大量数据到内容中,当需要的时候,游标会获取和释放数据,这对于大表是非常有效的。
fectchNote()类似于fectchAllNotes(),仅仅通过我们指定的rowId获取一条便签。他使用SQLiteDatabase的query()方法的轻量级版本。第一个参数(设置为true)表示我们对一个明显的结果感兴趣(只返回唯一的一行)。selection参数(第四个)有值表示仅仅返回符合“where _id=我们传送的rowId”的记录。因此,我们获得指向某行的游标。
最后,updateNote()针对rowId、title和body,使用ContentValue实例修改对应rowId的便签。
第三步
打开res/layout目录下的notepad_list.xml文件,仔细看看。(为了浏览XML标记你可能必须点击XML便签页,在底部)
这是一个大部分空着的界面定义文件。下面是你应该知道的关于界面文件的一些事情:
*所有界面文件必须以作为第一行
*下一个定义通常但不总是某种界面元素定义,比如LinearLayout
*XML名称空间应该总是定义在XML文件中的顶级组件或者界面,因此文件的其余部分总是能看到android标签:
xmlns:android="http://schemas.android.com/apk/res/android"
界面与活动
多数活动类会有一个与之联系的界面。界面是活动的呈现给用户的脸面,这个例子中,我们的界面占据整个屏幕,显示一个便签列表。
但是,对于一个活动来讲,全屏幕界面不是唯一选项。你也可能想使用一个浮动的界面(例如对话框和警告提示框),或者也许你根本不需要一个界面(如果你不指定活动使用的某种界面,则活动将是用户不可见的)。
第四步
我们需要创建界面来容纳我们的列表。增加包含LinearLayout元素,整个文件看起来像这样:
android:layout_width="wrap_content"
android:layout_height="wrap_content">
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/no_notes"/>
*ListView和TextView标签的id串中的@符号意思是:XML语法分析器应该解析和扩展id串的剩余部分,使用一个ID资源扩展。
*ListView和TextView可以作为两个可互相替换的视图,仅仅其中一个会被立即显示。
*通过Android平台列表和空的ID提供给我们,因此,我们必须使用android作为id的前缀。例如,@android:id/list
*当ListAdapter没有数据提供给ListView时,系统自动使用携带空id的视图。默认情况下,ListAdapter知道寻找这个名字。另外,你应该使用ListView的serEmptyView(VIew)方法来改变默认的空视图。
基本上说,android.R类是平台提供的一组预定义的资源,而你的项目的R类是你自己项目已经定义的一组资源。在android.R资源类找到的资源能被用于XML文件,通过使用android:名称空间前缀。
第五步
为了在ListView中创建便签列表,我们也需要为每一行定义一个VIew:
1、在res/layout目录下创建一个新文件,取名为notes_row.xml
2、增加如下的内容(注意:XML头部又被使用,第一个节点定义了android XML名称空间)
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
这是用于每个便签标题行的View——仅有一个文本字段。
在这个例子中,我们创建一个新的id,称为text1.@号后面的+表示如果id不存在那么自动作为资源创建,因此我们在text1,然后使用它。
3、保存文件
在项目中打开R.java类看看它,你将会看到新的定义:notes_row和text1,意味着我们现在能在代码中访问这些。
资源和R类
res/文件夹用于资源。res/下面有对于文件夹和文件的特别结构。
这些文件夹和文件中定义的资源将会对应R类中的条目,以便允许他们容易被访问和使用。R类由eclipse插件根据res/的内容自动产生(或者,在命令行工具中使用aapt命令)。作为应用程序的一部分,他们可展开和收集。
第六步
下面,在源代码中打开Noteadv1类。下面的步骤中,我们打算替换这个类,让其变成列表适配器,显示我们的便签,也会允许增加新的便签。
Notepadv1将会继承Activity的子类ListActivity,ListActivity有额外的功能以容纳多种可能加入的列表功能,例如:在每行的列表条目上显示任意的数字、移动列表条目、选择列表条目。
仔细查看Notepadv1类的已存在的代码。有一个还没有使用的私有字段叫做mNoteNumber,我们将会用来创建编号了的便签的标题。
还有三个重载方法:onCreate,OnCreateOptionsMenu和OnOptionsItemSelected,我们需要填充他们。
* onCreate()在活动启动的时候调用——有点像活动的“main”方法。运行时使用这个方法来设置资源和状态。
*onCreateOptionsMenu()用于增加活动的菜单。当用户点击菜单按钮时将会表现出来,有一个选项列表能选择。
*onOptionsItemSelected()是菜单等式的另外一半,用于处理菜单事件(例如,在用户选择“Create NOte”子菜单时)
第七步
修改Notepadv1的继承关系,从Activity改为ListActivity:
public class Notepadv1 extends ListActivity
注意:你必须使用Eclipse导入ListActivity到Notepadv1类中,Windows 和Linux的热键为ctrl-shift-O,Mac的热键cmd-shift-O。
第八步
填充onCreate()方法的代码。
这里我们会设置活动的标题(显示在屏幕的顶部),使用我们在XML文件中创建的notepad_list界面,设置NotesDbAdapter实例以便将来访问便签数据,使用可获取的便签标题来增加列表:
1、在onCreate方法中,携带savedInstanceState参数调用super()
2、调用setContentView(),传递R.layout.notepad_list
3、在类的顶部,创建一个NotesDbAdapter类的私有属性,称为mDbHelper
4、在onCreate方法中,构建一个新NotesDbAdapter实例,绑定到mDbHelper字段(传递给DBHelper的构造函数)
5、在mDbHelper上调用open()方法,以打开数据库
6、最后,调用新方法fillData(),作用是:获取数据,使用helper增加ListView——我们还没有定义这个方法
onCreate() 应该看起来这样:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.notepad_list);
mDbHelper = new NotesDbAdapter(this);
mDbHelper.open();
fillData();
}
并且保证你已经定义了mDbHelper域(正好在mNoteNumber下)
private NotesDbAdapter mDbHelper;
第九步
填写onCreateOptionsMenu()的代码。
现在我们会创建“Add Item”按钮,这个按钮能在设备上点击。我们会指定他在菜单上占据的位置.
1、strings.xml中增加一个名叫“menu_insert”的新字串,该字串的值为Add Item:
Add Item
然后保存文件并返回到Notepadv1.
2、在类的顶部创建菜单位置常量
public static final int INSERT_ID = Menu.FIRST;
3、OnCreateOptionsMenu()中修改super的调用,因此我们捕获布尔变量作为结果返回。我们将会在最后返回这个值。
4、然后,使用menu.add()方法增加菜单。
整个方法应该看起来这样:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
boolean result = super.onCreateOptionsMenu(menu);
menu.add(0, INSERT_ID, 0, R.string.menu_insert);
return result;
}
传给add()的参数是:这个菜单的上级菜单标识(这个例子无),一个唯一ID(如上的定义),菜单项的顺序(o表示没有优先级),用于这个条目的资源串。
关于菜单的更多知识
我们构造的notepad程序仅仅接触了菜单的表面。
你还能“增加菜单项的快捷键”、“创建子菜单”,甚至增加菜单项到其他应用。
第十步
填充onOptionsItemSelected()方法的代码。
打算处理我们新的Add Note菜单项。当菜单被选择时,onOptionsItemSelected方法携带item.getId(),设置到INSERT_ID(这个常数我们用来识别菜单项)。我们能检测这个选择事件,作出适当的动作:
1、super.onOptionsItemSelected(item)方法的调用发生在这个方法的最后——我们想首先捕获我们的事件。
2、在item.getItemID()中写一个转换状态。
INSERT_ID的例子中,调用一个新的方法,createNote(),返回true,因为我们已经处理了这个时间,不想通过系统传递它。
3、最后,返回超类的onOptionsItemSelected方法的结果
onOptionsItemSelected方法应该看起来如下:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case INSERT_ID:
createNote();
return true;
}
return super.onOptionsItemSelected(item);
}
第11步
增加一个新的createNote方法:
我们的程序的第一个版本,createNote()不会很有用。我们将会简单地创建新的带有标题的便签,假定标题基于计数器、便签内容是空的。目前,我们无法编辑便签的内容,因此现在我们毫无疑问是设置默认值的内容:
1、使用Note和计数器来构造名称,我们在类中定义String noteName = "Note " + mNoteNumber++
2、调用mDbHelper.createNote(),使用noteName作为标题,内容就写为""
3、调用fillData(),组装便签列表(低效但是简单)——我们将会创建这个方法
createNote方法看起来如下:
private void createNote() {
String noteName = "Note " + mNoteNumber++;
mDbHelper.createNote(noteName, "");
fillData();
}
第12步
定义fillData方法:
这个方法使用SimpleCursorAdapter,SimpleCursorAdapter获取一个数据库游标,绑定到界面中提供的字段上。这个字段定义了我们列表的行元素(这个例子中,我们使用notes_row.xml界面的text1字段),因此允许我们轻易地使用数据库中的数据项来组装列表。
我们必须提供一个映射,从返回的游标的标题字段映射到我们的text1文本视图中,定义了两个数组:第一个,字串数组,映射到列(这个例子中,仅仅标题,来自常量NotesDbAdapter.KEY_TITLE),第二个,整型数组,包含到绑定了数据的视图(R.id.text1文本视图)的引用
这是一个较大块的代码,因此让我们首先看看:
private void fillData() {
// Get all of the notes from the database and create the item list
Cursor c = mDbHelper.fetchAllNotes();
startManagingCursor(c);
String[] from = new String[] { NotesDbAdapter.KEY_TITLE };
int[] to = new int[] { R.id.text1 };
// Now create an array adapter and set it to display using our row
SimpleCursorAdapter notes =
new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to);
setListAdapter(notes);
}
以下是我们已经做得事情:
1、当从mDbHelper.fetchAllNotes()获得游标后,我们使用一个称为startManagingCursor()的活动的方法,该方法允许Android照看游标的生命周期,而不需要我们担心她。(我们将会在练习3中牵涉生命周期,但是现在仅仅知道:这让Android为我们做一些资源管理的事情)
2、然后,我们创建一个字符串数组映射到标题,一个整型数组映射到视图
3、下一步是实例化SimpleCursorAdapter。象Android的许多类一样,SimpleCursorAdapter需要Context,因此,我们传入上下文环境参数(因为Activity的子类实现了Context)。我们传递notes_row视图(作为数据容器创建),游标(刚刚创建的),数组。
将来记住,从列到资源的映射用于决定两个数组的顺序。如果我们想绑定有更多的列,对应的有更多的视图绑定,那么我们会按顺序指定它们,例如:我们可能使用{ NotesDbAdapter.KEY_TITLE, NotesDbAdapter.KEY_BODY }和{ R.id.text1, R.id.text2 }来绑定两个字段进入记录行(而且,我们也会需要在notes_row.xml中定义text2,针对内容字符串)。这是绑定多个字段到单记录的方法(而且,也获得一个自定义的记录行的界面)。
If you get compiler errors about classes not being found, ctrl-shift-O or (cmd-shift-O on the mac) to organize imports.
如果你看到这个类的编译错误是没有找到,那么按ctrl-shift-O 来组织import。
第13步
运行你的程序
1、右击Notepadv1项目
2、从弹出菜单中选择Run As > Android Application.
3、如果你看到一个对话框出现,选择Android发射器作为运行程序的方法(你也能使用对话框顶部附近的链接,设置你的工作区的默认启动方式:这种方式是推荐的因为每次回询问你是否暂停插件)
4、通过点击menu按钮选择Add Item菜单,来增加新的便签
在这个练习中,你将会构建一个简单的便签列表,让用户增加新便签,而不能编辑。这个练习展示了:
* ListActivities的基本用法,创建和处理菜单选项
* 怎样使用SQLite数据库来存储便签
* 怎样使用SimpleCursorAdapter来绑定数据库游标上的数据到ListView上
* 界面布局的基本用法,包括怎样摆放一个列表视图,怎样增加条目到活动菜单中,活动怎样处理菜单选择事件。
第一步
在Eclipse中打开Notepadv1项目
Notepadv1是作为起始点提供的项目。该工程项目关注一些样板工作,正如你在“Hello Android tutorial”中看到的一样。
1、通过点击菜单File > New > Android Project进入一个新的Android项目
2、在New Android Project对话框中,选择Create project from existing source
3、点击Browse,然后选择你存放NotepadCodeLab的目录。选择Notepadv1,然后点击Choose。
4、你会在Project name中看到Notepadv1,也会看到填充了你选择的路径的Location。
5、点击Finish。Notepadv1项目应该打开了,并在Eclipse package explorer(包浏览器)中可见
如果你看到关于AndroidManifest.xml的错误,或者关于Android zip文件的问题,在项目上右键,选择菜单Android Tools > Fix Project Properties。(这个项目看起来库文件处于错误的位置,这种办法会替你纠正它。)
第二步
看看NotesDbAdapter类——这个类提供SQLite数据库访问的封装,将会保存我们的便签数据,允许我们修改它。
这个类顶部是一些常量定义,程序使用这些常量来查找数据库中合适字段的数据。也定义了一个数据库创建字串,用于创建新数据库表格,如果不存在的话。
我们的数据库的名称为data,还有一个成为notes的表,按次序有三个字段:_id,title,body。_id命名使用下划线规则,这个规则用于许多地方,包括AndroidSDK,有助于保持状态跟踪。通常,当查询或者修改数据库(在列预测等等)时_id必须定义。其他两个字段是存储数据的简单的文本字段。
NotesDbAdapter的构造函数使用了Context,Context允许和Android操作系统的各个方面通讯。对于需要以某种方式和Android系统接触的类,这是很常见的。Activity类重新实现了Context类,因此,当你需要Context时,通常你只需要从Activity传递Context。
open()方法调用一个DatabaseHelper实例,DatabaseHelper是SQLiteOpenHelper类的本地实现。它调用getWritableDatabase()来处理创建、打开数据库的操作。
close()仅仅关闭数据库,释放和连接相关的资源。
createNote()携带标题文本字串和新便签的内容,然后再数据库中创建那个便签。假如新便签成功创建,这个方法返回新创建的便签所在行的_id的值。
deleteNote()携带指向一个特定便签的rowId,然后从数据库中删除那条便签。
fetchAllNotes()发出一个查询,返回数据库中所有便签的游标。query()调用是值得研究和理解。第一个字段是查询的数据表的名称(这个例子DATABASE_TABLE是“notes”)。下面是我们想返回的列的列表,这个例子就是_id、title和列的数据体,因此被指定放到String数组中。剩下的字段依次是:selection、selectionArgs、groupBy、having和orderBy。这些字段都允许空意味著我们允许存放所有的数据,不必分组,采用默认的排序方式。更多细节请参见“SQLiteDatabase”。
注意:返回的是游标而不是记录集。这允许Android有效使用资源——避免直接加载大量数据到内容中,当需要的时候,游标会获取和释放数据,这对于大表是非常有效的。
fectchNote()类似于fectchAllNotes(),仅仅通过我们指定的rowId获取一条便签。他使用SQLiteDatabase的query()方法的轻量级版本。第一个参数(设置为true)表示我们对一个明显的结果感兴趣(只返回唯一的一行)。selection参数(第四个)有值表示仅仅返回符合“where _id=我们传送的rowId”的记录。因此,我们获得指向某行的游标。
最后,updateNote()针对rowId、title和body,使用ContentValue实例修改对应rowId的便签。
第三步
打开res/layout目录下的notepad_list.xml文件,仔细看看。(为了浏览XML标记你可能必须点击XML便签页,在底部)
这是一个大部分空着的界面定义文件。下面是你应该知道的关于界面文件的一些事情:
*所有界面文件必须以作为第一行
*下一个定义通常但不总是某种界面元素定义,比如LinearLayout
*XML名称空间应该总是定义在XML文件中的顶级组件或者界面,因此文件的其余部分总是能看到android标签:
xmlns:android="http://schemas.android.com/apk/res/android"
界面与活动
多数活动类会有一个与之联系的界面。界面是活动的呈现给用户的脸面,这个例子中,我们的界面占据整个屏幕,显示一个便签列表。
但是,对于一个活动来讲,全屏幕界面不是唯一选项。你也可能想使用一个浮动的界面(例如对话框和警告提示框),或者也许你根本不需要一个界面(如果你不指定活动使用的某种界面,则活动将是用户不可见的)。
第四步
我们需要创建界面来容纳我们的列表。增加包含LinearLayout元素,整个文件看起来像这样:
android:layout_height="wrap_content">
android:layout_height="wrap_content"/>
android:layout_height="wrap_content"
android:text="@string/no_notes"/>
*ListView和TextView标签的id串中的@符号意思是:XML语法分析器应该解析和扩展id串的剩余部分,使用一个ID资源扩展。
*ListView和TextView可以作为两个可互相替换的视图,仅仅其中一个会被立即显示。
*通过Android平台列表和空的ID提供给我们,因此,我们必须使用android作为id的前缀。例如,@android:id/list
*当ListAdapter没有数据提供给ListView时,系统自动使用携带空id的视图。默认情况下,ListAdapter知道寻找这个名字。另外,你应该使用ListView的serEmptyView(VIew)方法来改变默认的空视图。
基本上说,android.R类是平台提供的一组预定义的资源,而你的项目的R类是你自己项目已经定义的一组资源。在android.R资源类找到的资源能被用于XML文件,通过使用android:名称空间前缀。
第五步
为了在ListView中创建便签列表,我们也需要为每一行定义一个VIew:
1、在res/layout目录下创建一个新文件,取名为notes_row.xml
2、增加如下的内容(注意:XML头部又被使用,第一个节点定义了android XML名称空间)
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
这是用于每个便签标题行的View——仅有一个文本字段。
在这个例子中,我们创建一个新的id,称为text1.@号后面的+表示如果id不存在那么自动作为资源创建,因此我们在text1,然后使用它。
3、保存文件
在项目中打开R.java类看看它,你将会看到新的定义:notes_row和text1,意味着我们现在能在代码中访问这些。
资源和R类
res/文件夹用于资源。res/下面有对于文件夹和文件的特别结构。
这些文件夹和文件中定义的资源将会对应R类中的条目,以便允许他们容易被访问和使用。R类由eclipse插件根据res/的内容自动产生(或者,在命令行工具中使用aapt命令)。作为应用程序的一部分,他们可展开和收集。
第六步
下面,在源代码中打开Noteadv1类。下面的步骤中,我们打算替换这个类,让其变成列表适配器,显示我们的便签,也会允许增加新的便签。
Notepadv1将会继承Activity的子类ListActivity,ListActivity有额外的功能以容纳多种可能加入的列表功能,例如:在每行的列表条目上显示任意的数字、移动列表条目、选择列表条目。
仔细查看Notepadv1类的已存在的代码。有一个还没有使用的私有字段叫做mNoteNumber,我们将会用来创建编号了的便签的标题。
还有三个重载方法:onCreate,OnCreateOptionsMenu和OnOptionsItemSelected,我们需要填充他们。
* onCreate()在活动启动的时候调用——有点像活动的“main”方法。运行时使用这个方法来设置资源和状态。
*onCreateOptionsMenu()用于增加活动的菜单。当用户点击菜单按钮时将会表现出来,有一个选项列表能选择。
*onOptionsItemSelected()是菜单等式的另外一半,用于处理菜单事件(例如,在用户选择“Create NOte”子菜单时)
第七步
修改Notepadv1的继承关系,从Activity改为ListActivity:
public class Notepadv1 extends ListActivity
注意:你必须使用Eclipse导入ListActivity到Notepadv1类中,Windows 和Linux的热键为ctrl-shift-O,Mac的热键cmd-shift-O。
第八步
填充onCreate()方法的代码。
这里我们会设置活动的标题(显示在屏幕的顶部),使用我们在XML文件中创建的notepad_list界面,设置NotesDbAdapter实例以便将来访问便签数据,使用可获取的便签标题来增加列表:
1、在onCreate方法中,携带savedInstanceState参数调用super()
2、调用setContentView(),传递R.layout.notepad_list
3、在类的顶部,创建一个NotesDbAdapter类的私有属性,称为mDbHelper
4、在onCreate方法中,构建一个新NotesDbAdapter实例,绑定到mDbHelper字段(传递给DBHelper的构造函数)
5、在mDbHelper上调用open()方法,以打开数据库
6、最后,调用新方法fillData(),作用是:获取数据,使用helper增加ListView——我们还没有定义这个方法
onCreate() 应该看起来这样:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.notepad_list);
mDbHelper = new NotesDbAdapter(this);
mDbHelper.open();
fillData();
}
并且保证你已经定义了mDbHelper域(正好在mNoteNumber下)
private NotesDbAdapter mDbHelper;
第九步
填写onCreateOptionsMenu()的代码。
现在我们会创建“Add Item”按钮,这个按钮能在设备上点击。我们会指定他在菜单上占据的位置.
1、strings.xml中增加一个名叫“menu_insert”的新字串,该字串的值为Add Item:
然后保存文件并返回到Notepadv1.
2、在类的顶部创建菜单位置常量
public static final int INSERT_ID = Menu.FIRST;
3、OnCreateOptionsMenu()中修改super的调用,因此我们捕获布尔变量作为结果返回。我们将会在最后返回这个值。
4、然后,使用menu.add()方法增加菜单。
整个方法应该看起来这样:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
boolean result = super.onCreateOptionsMenu(menu);
menu.add(0, INSERT_ID, 0, R.string.menu_insert);
return result;
}
传给add()的参数是:这个菜单的上级菜单标识(这个例子无),一个唯一ID(如上的定义),菜单项的顺序(o表示没有优先级),用于这个条目的资源串。
关于菜单的更多知识
我们构造的notepad程序仅仅接触了菜单的表面。
你还能“增加菜单项的快捷键”、“创建子菜单”,甚至增加菜单项到其他应用。
第十步
填充onOptionsItemSelected()方法的代码。
打算处理我们新的Add Note菜单项。当菜单被选择时,onOptionsItemSelected方法携带item.getId(),设置到INSERT_ID(这个常数我们用来识别菜单项)。我们能检测这个选择事件,作出适当的动作:
1、super.onOptionsItemSelected(item)方法的调用发生在这个方法的最后——我们想首先捕获我们的事件。
2、在item.getItemID()中写一个转换状态。
INSERT_ID的例子中,调用一个新的方法,createNote(),返回true,因为我们已经处理了这个时间,不想通过系统传递它。
3、最后,返回超类的onOptionsItemSelected方法的结果
onOptionsItemSelected方法应该看起来如下:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case INSERT_ID:
createNote();
return true;
}
return super.onOptionsItemSelected(item);
}
第11步
增加一个新的createNote方法:
我们的程序的第一个版本,createNote()不会很有用。我们将会简单地创建新的带有标题的便签,假定标题基于计数器、便签内容是空的。目前,我们无法编辑便签的内容,因此现在我们毫无疑问是设置默认值的内容:
1、使用Note和计数器来构造名称,我们在类中定义String noteName = "Note " + mNoteNumber++
2、调用mDbHelper.createNote(),使用noteName作为标题,内容就写为""
3、调用fillData(),组装便签列表(低效但是简单)——我们将会创建这个方法
createNote方法看起来如下:
private void createNote() {
String noteName = "Note " + mNoteNumber++;
mDbHelper.createNote(noteName, "");
fillData();
}
第12步
定义fillData方法:
这个方法使用SimpleCursorAdapter,SimpleCursorAdapter获取一个数据库游标,绑定到界面中提供的字段上。这个字段定义了我们列表的行元素(这个例子中,我们使用notes_row.xml界面的text1字段),因此允许我们轻易地使用数据库中的数据项来组装列表。
我们必须提供一个映射,从返回的游标的标题字段映射到我们的text1文本视图中,定义了两个数组:第一个,字串数组,映射到列(这个例子中,仅仅标题,来自常量NotesDbAdapter.KEY_TITLE),第二个,整型数组,包含到绑定了数据的视图(R.id.text1文本视图)的引用
这是一个较大块的代码,因此让我们首先看看:
private void fillData() {
// Get all of the notes from the database and create the item list
Cursor c = mDbHelper.fetchAllNotes();
startManagingCursor(c);
String[] from = new String[] { NotesDbAdapter.KEY_TITLE };
int[] to = new int[] { R.id.text1 };
// Now create an array adapter and set it to display using our row
SimpleCursorAdapter notes =
new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to);
setListAdapter(notes);
}
以下是我们已经做得事情:
1、当从mDbHelper.fetchAllNotes()获得游标后,我们使用一个称为startManagingCursor()的活动的方法,该方法允许Android照看游标的生命周期,而不需要我们担心她。(我们将会在练习3中牵涉生命周期,但是现在仅仅知道:这让Android为我们做一些资源管理的事情)
2、然后,我们创建一个字符串数组映射到标题,一个整型数组映射到视图
3、下一步是实例化SimpleCursorAdapter。象Android的许多类一样,SimpleCursorAdapter需要Context,因此,我们传入上下文环境参数(因为Activity的子类实现了Context)。我们传递notes_row视图(作为数据容器创建),游标(刚刚创建的),数组。
将来记住,从列到资源的映射用于决定两个数组的顺序。如果我们想绑定有更多的列,对应的有更多的视图绑定,那么我们会按顺序指定它们,例如:我们可能使用{ NotesDbAdapter.KEY_TITLE, NotesDbAdapter.KEY_BODY }和{ R.id.text1, R.id.text2 }来绑定两个字段进入记录行(而且,我们也会需要在notes_row.xml中定义text2,针对内容字符串)。这是绑定多个字段到单记录的方法(而且,也获得一个自定义的记录行的界面)。
If you get compiler errors about classes not being found, ctrl-shift-O or (cmd-shift-O on the mac) to organize imports.
如果你看到这个类的编译错误是没有找到,那么按ctrl-shift-O 来组织import。
第13步
运行你的程序
1、右击Notepadv1项目
2、从弹出菜单中选择Run As > Android Application.
3、如果你看到一个对话框出现,选择Android发射器作为运行程序的方法(你也能使用对话框顶部附近的链接,设置你的工作区的默认启动方式:这种方式是推荐的因为每次回询问你是否暂停插件)
4、通过点击menu按钮选择Add Item菜单,来增加新的便签
2008年12月12日星期五
教程:Notepad程序
教程:Notepad程序
本章的教程手把手介绍Android框架和工具,以便用之建立应用程序。从预先配置的项目文件开始,本教程引导你深入一下过程:简单的Notepad程序开发、提供设置项目的具体例子、开发程序逻辑和UI、编译运行程序。
教程作为一套练习介绍notepad程序的开发,每个联系由几个步骤组成。你可以按照每个联系中的步骤逐步建立和完善你的程序。这些联系详细解释每个步骤,并提供所有你需要的例子代码以完成程序。
当你完成教程时,你会成功创建正常运行的Android程序,并深入了解Android开发的更多重要概念。如果你想计入更多复杂功能到你的程序,你能通过notepad程序的可替换实现来检查代码,notepad程序在“ Sample Code”文档中提及。
谁应该使用这个教程
这个教程为有经验的开发人员设计,尤其是那些拥有JAVA编程知识的人。如果你没有编写过JAVA程序,你仍旧能使用这个教程,但是,你可能需要花费较多的时间,速度较慢。
这个教程假设你已经熟悉基本的Android程序概念和术语。如果你还不熟悉这些,你应该再继续以前阅读“Overview of an Android Application ”
也要注意:这个教程使用Eclipse开发环境,并安装有ADT插件。如果没有使用Eclipse,你能按照练习建立程序,但是你将会决定怎样在你的环境中完成和Eclipse相关的步骤。
准备练习
这个教程建立在“Installing the SDK ”、“Hello Android ”两篇文章的信息之上,这两篇文章详细解释了怎样设置你的开发环境。在你开始这个教程之前,你应该阅读这两篇文章,安装SDK,设置工作环境。
为了这个课程,准备如下:
1.下载项目练习数据包(zip)
2.解压到合适的位置
3.打开NotepadCodeLab文件夹
在NotepadCodeLab文件之内,你应该找到六个项目文件:Notepadv1, Notepadv2, Notepadv3, Notepadv1Solution, Notepadv2Solution 和Notepadv3Solution。Notepadv#项目是每个练习的起点,而Notepadv#Solutio项目是练习的解决方案。如果你在某个练习中有麻烦,你能通过比较你目前的工作和练习解决方案来解决。
练习
下表列举了教程练习,描述了每一条的发展领域。每个联系假定你已经所有前面的联系。
1、练习1
这里还是。构造简单的便签列表,让用户增加新的便签但是不能编辑。展示基本的ListActivity以及创建处理菜单选项。使用SQLite数据库存储便签。
2、练习2
增加第二个活动到程序中。展示构建新活动,增加活动到Android manifest文件,在两个活动之间传递数据,还有使用更高级的屏幕布局。还展示了怎么使用startActivityForResult()调用另外一个活动并返回一个结果.
3、练习3
增加生命周期事件处理到程序中,并让该事件维持生命周期内的程序状态。
4、练习4
展示怎样使用Eclipse调试器,怎样使用调试器来查看生命周期事件。本章是可选的但是强烈推荐。
其他资源以及进一步的学习
* 针对概念的更浅显更广泛的介绍并没有在教程中覆盖,请参考“Common Android Tasks”
* AndroidSDK包含各种各样具有完备功能的例子程序,有极好的机会进一步学习。你能在SDK的samples/目录下找到例子程序。
* 这个教程引入了完整Notepad应用程序,包含在SDK的samples/目录中,尽管它不能完全匹配。当你按照教程做的时候,强烈推荐你仔细看看Notepad程序的这个版本,它展示了多种有趣的补充,比如:
o 针对便签列表,设置一个自定义条纹列表
o 创建一个自定义文本视图,重载draw()方法以便使便签看起来像一个内忖的便签。
针对便签实现一个完整的ContentProvider。
o 还原、丢弃编辑,而不只是自动保存
2008年12月9日星期二
Android程序的解剖
Android程序的解剖
* Activity,即活动
* Broadcast Intent Receiver,即广播接收器
* Service,即服务
* Content Provider,即内容提供商
并不是每个程序都需要所有的这四个组件,但是你的程序将会使用这四个方面的某些部分组合而成。
一旦你已经决定你的应用程序需要的组件,你应该在AndroidManifest.xml文件中列举出来。AndroidManifest.xml是一个XML文件,你在其中声明你的程序的组件以及他们的能力和需求。请阅读“Android manifest file documentation”以了解更详细的资料。
Activity
活动(Activity)是Android生成块的最常用组件。一个活动通常是程序中的一个单一的屏幕。每个活动作为Activity基类的单个的继承类来实现。你的类将会显示一个UI,这个UI由Views(视图)以及对应的事件(event)组成。大多数程序由多个界面组成。例如,一个文本消息程序可能有一个展示发送消息目标的通讯录列表的界面,以及一个写消息到选中的通讯录的界面,还有查询旧消息或者修改设置的其他界面。这些界面的每一个都会被作为一个活动实现。移动到另外一个界面通过启动一个新的活动来完成。一些情况下,一个活动可能返回一个值给上一个活动——例如:一个让用户挑选图片的活动将会返回所选图片给呼叫者。
当一个新界面打开时,前一个界面暂停,放入一个历史堆栈中。用户能回退到历史堆栈中的前一个打开的界面。当不适合保留的时候,界面能从历史堆栈中选出来删除。Android为每个程序从主界面开始的所有界面保留历史堆栈。
Intent(计划)和计划过滤
Android使用一个特殊类调用Intent来从一个屏幕移动到另外一个界面。一个Intent描述了一个应用程序想做什么。intent数据结构的两个最重要部分是动作以及起作用的数据。动作的典型是MAIN(应用程序的入口)、VIEW、PICK、EDIT等。数据表达为一个URI。例如,为了浏览个人的通讯录信息,你会创建一个intent,该intent携带VIEW动作以及描述那个人的URI指向的数据。
有一个相关类称为IntentFilter。当意图(intent)是做什么事情的有效请求时,intent filter是说明活动(或者BroadcastReceiver)能处理的意图(intent),能显示个人通信录信息的活动将会发布一个IntentFilter,以说明活动知道当应用数据描述一个人时怎么处理动作VIEW。活动在AndroidManifest.xml文件中发布他们的IntentFilter。
从一个界面导航切换到另外一个界面是通过解析intent来完成的。为了向前导航,活动调用startActivity(myIntent)。然后系统搜索所有已安装程序的intent filter(意图过滤器),选择intent filter和myIntent最匹配的活动。新活动接到intent的通知,从而导致他启动。解析intent过程发生在运行时,此时startActivity被调用,提供了两个关键好处:
* 活动对于其他组件可以功能重用,只需简单的以Intent的形式发出请求。
* 活动能在任何时候被携带同等的IntentFilter的新活动替换。
广播接收器
当你想程序的代码用于外部事件的反馈时,你可以使用BroadcastReceiver,例如,电话铃声响时,或者,数据网络可获得时,或者午夜时分。BroadcstReceivers不显示UI,尽管他们可以使用NotificationManager来警告用户其感兴趣的事情是否已经发生。BroadcastReceivers在AndroidManifest.xml中注册,但是你也能使用Context.registerReceiver()从代码中注册。你的程序不一定在运行,因为BroadcastReceiver被调用;系统将会启动你的程序,如果必要的话,当BroadcastReceiver被触发的时候。程序也能通过Context.sendBroadcst()发送他们自己的intent广播到其他应用程序。
服务
服务是长期存活的没有UI运行的代码。好例子是从播放列表播放歌曲的媒体播放器。在媒体播放程序中,可能有一到多个活动供用户选择歌曲并开始播放。但是,音乐回放自己不应该被一个活动处理,因为用户希望音乐继续播放即使切换到一个新的界面。这种情况下,媒体播放器活动可使用Context.startService()启动一个后台运行的服务来保持音乐播放。然后,系统会继续运行音乐回放服务直到音乐播放完成。你可以通过阅读“Life Cycle of an Android Application”来了解系统服务的优先级。注意:你可以连接到服务(如果没有运行就启动它),使用方法Context.bindService()。当连接到服务时,你能通过服务暴露的接口和服务通信。对于音乐服务,这可能允许你暂停、重放等。
内容提供者
程序能保存数据到文件,一个SQLite数据库,或者,合理的其他任何机制。但是,内容提供者是有用的,如果你想你的程序的数据和其他程序共享。内容提供者是一个类,实现一个标准方法集,以便其他程序存取内容提供者能处理的的数据类型。
To get more details on content providers, see Accessing Content Providers.
为了了解内容提供者的详细情况,请参见“ Accessing Content Providers”
* Activity,即活动
* Broadcast Intent Receiver,即广播接收器
* Service,即服务
* Content Provider,即内容提供商
并不是每个程序都需要所有的这四个组件,但是你的程序将会使用这四个方面的某些部分组合而成。
一旦你已经决定你的应用程序需要的组件,你应该在AndroidManifest.xml文件中列举出来。AndroidManifest.xml是一个XML文件,你在其中声明你的程序的组件以及他们的能力和需求。请阅读“Android manifest file documentation”以了解更详细的资料。
Activity
活动(Activity)是Android生成块的最常用组件。一个活动通常是程序中的一个单一的屏幕。每个活动作为Activity基类的单个的继承类来实现。你的类将会显示一个UI,这个UI由Views(视图)以及对应的事件(event)组成。大多数程序由多个界面组成。例如,一个文本消息程序可能有一个展示发送消息目标的通讯录列表的界面,以及一个写消息到选中的通讯录的界面,还有查询旧消息或者修改设置的其他界面。这些界面的每一个都会被作为一个活动实现。移动到另外一个界面通过启动一个新的活动来完成。一些情况下,一个活动可能返回一个值给上一个活动——例如:一个让用户挑选图片的活动将会返回所选图片给呼叫者。
当一个新界面打开时,前一个界面暂停,放入一个历史堆栈中。用户能回退到历史堆栈中的前一个打开的界面。当不适合保留的时候,界面能从历史堆栈中选出来删除。Android为每个程序从主界面开始的所有界面保留历史堆栈。
Intent(计划)和计划过滤
Android使用一个特殊类调用Intent来从一个屏幕移动到另外一个界面。一个Intent描述了一个应用程序想做什么。intent数据结构的两个最重要部分是动作以及起作用的数据。动作的典型是MAIN(应用程序的入口)、VIEW、PICK、EDIT等。数据表达为一个URI。例如,为了浏览个人的通讯录信息,你会创建一个intent,该intent携带VIEW动作以及描述那个人的URI指向的数据。
有一个相关类称为IntentFilter。当意图(intent)是做什么事情的有效请求时,intent filter是说明活动(或者BroadcastReceiver)能处理的意图(intent),能显示个人通信录信息的活动将会发布一个IntentFilter,以说明活动知道当应用数据描述一个人时怎么处理动作VIEW。活动在AndroidManifest.xml文件中发布他们的IntentFilter。
从一个界面导航切换到另外一个界面是通过解析intent来完成的。为了向前导航,活动调用startActivity(myIntent)。然后系统搜索所有已安装程序的intent filter(意图过滤器),选择intent filter和myIntent最匹配的活动。新活动接到intent的通知,从而导致他启动。解析intent过程发生在运行时,此时startActivity被调用,提供了两个关键好处:
* 活动对于其他组件可以功能重用,只需简单的以Intent的形式发出请求。
* 活动能在任何时候被携带同等的IntentFilter的新活动替换。
广播接收器
当你想程序的代码用于外部事件的反馈时,你可以使用BroadcastReceiver,例如,电话铃声响时,或者,数据网络可获得时,或者午夜时分。BroadcstReceivers不显示UI,尽管他们可以使用NotificationManager来警告用户其感兴趣的事情是否已经发生。BroadcastReceivers在AndroidManifest.xml中注册,但是你也能使用Context.registerReceiver()从代码中注册。你的程序不一定在运行,因为BroadcastReceiver被调用;系统将会启动你的程序,如果必要的话,当BroadcastReceiver被触发的时候。程序也能通过Context.sendBroadcst()发送他们自己的intent广播到其他应用程序。
服务
服务是长期存活的没有UI运行的代码。好例子是从播放列表播放歌曲的媒体播放器。在媒体播放程序中,可能有一到多个活动供用户选择歌曲并开始播放。但是,音乐回放自己不应该被一个活动处理,因为用户希望音乐继续播放即使切换到一个新的界面。这种情况下,媒体播放器活动可使用Context.startService()启动一个后台运行的服务来保持音乐播放。然后,系统会继续运行音乐回放服务直到音乐播放完成。你可以通过阅读“Life Cycle of an Android Application”来了解系统服务的优先级。注意:你可以连接到服务(如果没有运行就启动它),使用方法Context.bindService()。当连接到服务时,你能通过服务暴露的接口和服务通信。对于音乐服务,这可能允许你暂停、重放等。
内容提供者
程序能保存数据到文件,一个SQLite数据库,或者,合理的其他任何机制。但是,内容提供者是有用的,如果你想你的程序的数据和其他程序共享。内容提供者是一个类,实现一个标准方法集,以便其他程序存取内容提供者能处理的的数据类型。
To get more details on content providers, see Accessing Content Providers.
为了了解内容提供者的详细情况,请参见“ Accessing Content Providers”
2008年12月3日星期三
升级UI到XML布局
升级UI到XML布局
你刚完成的“Hello,World”例子使用叫作“programmatic”(编码)UI布局,也就是你直接在源代码中构造和建立你的应用程序UI。如果你做过UI编程,你可能了解道路有时可能非常不容易的:布局小变动可能导致代码方面的大头痛。忘记恰当地融入VIew也是非常容易的,这将导致布局错误,并浪费时间来调试你的代码。
这就是Android提供另外一种UI构造模式:XML布局文件的原因。解释这种模式的最容易的方法是展示一个例子。下面是一个XML布局文件,其行为等同于你刚完成的基于编码构造的例子:
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="Hello, Android"/>
一个AndroidXML布局文件的普通结构是简单的。它是一棵标记树,每个标记是View类的名称。这个例子是包含一个元素,TextView,的非常简单的树。你可以使用在XML布局文件中View扩展类的名称,包括你在自己代码中定义的自定义View类。这种结构使快速建立UI变得非常容易,相比你在代码中使用的结构和语法,更简单。这种模式对web开发模式更有吸引力,你能隔离应用程序的表示层(UI)和逻辑层(存取数据)。
这个例子中,有四个XML属性。下面总结他们的意思:
(1)xmlns:android
这是一个XML名称空间声明,告诉Android工具:你打算引用在Android名称空间定义的通用属性。在每个Android布局文件中的外层标签必须有这个属性。
(2)android:layout_width
这个属性定义了View使用的屏幕的有效宽度是多少。这个例子中,他是唯一的VIew,所以我们想它占用整个屏幕,“fill_parent”就是这个意思。
(3)android:layout_height
这个属性很像android:layout_width,只是用于规定有效高度。
(4)android:text
这个属性设置TextView应该包含的文本内容。在这个例子中,他是我们司空见惯的"Hello, Android" 信息。
所以,那就是XML布局文件看起来的样子,但是你把它放在哪里呢?放到你的项目下/res/layout目录下。“res”是资源单词的缩写,这个目录存放你的程序需要的非代码资源,包括图片、本地化字符、XML布局文件。
ADT插件为你创建一个XML文件。在我们的上面的例子中,我们简直从来没有使用它。在Package浏览器总,展开文件夹/res/layout,编辑文件main.xml,使用上面的文字提供main.xml的内容然后保存。
马上打开文件r.java,这个文件在Package浏览器的源代码文件夹中。你将会发现它现在看起来像这样:
public final class R {
public static final class attr {
};
public static final class drawable {
public static final int icon=0x7f020000;
};
public static final class layout {
public static final int main=0x7f030000;
};
public static final class string {
public static final int app_name=0x7f040000;
};
};
项目的R.java文件是一个指向文件中定义的所有资源的索引。你在你的源代码中使用这个类,作为一种快捷引用你项目中的资源的方式。这是特别强大的代码完成功能,该功能存在于诸如Eclipse的集成开发环境中,之所以强大是因为他让你快速地、互动地定位你正在寻找的具体引用。
现在要注意的重要事情是名叫“layout”的内嵌类,以及他的成员“main”。ADT插件注意到你增加了一个新的XML布局文件,然后产生了这个r.java文件。当你加入其他资源进入你的项目时,你会发现r.java会跟着改变。
你需要做的最后的事情是使用你的UI的新XML版本而不是硬编码版本来修改HelloAndroid源代码。下面是你的新类看起来的样子,正如你看到的一样,源代码变得非常简单:
package com.android.hello;
import android.app.Activity;
import android.os.Bundle;
public class HelloAndroid extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
当你做这个修改的时候,不要仅仅拷贝粘贴进入。在那个R类上试试代码完成功能。你可能会发现它会帮助你许多。
既然你已经做了修改,进一步重新运行你的程序,你需要做的所有事情是点击绿色的Run箭头图标,或者选择菜单Run > Run History > Hello, Android。你应该看到...好,和你以前看到的是完全相同的界面!毕竟,这一点是要表明,两种不同的布局方式产生了相同的结果。
创建XML布局文件有更多的知识,但是我们在这里只介绍这么多了。想要知道这种强力方式的更多信息请参见“Implementing a User Interface”。
调试你的工程项目
ADT插件也和Eclipse调试器进行了完美集成。为了证明这点,让我们介绍我们代码的一个BUG。像这样修改你的代码:
package com.android.hello;
import android.app.Activity;
import android.os.Bundle;
public class HelloAndroid extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Object o = null;
o.toString();
setContentView(R.layout.main);
}
}
这个修改引入了一个NullPointerException异常。如果你再次运行你的程序,你最后会在模拟器上看到:
按"Force Quit"来终止应用程序,并关闭模拟器窗口。
为了找出更多的错误,在Object o = null;行上设置断点,设置断点的操作是双击紧靠源代码行的标记栏。从主菜单中选择菜单Run > Debug History > Hello, Android进入调试模式。你的应用程序会在模拟器中重新启动,但是这次她会挂起在你设置的断点处。你能在Eclipse调试视图中单步调试你的代码,正如你为其他任何程序做的事情一样。
没有Eclipse时创建工程项目
如果你没有使用Eclipse,比如如果你喜欢其他IDE,或者简单地使用文本编辑器和命令行工具,那么Eclipse插件不能帮助你。但是,不要太担心,你不会因为不使用Eclipse而损失任何功能。
Android插件实际只是对包含在AndroidSDK内的一套工具的封装,这套工具包括模拟器、AAPT、ADB、DDMS,以及其他在别的地方提到的工具。因此,使用诸如ant建立文件之类的别的工具封装这些工具是可能的。
AndroidSDK包含一个称为“activitycreator.py”的Python描述脚本,这个脚本用于为你的工程项目创建所有的源代码和目录桩,又兼容ant的build.xml。这允许你从命令行建立你的工程项目,或者和你选择的IDE集成。
例如,为了创建一个HelloAndroid项目,和你在Eclipse中创建的一样,你使用如下的命令:
activitycreator.py --out HelloAndroid com.android.hello.HelloAndroid
为了建立项目工程,你运行命令“ant”。当命令成功完成时,你会放置一个文件helloAndroid.apk到bin目录下。.apk文件是一个Android包,能使用adb工具在模拟器中安装和运行。
欲知更多信息,请阅读上面引用的文档。
你刚完成的“Hello,World”例子使用叫作“programmatic”(编码)UI布局,也就是你直接在源代码中构造和建立你的应用程序UI。如果你做过UI编程,你可能了解道路有时可能非常不容易的:布局小变动可能导致代码方面的大头痛。忘记恰当地融入VIew也是非常容易的,这将导致布局错误,并浪费时间来调试你的代码。
这就是Android提供另外一种UI构造模式:XML布局文件的原因。解释这种模式的最容易的方法是展示一个例子。下面是一个XML布局文件,其行为等同于你刚完成的基于编码构造的例子:
android:layout_height="fill_parent"
android:text="Hello, Android"/>
一个AndroidXML布局文件的普通结构是简单的。它是一棵标记树,每个标记是View类的名称。这个例子是包含一个元素,TextView,的非常简单的树。你可以使用在XML布局文件中View扩展类的名称,包括你在自己代码中定义的自定义View类。这种结构使快速建立UI变得非常容易,相比你在代码中使用的结构和语法,更简单。这种模式对web开发模式更有吸引力,你能隔离应用程序的表示层(UI)和逻辑层(存取数据)。
这个例子中,有四个XML属性。下面总结他们的意思:
(1)xmlns:android
这是一个XML名称空间声明,告诉Android工具:你打算引用在Android名称空间定义的通用属性。在每个Android布局文件中的外层标签必须有这个属性。
(2)android:layout_width
这个属性定义了View使用的屏幕的有效宽度是多少。这个例子中,他是唯一的VIew,所以我们想它占用整个屏幕,“fill_parent”就是这个意思。
(3)android:layout_height
这个属性很像android:layout_width,只是用于规定有效高度。
(4)android:text
这个属性设置TextView应该包含的文本内容。在这个例子中,他是我们司空见惯的"Hello, Android" 信息。
所以,那就是XML布局文件看起来的样子,但是你把它放在哪里呢?放到你的项目下/res/layout目录下。“res”是资源单词的缩写,这个目录存放你的程序需要的非代码资源,包括图片、本地化字符、XML布局文件。
ADT插件为你创建一个XML文件。在我们的上面的例子中,我们简直从来没有使用它。在Package浏览器总,展开文件夹/res/layout,编辑文件main.xml,使用上面的文字提供main.xml的内容然后保存。
马上打开文件r.java,这个文件在Package浏览器的源代码文件夹中。你将会发现它现在看起来像这样:
public final class R {
public static final class attr {
};
public static final class drawable {
public static final int icon=0x7f020000;
};
public static final class layout {
public static final int main=0x7f030000;
};
public static final class string {
public static final int app_name=0x7f040000;
};
};
项目的R.java文件是一个指向文件中定义的所有资源的索引。你在你的源代码中使用这个类,作为一种快捷引用你项目中的资源的方式。这是特别强大的代码完成功能,该功能存在于诸如Eclipse的集成开发环境中,之所以强大是因为他让你快速地、互动地定位你正在寻找的具体引用。
现在要注意的重要事情是名叫“layout”的内嵌类,以及他的成员“main”。ADT插件注意到你增加了一个新的XML布局文件,然后产生了这个r.java文件。当你加入其他资源进入你的项目时,你会发现r.java会跟着改变。
你需要做的最后的事情是使用你的UI的新XML版本而不是硬编码版本来修改HelloAndroid源代码。下面是你的新类看起来的样子,正如你看到的一样,源代码变得非常简单:
package com.android.hello;
import android.app.Activity;
import android.os.Bundle;
public class HelloAndroid extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
当你做这个修改的时候,不要仅仅拷贝粘贴进入。在那个R类上试试代码完成功能。你可能会发现它会帮助你许多。
既然你已经做了修改,进一步重新运行你的程序,你需要做的所有事情是点击绿色的Run箭头图标,或者选择菜单Run > Run History > Hello, Android。你应该看到...好,和你以前看到的是完全相同的界面!毕竟,这一点是要表明,两种不同的布局方式产生了相同的结果。
创建XML布局文件有更多的知识,但是我们在这里只介绍这么多了。想要知道这种强力方式的更多信息请参见“Implementing a User Interface”。
调试你的工程项目
ADT插件也和Eclipse调试器进行了完美集成。为了证明这点,让我们介绍我们代码的一个BUG。像这样修改你的代码:
package com.android.hello;
import android.app.Activity;
import android.os.Bundle;
public class HelloAndroid extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Object o = null;
o.toString();
setContentView(R.layout.main);
}
}
这个修改引入了一个NullPointerException异常。如果你再次运行你的程序,你最后会在模拟器上看到:
按"Force Quit"来终止应用程序,并关闭模拟器窗口。
为了找出更多的错误,在Object o = null;行上设置断点,设置断点的操作是双击紧靠源代码行的标记栏。从主菜单中选择菜单Run > Debug History > Hello, Android进入调试模式。你的应用程序会在模拟器中重新启动,但是这次她会挂起在你设置的断点处。你能在Eclipse调试视图中单步调试你的代码,正如你为其他任何程序做的事情一样。
没有Eclipse时创建工程项目
如果你没有使用Eclipse,比如如果你喜欢其他IDE,或者简单地使用文本编辑器和命令行工具,那么Eclipse插件不能帮助你。但是,不要太担心,你不会因为不使用Eclipse而损失任何功能。
Android插件实际只是对包含在AndroidSDK内的一套工具的封装,这套工具包括模拟器、AAPT、ADB、DDMS,以及其他在别的地方提到的工具。因此,使用诸如ant建立文件之类的别的工具封装这些工具是可能的。
AndroidSDK包含一个称为“activitycreator.py”的Python描述脚本,这个脚本用于为你的工程项目创建所有的源代码和目录桩,又兼容ant的build.xml。这允许你从命令行建立你的工程项目,或者和你选择的IDE集成。
例如,为了创建一个HelloAndroid项目,和你在Eclipse中创建的一样,你使用如下的命令:
activitycreator.py --out HelloAndroid com.android.hello.HelloAndroid
为了建立项目工程,你运行命令“ant”。当命令成功完成时,你会放置一个文件helloAndroid.apk到bin目录下。.apk文件是一个Android包,能使用adb工具在模拟器中安装和运行。
欲知更多信息,请阅读上面引用的文档。
2008年11月26日星期三
转:从《西游记》和《魔戒》看东西方项目管理
打开电视,电视里正在播放《魔戒第一部.护戒使者》。面对墨多的黑君主索伦无以伦比的可怕力量,一个义无反顾的护戒小队成立了,他们的目标就是墨多的核心地带——末日火山。这看起来就像是一个不可能完成的任务,一开始就面临着难以逾越的劫难。这让我想起了我们的神怪小说《西游记》,同样是一个团队,同样是面临千难万险的征途,但是,中西方文化的差别却在这里表现得淋漓尽致。
首先,我们看看这两个团队的成立
“你挑着担,我牵着马,迎来旭日送走晚霞。。。。”这首歌好像描绘了一个非常和谐的,分工明确、责任清晰的最佳合作团队。但是,这个团队起初只有唐僧一个人,只有唐僧才真正明白此行的目的,一行四人中最坚决的取经者仅此一人而已。其他几位包括白龙马、孙悟空、猪八戒、沙和尚等基本上都是连哄带骗外加紧箍咒强行拉壮丁拉进来的,这几个人各打各的主义,各有各的心思,可以这样说:他们怀着不同的目的,为了同一个目标,走到了一起。
而护戒使者小分队的成立,则完全是另外一个样子。首先在精灵国的林谷会议上,大家对面临的问题非常清晰,索伦要找回魔戒,得到魔戒的索伦将成为不可战胜的力量。那样的话,人类、精灵、矮人、侏儒等一切爱好和平的种族将会面临灭顶之灾,解决问题的唯一办法就是将魔戒带到墨多的核心末日火山,然后将魔戒丢到火山中将其毁灭。但是,达到这个目的的困难是完全不可想象的,其难度完全不亚于去西天取经。这时候,英勇无畏的勇士们站了出来。游侠阿拉贡、战士博罗米尔、精灵莱格拉斯、矮人金利、巫师甘道夫以及几个以弗罗多为首的霍比特人,九个人的护戒使者可以说目的非常明确(皮平除外),而且完全是自发的主动的建立起这个团队。这个团队没有明确的队长,只有带戒人和护戒使者,遇到事情基本上采取协商解决。
从团队建设来看,东方文化带有明显的阶级因素。最高阶级是神,如来佛、观音菩萨、玉皇大帝等,神是法力无边的,与神斗,绝没有好下场,齐天大圣够厉害了吧?最后还不是逃不出如来佛的手心,如来佛一翻手掌就将他压在五界山下永世不得脱身。第二个阶级是师傅唐僧,唐僧作为神的代言人,具有管辖和掌握手下四个徒弟的特权,他忠实地行使神赋予他的权力和义务。但是神给他的权力是明确的,有限的,他只能管理这几个徒儿,除此之外,即便是面对狐狸兔子等最下等的小妖,他也无能为力。再下面是三个人形的徒弟,尽管他们个个长的实在没个人样,但是与连人形也没有修得的白龙马相比,他们显然要高一个阶级。在这个第三阶级里面,每个人的地位也是不平等的,因为唐僧给他们排了座次,大师兄悟空,二师兄八戒,三师弟沙僧,人人都有自己的位置安排,遇到事情大家依据自己的位置发表议论。如果谁违背了这个顺序,后果将不堪设想。唐僧曾经听信了八戒的谗言,忽视了大师兄悟空的权利,孙悟空越想越气,最后一怒之下撂挑子不干了。可见,在西游团队中,需要论资排辈,每个人既定的地位、座次都是非常重要、不可动摇地。最后一个阶级就是大家的奴隶——白龙马。白龙马说起来也是龙的传人,可惜他被神定位为马,从此只能负重行走,没有说话的权利,连吃饭也没份,是一个活脱脱的奴隶。
而西方文化,更看重个人的尊严。在护戒使者这个团队中,每个人都有他存在的价值,他们的地位是平等的。开始时,山姆是弗罗多的仆人,但是弗罗多以及其他小组成员从没有把他当奴隶看,最后,随着大家一起克服重重困难,弗罗多完全把山姆当成了自己的亲兄弟。因为没有不可逾越的阶级,人与神的界限就没有那么明显,黑君主索伦是强悍无比的黑暗势力,但是,只要你有足够的勇气,即便你是一个普通人,一样有可能打败他。
其次,我们来看一看两个团队如何处理所面对的困难
西游团队碰到困难时,师傅是不出面的。就像大话西游里唱得那样:“背黑锅我来,送死你去!”唐僧作为领导,当然是养尊处优的,碰到难处理的事情,你们三个去想办法。这时候,这三个人的责任便很明确,大师兄地位高,自然承担的责任也最大,降妖除怪一般都是大师兄责无旁贷。孙悟空也从来不推托,总是第一个冲了上去,这时候东方文化的良好秩序发挥了效率。由于地位清晰,责任明确,大家该干什么干什么,碰到妖怪,唐僧只要说一句:“悟空,你去看看!”接下来降妖机器便自动运转起来,来一个妖怪是这个打法,来一百个妖怪,也是这个打法。每个人对自己的工作都驾轻就熟,孙悟空自然是打妖的主力,八戒主要保护师傅,有时候是机动力量,由于能力有限,常常充当第二梯队打打二流小妖;沙僧地位最低,责任也最小,只要看好行李物品不要丢失就行了。白龙马是奴隶,没有地位,自然也没有责任,打不打妖和他没有任何关系,谁也不会把责任推到他的头上。
而护戒使者队伍因为大家地位相同,因此碰到怪物的时候,只要喊一声:“大家并肩子上啊!”便人不分老幼,地不分南北一拥而上,打得个不亦乐乎。虽然大家的能力是不一样的,但是杀敌的勇气却是一样的,面对蜂拥而至的妖怪,谁也不能够独善其身,只能拿起武器战斗。这种同进同退的战斗生活,能够加深团队成员之间的友谊,实际上这个团队最终大家都成了生死兄弟。一个兄弟般的团队,其战斗力自然会更强,正所谓:“兄弟同心,其利断金!”正是这个互相关心,共同努力的团队,面对一次次令人绝望的危险,都能够涉险过关。
这两种不同的态度,所造成的后果也是各有千秋。东方人讲秩序,讲责、权、利分工明确,“各人自扫门前雪,莫管他人瓦上霜”这句话便是东方人处世态度的最好写照。作为团队的管理者,最核心的问题就是赏罚分明,只有纪律严明赏罚分明的队伍才是一个有能力的团队。而西方人鼓动人的主动性、积极发挥个人的创造力,要求团队更主动,更合作,团队管理的核心是激发每个人的主观能动性与创造力。
第三,我们再看看两个团队处理问题的方式
西游团队碰到危险,总是按照一定的思维方式来处理。首先,孙悟空会看到一股妖气,或者孙悟空的火眼金睛看出对方是妖怪,然后孙悟空先上去斗法。如果斗法成功,则为民除害,大家继续上路。如果斗法不成功,十有八九唐僧师徒被捉,这时候悟空总是有法子脱身,然后便会去找更厉害的帮手。通常帮手都是是天上位列仙班的大罗神仙,或者观世音菩萨的同道中人。而那些妖怪十有八九都是这些人的家眷、门人或者家禽家畜等等,因为有了这一层关系,便下界为祸人间。对待这些妖怪,孙悟空是搞不定的,因为他们出身高贵,必须找到 他们的上级才能够把问题解决。
西游记的九九八十一难,十有八九是因为高层领导的裙带关系,家属腐败等等原因造成的。所以,尽管取经路上危险重重,但是大家心里并不担心,为什么?因为一切尽在掌握中。所有的磨难或者危险,都是有因有果,都逃不出这四大阶级的天罗地网。只要这个阶级秩序能 够保持稳定,一切困难都会迎刃而解。
因此,西游团队面对问题的时候,他们首先回来分辨这个妖怪到底是什么来历?也就是说,妖怪的出身很重要,知道了妖怪的出身,问题也就解决了一大半了,接下来无非是怎样起出妖怪背后的最大靠山而已。这也符合东方文化的谋定而后动的操作要领,最后收拾妖怪,不过是整个行为的收关之作,整个降妖过程,很多工作都在幕后进行。所谓“功夫在诗外”,诗写道好的关键是你能够有足够的素材。通过自己的人情脉络找出那个真正的谋后主使,才是孙悟空能够降妖除魔的看家本领。
护戒使者所碰到的磨难,往往是突发的,没有任何征兆的,也绝不可能通过外援来调解的。他们没有一个掌控全局的神来主持大局,有的只是正义和邪恶两个完全不可调和的对立面,要么杀死敌人,要么被敌人杀死。水怪、炎魔、布鲁克武士、狼骑士,所有的敌人都是呼啸而来。应付的办法也是出奇的简单,拿起武器拼个你死我活。西方文化当中的正义与邪恶观点相对来说要幼稚的多,与西游团队不同,护戒使者面对敌人时,采取最直接也最有效的解决办法。在这种情况下,一切的运筹帷幄、一切的人情世故都是没用的,取胜只能靠自己的 力量、勇气、信心和敏捷的反应能力。
在应急处理问题的方式上,东西方文化截然不同的反应,给我们很大的启示。面对困难,西方人总是鼓励你主动地、勇敢地去尝试,或许你的方法完全不对,但是只要你有勇气尝试去解决问题,你就会得到尊重。西方文化更看重你解决问题的态度。与此相反,东方人在面临巨大困难的时候,他们首先考虑的是如何解决,或者如何选择最恰当的方式来解决问题。而不是贸然动手,如果你贸然动手,一旦你失败了,可能会招致大量的批评。
这种解决问题的思维方式,已经深深的烙在每一个人的心里。我们在教育方面,会花费大量的精力来告诉学生事物的原理,我们有十万个为什么?我们有格物致知的祖训,我们每个人都在探索事情的规律和论证最佳的解决方案。我们唯独忘了鼓励小朋友大胆去尝试自己的想法;我们总是告诉小朋友正确的解决方法,但是却忘了告诉小朋友如何通过自己的尝试获得正确地解决方案。授人以鱼不如授人以渔,很多人已经发现了我们教育方面的问题,但是扭转局面谈何容易,因为我们的文化根源与西方文化是那么的格格不入。我们的高考制度,考的是你能否给出问题的正确答案,而不是你能否以正确的方式来回答问题。高考制度不改变,模仿西方的所谓素质教育将永远是一句空话。
扯远了,我想如果西游一行碰到得九九八十一难都是像护戒使者面对的那样的敌人的话,唐僧可能已经死了七八十回了。但是,你放心,东方的妖怪也是东方化的。当他们捉住了唐僧,通常都不会便吃,而是要弄清楚齐天大圣走了没有?危险过去了没有?有的妖怪更是可爱的很,他们会很有孝心的邀请自己的长辈来共食,这样的妖怪自然给悟空留下了足够做好后台工作的时间,足以让你找到解决问题的法门。东方的妖怪还是比较仁义的,抓住唐僧后会对悟空说:不是我没给你机会阿,你要抓紧时间来救你师傅啊,你不来救可就莫怪我嘴下不 留情了。
第四,两个故事的结局
西游的结局大家都知道,五个人取经成功修的正果,都脱离苦海成了佛。而护戒使者一行人,除了为此事业献身的波罗米尔,其他人奋斗的结果便是成功阻止了索伦的强大,其结局非常简单。
从两个故事的结局可以清楚地看到,东方人功利思想要重得多,尽管当初一行人各怀鬼胎走上去西天取经之路。但是,当取经成功之后,孙悟空也老是不客气地做起了佛爷,那花果山的猴头看样子到底没有做佛爷来的风光啊。
但是在西方人眼里,成功就是任务的完成,对成功者来说,这件事情本身就够他自豪一辈子了,其他虚名则可有可无。
近年来,西方拍了很多的灾难片,有火山爆发的,有龙卷风的,有地震海啸的,有暴风雪的。在这些片子里,主人公无一例外的尝试去对抗大自然的力量。人类还没有取得应付自然灾害的能力,这时候面对它和解决它的勇气非常重要。东西方文化各有所长,但是,在我们这一代以及我们的下一代,在面对突发事件的情况下,我们的应变能力显然已经落于人后。
从西游记与魔戒这两部神怪小说分析,东西方文化有他根源上的巨大差异。东方文化胜在秩序的稳定和对事物原理孜孜不倦的追求上。而西方文化在人性的尊重,个性化创造力的培养,以及鼓励主动尝试等方面略高一筹。二十一世纪是全球一体化时代,东西方文化的融合将成为大家关心的主题。为了我们的下一代,西方一些优秀的教育理念是值得我们研究和学习的。
首先,我们看看这两个团队的成立
“你挑着担,我牵着马,迎来旭日送走晚霞。。。。”这首歌好像描绘了一个非常和谐的,分工明确、责任清晰的最佳合作团队。但是,这个团队起初只有唐僧一个人,只有唐僧才真正明白此行的目的,一行四人中最坚决的取经者仅此一人而已。其他几位包括白龙马、孙悟空、猪八戒、沙和尚等基本上都是连哄带骗外加紧箍咒强行拉壮丁拉进来的,这几个人各打各的主义,各有各的心思,可以这样说:他们怀着不同的目的,为了同一个目标,走到了一起。
而护戒使者小分队的成立,则完全是另外一个样子。首先在精灵国的林谷会议上,大家对面临的问题非常清晰,索伦要找回魔戒,得到魔戒的索伦将成为不可战胜的力量。那样的话,人类、精灵、矮人、侏儒等一切爱好和平的种族将会面临灭顶之灾,解决问题的唯一办法就是将魔戒带到墨多的核心末日火山,然后将魔戒丢到火山中将其毁灭。但是,达到这个目的的困难是完全不可想象的,其难度完全不亚于去西天取经。这时候,英勇无畏的勇士们站了出来。游侠阿拉贡、战士博罗米尔、精灵莱格拉斯、矮人金利、巫师甘道夫以及几个以弗罗多为首的霍比特人,九个人的护戒使者可以说目的非常明确(皮平除外),而且完全是自发的主动的建立起这个团队。这个团队没有明确的队长,只有带戒人和护戒使者,遇到事情基本上采取协商解决。
从团队建设来看,东方文化带有明显的阶级因素。最高阶级是神,如来佛、观音菩萨、玉皇大帝等,神是法力无边的,与神斗,绝没有好下场,齐天大圣够厉害了吧?最后还不是逃不出如来佛的手心,如来佛一翻手掌就将他压在五界山下永世不得脱身。第二个阶级是师傅唐僧,唐僧作为神的代言人,具有管辖和掌握手下四个徒弟的特权,他忠实地行使神赋予他的权力和义务。但是神给他的权力是明确的,有限的,他只能管理这几个徒儿,除此之外,即便是面对狐狸兔子等最下等的小妖,他也无能为力。再下面是三个人形的徒弟,尽管他们个个长的实在没个人样,但是与连人形也没有修得的白龙马相比,他们显然要高一个阶级。在这个第三阶级里面,每个人的地位也是不平等的,因为唐僧给他们排了座次,大师兄悟空,二师兄八戒,三师弟沙僧,人人都有自己的位置安排,遇到事情大家依据自己的位置发表议论。如果谁违背了这个顺序,后果将不堪设想。唐僧曾经听信了八戒的谗言,忽视了大师兄悟空的权利,孙悟空越想越气,最后一怒之下撂挑子不干了。可见,在西游团队中,需要论资排辈,每个人既定的地位、座次都是非常重要、不可动摇地。最后一个阶级就是大家的奴隶——白龙马。白龙马说起来也是龙的传人,可惜他被神定位为马,从此只能负重行走,没有说话的权利,连吃饭也没份,是一个活脱脱的奴隶。
而西方文化,更看重个人的尊严。在护戒使者这个团队中,每个人都有他存在的价值,他们的地位是平等的。开始时,山姆是弗罗多的仆人,但是弗罗多以及其他小组成员从没有把他当奴隶看,最后,随着大家一起克服重重困难,弗罗多完全把山姆当成了自己的亲兄弟。因为没有不可逾越的阶级,人与神的界限就没有那么明显,黑君主索伦是强悍无比的黑暗势力,但是,只要你有足够的勇气,即便你是一个普通人,一样有可能打败他。
其次,我们来看一看两个团队如何处理所面对的困难
西游团队碰到困难时,师傅是不出面的。就像大话西游里唱得那样:“背黑锅我来,送死你去!”唐僧作为领导,当然是养尊处优的,碰到难处理的事情,你们三个去想办法。这时候,这三个人的责任便很明确,大师兄地位高,自然承担的责任也最大,降妖除怪一般都是大师兄责无旁贷。孙悟空也从来不推托,总是第一个冲了上去,这时候东方文化的良好秩序发挥了效率。由于地位清晰,责任明确,大家该干什么干什么,碰到妖怪,唐僧只要说一句:“悟空,你去看看!”接下来降妖机器便自动运转起来,来一个妖怪是这个打法,来一百个妖怪,也是这个打法。每个人对自己的工作都驾轻就熟,孙悟空自然是打妖的主力,八戒主要保护师傅,有时候是机动力量,由于能力有限,常常充当第二梯队打打二流小妖;沙僧地位最低,责任也最小,只要看好行李物品不要丢失就行了。白龙马是奴隶,没有地位,自然也没有责任,打不打妖和他没有任何关系,谁也不会把责任推到他的头上。
而护戒使者队伍因为大家地位相同,因此碰到怪物的时候,只要喊一声:“大家并肩子上啊!”便人不分老幼,地不分南北一拥而上,打得个不亦乐乎。虽然大家的能力是不一样的,但是杀敌的勇气却是一样的,面对蜂拥而至的妖怪,谁也不能够独善其身,只能拿起武器战斗。这种同进同退的战斗生活,能够加深团队成员之间的友谊,实际上这个团队最终大家都成了生死兄弟。一个兄弟般的团队,其战斗力自然会更强,正所谓:“兄弟同心,其利断金!”正是这个互相关心,共同努力的团队,面对一次次令人绝望的危险,都能够涉险过关。
这两种不同的态度,所造成的后果也是各有千秋。东方人讲秩序,讲责、权、利分工明确,“各人自扫门前雪,莫管他人瓦上霜”这句话便是东方人处世态度的最好写照。作为团队的管理者,最核心的问题就是赏罚分明,只有纪律严明赏罚分明的队伍才是一个有能力的团队。而西方人鼓动人的主动性、积极发挥个人的创造力,要求团队更主动,更合作,团队管理的核心是激发每个人的主观能动性与创造力。
第三,我们再看看两个团队处理问题的方式
西游团队碰到危险,总是按照一定的思维方式来处理。首先,孙悟空会看到一股妖气,或者孙悟空的火眼金睛看出对方是妖怪,然后孙悟空先上去斗法。如果斗法成功,则为民除害,大家继续上路。如果斗法不成功,十有八九唐僧师徒被捉,这时候悟空总是有法子脱身,然后便会去找更厉害的帮手。通常帮手都是是天上位列仙班的大罗神仙,或者观世音菩萨的同道中人。而那些妖怪十有八九都是这些人的家眷、门人或者家禽家畜等等,因为有了这一层关系,便下界为祸人间。对待这些妖怪,孙悟空是搞不定的,因为他们出身高贵,必须找到 他们的上级才能够把问题解决。
西游记的九九八十一难,十有八九是因为高层领导的裙带关系,家属腐败等等原因造成的。所以,尽管取经路上危险重重,但是大家心里并不担心,为什么?因为一切尽在掌握中。所有的磨难或者危险,都是有因有果,都逃不出这四大阶级的天罗地网。只要这个阶级秩序能 够保持稳定,一切困难都会迎刃而解。
因此,西游团队面对问题的时候,他们首先回来分辨这个妖怪到底是什么来历?也就是说,妖怪的出身很重要,知道了妖怪的出身,问题也就解决了一大半了,接下来无非是怎样起出妖怪背后的最大靠山而已。这也符合东方文化的谋定而后动的操作要领,最后收拾妖怪,不过是整个行为的收关之作,整个降妖过程,很多工作都在幕后进行。所谓“功夫在诗外”,诗写道好的关键是你能够有足够的素材。通过自己的人情脉络找出那个真正的谋后主使,才是孙悟空能够降妖除魔的看家本领。
护戒使者所碰到的磨难,往往是突发的,没有任何征兆的,也绝不可能通过外援来调解的。他们没有一个掌控全局的神来主持大局,有的只是正义和邪恶两个完全不可调和的对立面,要么杀死敌人,要么被敌人杀死。水怪、炎魔、布鲁克武士、狼骑士,所有的敌人都是呼啸而来。应付的办法也是出奇的简单,拿起武器拼个你死我活。西方文化当中的正义与邪恶观点相对来说要幼稚的多,与西游团队不同,护戒使者面对敌人时,采取最直接也最有效的解决办法。在这种情况下,一切的运筹帷幄、一切的人情世故都是没用的,取胜只能靠自己的 力量、勇气、信心和敏捷的反应能力。
在应急处理问题的方式上,东西方文化截然不同的反应,给我们很大的启示。面对困难,西方人总是鼓励你主动地、勇敢地去尝试,或许你的方法完全不对,但是只要你有勇气尝试去解决问题,你就会得到尊重。西方文化更看重你解决问题的态度。与此相反,东方人在面临巨大困难的时候,他们首先考虑的是如何解决,或者如何选择最恰当的方式来解决问题。而不是贸然动手,如果你贸然动手,一旦你失败了,可能会招致大量的批评。
这种解决问题的思维方式,已经深深的烙在每一个人的心里。我们在教育方面,会花费大量的精力来告诉学生事物的原理,我们有十万个为什么?我们有格物致知的祖训,我们每个人都在探索事情的规律和论证最佳的解决方案。我们唯独忘了鼓励小朋友大胆去尝试自己的想法;我们总是告诉小朋友正确的解决方法,但是却忘了告诉小朋友如何通过自己的尝试获得正确地解决方案。授人以鱼不如授人以渔,很多人已经发现了我们教育方面的问题,但是扭转局面谈何容易,因为我们的文化根源与西方文化是那么的格格不入。我们的高考制度,考的是你能否给出问题的正确答案,而不是你能否以正确的方式来回答问题。高考制度不改变,模仿西方的所谓素质教育将永远是一句空话。
扯远了,我想如果西游一行碰到得九九八十一难都是像护戒使者面对的那样的敌人的话,唐僧可能已经死了七八十回了。但是,你放心,东方的妖怪也是东方化的。当他们捉住了唐僧,通常都不会便吃,而是要弄清楚齐天大圣走了没有?危险过去了没有?有的妖怪更是可爱的很,他们会很有孝心的邀请自己的长辈来共食,这样的妖怪自然给悟空留下了足够做好后台工作的时间,足以让你找到解决问题的法门。东方的妖怪还是比较仁义的,抓住唐僧后会对悟空说:不是我没给你机会阿,你要抓紧时间来救你师傅啊,你不来救可就莫怪我嘴下不 留情了。
第四,两个故事的结局
西游的结局大家都知道,五个人取经成功修的正果,都脱离苦海成了佛。而护戒使者一行人,除了为此事业献身的波罗米尔,其他人奋斗的结果便是成功阻止了索伦的强大,其结局非常简单。
从两个故事的结局可以清楚地看到,东方人功利思想要重得多,尽管当初一行人各怀鬼胎走上去西天取经之路。但是,当取经成功之后,孙悟空也老是不客气地做起了佛爷,那花果山的猴头看样子到底没有做佛爷来的风光啊。
但是在西方人眼里,成功就是任务的完成,对成功者来说,这件事情本身就够他自豪一辈子了,其他虚名则可有可无。
近年来,西方拍了很多的灾难片,有火山爆发的,有龙卷风的,有地震海啸的,有暴风雪的。在这些片子里,主人公无一例外的尝试去对抗大自然的力量。人类还没有取得应付自然灾害的能力,这时候面对它和解决它的勇气非常重要。东西方文化各有所长,但是,在我们这一代以及我们的下一代,在面对突发事件的情况下,我们的应变能力显然已经落于人后。
从西游记与魔戒这两部神怪小说分析,东西方文化有他根源上的巨大差异。东方文化胜在秩序的稳定和对事物原理孜孜不倦的追求上。而西方文化在人性的尊重,个性化创造力的培养,以及鼓励主动尝试等方面略高一筹。二十一世纪是全球一体化时代,东西方文化的融合将成为大家关心的主题。为了我们的下一代,西方一些优秀的教育理念是值得我们研究和学习的。
Hello, Android!
Hello, Android!
作为一个开发人员,你得到一个开发框架的第一映像是编写“Hello, World!”程序是否容易,对于Android,他是非常容易的。
创建项目
创建项目能有多容易就多容易。获得Eclipse插件使Android开发非常容易。
你需要安装有EclipseIDE的开发计算机,你还需要安装ADT插件。一旦你准备好了,你现在就可以开始进入开发。
首先,下面是高度概括的建立"Hello, World!"程序的方法:
1)在菜单File > New > Project中创建一个新的"Android Project"
2)在New Android Project对话框中填写项目详细信息
3)编辑自动产生的源代码模板以便使程序显示一些输出信息
这就是全部!下一步,让我们详细进入每一步。
1、创建一个新的Android项目
从Eclipse中,选择菜单File > New > Project。如果ADT插件成功安装,结果对话框应该有一个标记为“Android”的文件夹,该文件夹包含一个简单入口:"Android Project"。
一旦你选择了"Android Project"文件夹,点击Next按钮。
2、填写项目详细信息
下一步的屏幕允许你填写项目详细信息。下面是例子:
以下列出这个屏幕的每个字段的意思:
(1)Project Name
这是存放该项目的目录或者文件夹名称。
(2)Package Name
这是包的名称空间(JAVA语言针对包使用同样的规则),你的所有代码均驻留在该命名空间中。同时也设置了即将产生的桩活动下面的包名称
你用在应用程序中的包名称必须在系统安装的所有包中唯一;基于这样的原因,你的应用程序使用标准域风格的包是非常必要的。上面的例子中,你使用了包域“com.android”;你应该使用一个不同的名字,根据你的组织做适当变化。
(3)Activity Name
这是类桩的名称,类桩(class stub)由插件产生。这是Android活动的子类。一个活动是一个能运行和完成一定工作的简单类。如果选择的话,能创建一个UI,但是不是必要的。
(4)Application Name
这是你的应用程序供使用者查看的标题。
复选项切换选项“Use default location”允许你改变磁盘位置,该位置用于项目文件产生和保存。
3、编辑自动产生的源代码
插件运行后,你将会有一个称为HelloAndroid的类,位于包HelloAndroid > src > com.android.hello。它看起来是这样的:
public class HelloAndroid extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
现在,你能马上运行这些代码,但是让我们更进一步,我们应该理解到底发生了什么事情。下一步让我们修改一些代码!
建立UI
仔细看看下面的这些修改过的代码,在你的HelloAndroid.java文件中做同样的修改。我们会一行一行的剖析他们:
package com.android.hello;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class HelloAndroid extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView tv = new TextView(this);
tv.setText("Hello, Android");
setContentView(tv);
}
}
提示:如果你忘记引入TextView包,请试着做:按Ctrl-Shift-O (Cmd-Shift-O, 在 Mac操作系统上),这是一个Eclipse组织引入操作的快捷键——他自动识别丢失的包并为你自动加入。
Android的UI由叫做Views类的体系构成。一个View是一个简化的绘图对象,比如单选按钮、动画、文本标签。处理文本的View子类的名称为TextView。
下面是你建立TextView的方法:
TextView tv = new TextView(this);
TextView的构造函数的入参是Android Context实例。Context是系统的句柄;它提供诸如资源解析、数据库访问、参数选取等服务。Activity类继承自Context。因此,我们的HelloAndroid类是Activity的子类,它也是一个Context,所以我们能把this引用传送给TextView。
一旦我们建立了TextView,我们需要告之显示什么:
tv.setText("Hello, Android");
这里没有什么太令人惊讶的。
在此处,我们建立了TextView,并告之显示什么文本内容。最后一步是连接这个TextView到屏幕显示,像这样:
setContentView(tv);
在Activity中的setContentView方法指示系统和Activity的UI联系起来。如果一个Activity没有调用这个方法,根本没有UI出现,系统将会显示一个空白屏幕。为了我们的目的,我们想的所有事情是现实一些文本,因此我们传送文本给才创建的TextView。
这就是Android的“Hello, World”!下一步我们来看看他怎样运行的。
运行代码:Hello, Android
ADT插件非常容易运行应用程序。选择菜单Run > Open Run Dialog开始,Eclipse3.4,选择菜单Run > Run Configurations。
下一步,选中"Android Application"条目,然后点击左上角的图标(在角上画有加号的一张纸),或者双击"Android Application" 入口。这样,你就有了一个叫“New_configuration”的新的入口条目。
修改名称来表达要做的事情,比如"Hello, Android",然后双击Browse按钮来选取你的项目。(如果你在Eclipse中打开了不止一个项目,保证你选取正确的一个。)
插件自动扫描你的项目以寻找活动子类,自动添加找到的每一个到"Activity:" 标签下的下拉列表中。因为,你的 "Hello, Android"仅仅有一个,所以它将会是默认的一个活动子类,你可以继续下面的步骤。
点击“Apply”按钮。
那就是他,大功告成!点击Run按钮,Android模拟器应该会启动。一旦模拟器启动,你的应用程序将会显示出来。当说过和做过这一切,你应该会看到这样的事情:
在Android中显示“Hello, World”。非常简单。教程的下一章提供更多的详细信息,你可以找到你能更多了解Android的有价值的内容。
作为一个开发人员,你得到一个开发框架的第一映像是编写“Hello, World!”程序是否容易,对于Android,他是非常容易的。
创建项目
创建项目能有多容易就多容易。获得Eclipse插件使Android开发非常容易。
你需要安装有EclipseIDE的开发计算机,你还需要安装ADT插件。一旦你准备好了,你现在就可以开始进入开发。
首先,下面是高度概括的建立"Hello, World!"程序的方法:
1)在菜单File > New > Project中创建一个新的"Android Project"
2)在New Android Project对话框中填写项目详细信息
3)编辑自动产生的源代码模板以便使程序显示一些输出信息
这就是全部!下一步,让我们详细进入每一步。
1、创建一个新的Android项目
从Eclipse中,选择菜单File > New > Project。如果ADT插件成功安装,结果对话框应该有一个标记为“Android”的文件夹,该文件夹包含一个简单入口:"Android Project"。
一旦你选择了"Android Project"文件夹,点击Next按钮。
2、填写项目详细信息
下一步的屏幕允许你填写项目详细信息。下面是例子:
以下列出这个屏幕的每个字段的意思:
(1)Project Name
这是存放该项目的目录或者文件夹名称。
(2)Package Name
这是包的名称空间(JAVA语言针对包使用同样的规则),你的所有代码均驻留在该命名空间中。同时也设置了即将产生的桩活动下面的包名称
你用在应用程序中的包名称必须在系统安装的所有包中唯一;基于这样的原因,你的应用程序使用标准域风格的包是非常必要的。上面的例子中,你使用了包域“com.android”;你应该使用一个不同的名字,根据你的组织做适当变化。
(3)Activity Name
这是类桩的名称,类桩(class stub)由插件产生。这是Android活动的子类。一个活动是一个能运行和完成一定工作的简单类。如果选择的话,能创建一个UI,但是不是必要的。
(4)Application Name
这是你的应用程序供使用者查看的标题。
复选项切换选项“Use default location”允许你改变磁盘位置,该位置用于项目文件产生和保存。
3、编辑自动产生的源代码
插件运行后,你将会有一个称为HelloAndroid的类,位于包HelloAndroid > src > com.android.hello。它看起来是这样的:
public class HelloAndroid extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
现在,你能马上运行这些代码,但是让我们更进一步,我们应该理解到底发生了什么事情。下一步让我们修改一些代码!
建立UI
仔细看看下面的这些修改过的代码,在你的HelloAndroid.java文件中做同样的修改。我们会一行一行的剖析他们:
package com.android.hello;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class HelloAndroid extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView tv = new TextView(this);
tv.setText("Hello, Android");
setContentView(tv);
}
}
提示:如果你忘记引入TextView包,请试着做:按Ctrl-Shift-O (Cmd-Shift-O, 在 Mac操作系统上),这是一个Eclipse组织引入操作的快捷键——他自动识别丢失的包并为你自动加入。
Android的UI由叫做Views类的体系构成。一个View是一个简化的绘图对象,比如单选按钮、动画、文本标签。处理文本的View子类的名称为TextView。
下面是你建立TextView的方法:
TextView tv = new TextView(this);
TextView的构造函数的入参是Android Context实例。Context是系统的句柄;它提供诸如资源解析、数据库访问、参数选取等服务。Activity类继承自Context。因此,我们的HelloAndroid类是Activity的子类,它也是一个Context,所以我们能把this引用传送给TextView。
一旦我们建立了TextView,我们需要告之显示什么:
tv.setText("Hello, Android");
这里没有什么太令人惊讶的。
在此处,我们建立了TextView,并告之显示什么文本内容。最后一步是连接这个TextView到屏幕显示,像这样:
setContentView(tv);
在Activity中的setContentView方法指示系统和Activity的UI联系起来。如果一个Activity没有调用这个方法,根本没有UI出现,系统将会显示一个空白屏幕。为了我们的目的,我们想的所有事情是现实一些文本,因此我们传送文本给才创建的TextView。
这就是Android的“Hello, World”!下一步我们来看看他怎样运行的。
运行代码:Hello, Android
ADT插件非常容易运行应用程序。选择菜单Run > Open Run Dialog开始,Eclipse3.4,选择菜单Run > Run Configurations。
下一步,选中"Android Application"条目,然后点击左上角的图标(在角上画有加号的一张纸),或者双击"Android Application" 入口。这样,你就有了一个叫“New_configuration”的新的入口条目。
修改名称来表达要做的事情,比如"Hello, Android",然后双击Browse按钮来选取你的项目。(如果你在Eclipse中打开了不止一个项目,保证你选取正确的一个。)
插件自动扫描你的项目以寻找活动子类,自动添加找到的每一个到"Activity:" 标签下的下拉列表中。因为,你的 "Hello, Android"仅仅有一个,所以它将会是默认的一个活动子类,你可以继续下面的步骤。
点击“Apply”按钮。
那就是他,大功告成!点击Run按钮,Android模拟器应该会启动。一旦模拟器启动,你的应用程序将会显示出来。当说过和做过这一切,你应该会看到这样的事情:
在Android中显示“Hello, World”。非常简单。教程的下一章提供更多的详细信息,你可以找到你能更多了解Android的有价值的内容。
2008年11月24日星期一
设备调式和测试设置
设备调式和测试设置
Android让你设置需许多设置,这些设置会让测试和调试应用程序更容易。进入模拟器的开发设置页的方法:进入菜单“Dev Tools”-->“Development Settings”,系统会打开开发设置页面,里面包含如下的选项:
* Debug app——选中将要调式的应用程序。你不需要设置这个来attach绑定一个编辑器,但是设置这个值两种效果:
(1)如果你调试时长时间暂停在断点上,这个设置会阻止Android抛出异常。
(2)这个设置会使你选择“Wait for Debugger”选项,该选项会暂停应用程序启动直到你的调试器重新联系上。
* Wait for debugger——阻止被选择的应用程序调用直到一个调试器被依附。你能用于在OnCreate()上设置断点,在调试一个活动的启动过程时很重要。当你变更这个选项时任何正在运行的被选择的应用程序将会被终止。为了能选中这个选项,你必须象上一个选项描述的那样选中一个调试应用程序。你能通过在代码里面调用waitForDebugger()来达到同样的目的。
* Immediately destroy activities——告诉系统一旦活动停止就摧毁这个活动(类似Android回收内存)。这有助于测试onSaveInstance(Bundle)/onCreate(android.os.Bundle)代码执行路径,否则强制测试是很困难的。选用这个选项会恰当暴露许多程序问题,这些问题多半是不节约内存。
* Show screen updates——瞬时刷新一个粉红色的矩形框,在正在重画的任何屏幕区域。这个选项非常有利于发现不必要的屏幕重画动作。
* Show CPU usage——在屏幕顶端显示CPU仪表盘,用于显示CPU使用了多少。顶上的红色条显示全部的CPU使用,下面的绿色条显示话费在组合屏幕上的CPU时间。注意:一旦打开你不能关闭这个功能,除非你重新模拟器。
* Show background——当没有活动屏幕可见时显示一个背景屏幕。一般不会发生,但是调试期间可能发生。
这些设置经过模拟器重启后被记住。
顶级调试技巧
快速堆栈转储
为了从模拟器上获得堆栈转储,你可使用adb外壳登录,使用ps命令找到你想要的进程,然后使用kill -3。堆栈跟踪会显示在日志文件中。
在模拟器屏幕显示有用的信息
设备能显示有用的信息,比如CPU使用情况或者高亮显示重画区域。在开发设置窗口中打开或者关闭这些设置,参见“Setting debug and test configurations on the emulator”
从模拟器(dumpstate)中获取系统状态信息
从Dalvik调试监视器服务工具上你能访问dumpstate(转储状态)信息。参见adb主题页的“dumpsys and dumpstate”
从模拟器(dumpsys)中获取状态信息
从Dalvik调试监视器服务工具上你能访问dumpsys(转储系统)信息。参见adb主题页的“dumpsys and dumpstate”
获取无限连接信息
从Dalvik调试监视器服务工具上你能访问无线连接信息。从Device菜单中,选择“Dump radio state”
记录跟踪数据
你能记录活动中的方法调用以及其他跟踪数据,方法是:调用android.os.Debug.startMethodTracing()。参见“Running the Traceview Debugging Program”。
记录无线电数据
默认情况下,无线电信息不记录在系统中,因为数据量很大。但是,通过下面的命令打开无线电数据记录功能。
adb shell
logcat -b radio
运行adb
Android使用一个提供众多能力的工具称为adb,包括移动和同步模拟器文件、转发端口、在模拟器上运行UNIX外壳。详情请参考“Using adb”。
从模拟器中捕获屏幕
DDMS能从模拟器中捕获屏幕截图。
使用调试帮助类
Android提供调试帮助类,比如util.Log,方便调试。
生成和安装Android程序
Android需要定制生成工具,以便能够恰当的生成资源文件和应用程序的其他部分。正因为如此,你必须有一个针对你的应用程序特定的生成环境。
定制的Android编译步骤包括编译XMl和其他资源文件、创建合适的输出格式。一个已编译的应用程序是一个.apk文件,该文件是压缩文件,包含.dex文件、资源文件、原始数据文件以及其他文件。你可以创建一个合适结构的Android项目,从零开始,或者基于已存在的源代码。
Android目前不支持在本地代码级别(C/C++)上的第三方应用程序开发。
推荐开发方式是使用Android插件的Eclipse,提供生成、运行、调试支持。
如果你有其他IDE,Android提供工具来生成、调试应用程序,但是他们没有和IDE整合。
移除一个Android程序
为了移除一个你已经安装到模拟器上的程序,你需要运行adb并删除安装时送到模拟器上的.apk文件。使用adb外壳落入设备的命令外壳,就像相关主题描述的那样,定位到data/app/,然后使用rm your_app.apk移除文件。
Eclipse技巧
在Eclipse中执行任意Java表达式
当Eclipse展厅在一个断点时你能执行任意代码。例如,进入带称为zip的字符串参数的一个函数时,键入android.os.Debug.startMethodTracing()将会启动dmTrace。
打开一个代码执行窗口,选择主菜单下的菜单Window>Show View>Display打开Display窗口,一个简单的文本编辑器。输入您的表达式,突出显示文字,然后点击' J '图标(或按Ctrl + Shift + d )来运行您的代码。这里的代码运行在被选择的线程所在的上下文环境中,被选择的线程指在断点下停止或者处于单步运行点下的线程。(如果你手工挂起线程,你不得不一次运行一步;如果线程处于Object.wait()中就不会起作用。)
If you are currently paused on a breakpoint, you can simply highlight and execute a piece of source code by pressing CTRL + SHIFT + D.
如果你目前暂停于一个断点,你可以简单突出显示一段代码然后按“CTRL + SHIFT + D”执行之。
你能突出显示一段文本,该文本位于同一段范围内,该范围通过按ALT +SHIFT + UP ARROW选择的最大封闭块,或者按DOWN ARROW选择更小块。
这里有几个在Desplay窗口中执行的例子,还提供了执行响应。
Input Response
zip (java.lang.String) /work/device/out/linux-x86-debug/android/app/android_sdk.zip
zip.endsWith(".zip") (boolean) true
zip.endsWith(".jar") (boolean) false
你也能执行任意代码,而不是用剪贴簿来调试。请搜索和“scrapbook”相关的Eclipse文档。
手工运行DDMS
尽管推荐调试方法是使用ADT插件,但是你也可以手工运行DDMS并在8700端口上配置Eclipse。注意:保证你第一次使用DDMS。
加入JUnit测试类
在Eclipse/ADT中,你能加入JUnit测试类到你的程序中。但是,在你的测试适当运行之前,你需要设置一个自定义的JUnit配置。欲知有关怎样设置Junit配置的更多信息,请参见“ I can't run a Junit test class in Eclipse”。
Android让你设置需许多设置,这些设置会让测试和调试应用程序更容易。进入模拟器的开发设置页的方法:进入菜单“Dev Tools”-->“Development Settings”,系统会打开开发设置页面,里面包含如下的选项:
* Debug app——选中将要调式的应用程序。你不需要设置这个来attach绑定一个编辑器,但是设置这个值两种效果:
(1)如果你调试时长时间暂停在断点上,这个设置会阻止Android抛出异常。
(2)这个设置会使你选择“Wait for Debugger”选项,该选项会暂停应用程序启动直到你的调试器重新联系上。
* Wait for debugger——阻止被选择的应用程序调用直到一个调试器被依附。你能用于在OnCreate()上设置断点,在调试一个活动的启动过程时很重要。当你变更这个选项时任何正在运行的被选择的应用程序将会被终止。为了能选中这个选项,你必须象上一个选项描述的那样选中一个调试应用程序。你能通过在代码里面调用waitForDebugger()来达到同样的目的。
* Immediately destroy activities——告诉系统一旦活动停止就摧毁这个活动(类似Android回收内存)。这有助于测试onSaveInstance(Bundle)/onCreate(android.os.Bundle)代码执行路径,否则强制测试是很困难的。选用这个选项会恰当暴露许多程序问题,这些问题多半是不节约内存。
* Show screen updates——瞬时刷新一个粉红色的矩形框,在正在重画的任何屏幕区域。这个选项非常有利于发现不必要的屏幕重画动作。
* Show CPU usage——在屏幕顶端显示CPU仪表盘,用于显示CPU使用了多少。顶上的红色条显示全部的CPU使用,下面的绿色条显示话费在组合屏幕上的CPU时间。注意:一旦打开你不能关闭这个功能,除非你重新模拟器。
* Show background——当没有活动屏幕可见时显示一个背景屏幕。一般不会发生,但是调试期间可能发生。
这些设置经过模拟器重启后被记住。
顶级调试技巧
快速堆栈转储
为了从模拟器上获得堆栈转储,你可使用adb外壳登录,使用ps命令找到你想要的进程,然后使用kill -3。堆栈跟踪会显示在日志文件中。
在模拟器屏幕显示有用的信息
设备能显示有用的信息,比如CPU使用情况或者高亮显示重画区域。在开发设置窗口中打开或者关闭这些设置,参见“Setting debug and test configurations on the emulator”
从模拟器(dumpstate)中获取系统状态信息
从Dalvik调试监视器服务工具上你能访问dumpstate(转储状态)信息。参见adb主题页的“dumpsys and dumpstate”
从模拟器(dumpsys)中获取状态信息
从Dalvik调试监视器服务工具上你能访问dumpsys(转储系统)信息。参见adb主题页的“dumpsys and dumpstate”
获取无限连接信息
从Dalvik调试监视器服务工具上你能访问无线连接信息。从Device菜单中,选择“Dump radio state”
记录跟踪数据
你能记录活动中的方法调用以及其他跟踪数据,方法是:调用android.os.Debug.startMethodTracing()。参见“Running the Traceview Debugging Program”。
记录无线电数据
默认情况下,无线电信息不记录在系统中,因为数据量很大。但是,通过下面的命令打开无线电数据记录功能。
adb shell
logcat -b radio
运行adb
Android使用一个提供众多能力的工具称为adb,包括移动和同步模拟器文件、转发端口、在模拟器上运行UNIX外壳。详情请参考“Using adb”。
从模拟器中捕获屏幕
DDMS能从模拟器中捕获屏幕截图。
使用调试帮助类
Android提供调试帮助类,比如util.Log,方便调试。
生成和安装Android程序
Android需要定制生成工具,以便能够恰当的生成资源文件和应用程序的其他部分。正因为如此,你必须有一个针对你的应用程序特定的生成环境。
定制的Android编译步骤包括编译XMl和其他资源文件、创建合适的输出格式。一个已编译的应用程序是一个.apk文件,该文件是压缩文件,包含.dex文件、资源文件、原始数据文件以及其他文件。你可以创建一个合适结构的Android项目,从零开始,或者基于已存在的源代码。
Android目前不支持在本地代码级别(C/C++)上的第三方应用程序开发。
推荐开发方式是使用Android插件的Eclipse,提供生成、运行、调试支持。
如果你有其他IDE,Android提供工具来生成、调试应用程序,但是他们没有和IDE整合。
移除一个Android程序
为了移除一个你已经安装到模拟器上的程序,你需要运行adb并删除安装时送到模拟器上的.apk文件。使用adb外壳落入设备的命令外壳,就像相关主题描述的那样,定位到data/app/,然后使用rm your_app.apk移除文件。
Eclipse技巧
在Eclipse中执行任意Java表达式
当Eclipse展厅在一个断点时你能执行任意代码。例如,进入带称为zip的字符串参数的一个函数时,键入android.os.Debug.startMethodTracing()将会启动dmTrace。
打开一个代码执行窗口,选择主菜单下的菜单Window>Show View>Display打开Display窗口,一个简单的文本编辑器。输入您的表达式,突出显示文字,然后点击' J '图标(或按Ctrl + Shift + d )来运行您的代码。这里的代码运行在被选择的线程所在的上下文环境中,被选择的线程指在断点下停止或者处于单步运行点下的线程。(如果你手工挂起线程,你不得不一次运行一步;如果线程处于Object.wait()中就不会起作用。)
If you are currently paused on a breakpoint, you can simply highlight and execute a piece of source code by pressing CTRL + SHIFT + D.
如果你目前暂停于一个断点,你可以简单突出显示一段代码然后按“CTRL + SHIFT + D”执行之。
你能突出显示一段文本,该文本位于同一段范围内,该范围通过按ALT +SHIFT + UP ARROW选择的最大封闭块,或者按DOWN ARROW选择更小块。
这里有几个在Desplay窗口中执行的例子,还提供了执行响应。
Input Response
zip (java.lang.String) /work/device/out/linux-x86-debug/android/app/android_sdk.zip
zip.endsWith(".zip") (boolean) true
zip.endsWith(".jar") (boolean) false
你也能执行任意代码,而不是用剪贴簿来调试。请搜索和“scrapbook”相关的Eclipse文档。
手工运行DDMS
尽管推荐调试方法是使用ADT插件,但是你也可以手工运行DDMS并在8700端口上配置Eclipse。注意:保证你第一次使用DDMS。
加入JUnit测试类
在Eclipse/ADT中,你能加入JUnit测试类到你的程序中。但是,在你的测试适当运行之前,你需要设置一个自定义的JUnit配置。欲知有关怎样设置Junit配置的更多信息,请参见“ I can't run a Junit test class in Eclipse”。
2008年11月21日星期五
使用ApiDemos例子程序
使用ApiDemos例子程序
AndroidSDK包含一套例子程序,用于证明API和许多功能。ApiDemos包预装到模拟器上,因此你能访问他(通过启动一个模拟器),然后平滑打开主界面的应用程序抽屉。
你能找到ApiDemos应用程序的源代码,位于 /samples/ApiDemos目录里面,仔细考察他一边了解怎么实现的。
如果你想,你能作为源代码项目调用ApiDemos例子程序并修改他,然后在模拟器中运行他。但是,为了达到目的,你需要首先反安装ApiDemos的预装版本。如果你试图在你的开发环境中运行或者修改ApiDemos,而不是首先卸载预装版本,你会获得一个安装错误。
为了获取更多关于反安装和重新安装ApiDemos的信息,请参考问题主题文章“ I can't install ApiDemos apps in my IDE because of a signing error”。
调试
Android有一套相当广泛的工具来帮助你调试程序:
* DDMS——一款图形程序,支持端口映射(以便你在IDE中设置代码断点)、模拟器上屏幕捕获、线程堆栈信息以及其他特性。你也能运行logcat检索你的日志信息。欲知更多信息请查看相关主题。
* logcat——转储系统消息的日志。这些消息包括当模拟器抛出一个异常时堆栈跟踪,以及日志信息。为了运行logcat请参阅更过信息。
*Android Lod——一个日志记录类,用于输出消息到一个模拟器的日志文件。你能通过在DDMS上运行logcat来实时阅读消息。需要加入几个方法调用到你的代码。
为了使用这个Log类,你仅仅需要调用Log.v()(详细),Log.d()(调式),Log.i()(信息),Log.w()(警告)或者Log.e(错误),根据你的设计的日志消息的重要性来区别调用。
Log.i("MyActivity", "MyClass.getView() — Requesting item number " + position)
你能使用logcat来读这些消息。
* TraceView——Android能保存大量方法调用和次数到一个日志文件中,这个日志文件你能调用Traceview在图形阅读器中浏览。欲知更多信息请查看相关信息主题。
* Eclipse插件——Eclipse的Android插件整合了诸如ADB、DDMS、Logcat输出等工具。欲知更多信息请查看相关信息主题。
* Debug和Test Device设置——Android开放了几种设置,可以获取有用的信息,比如CPU使用率、帧速率。请参见下面的“Debug and Test Settings on the Emulator”
同时,请参见文档的问题解决章节,这些章节指出了为什么程序不能显示在模拟器中,或者为什么不能启动。
AndroidSDK包含一套例子程序,用于证明API和许多功能。ApiDemos包预装到模拟器上,因此你能访问他(通过启动一个模拟器),然后平滑打开主界面的应用程序抽屉。
你能找到ApiDemos应用程序的源代码,位于
如果你想,你能作为源代码项目调用ApiDemos例子程序并修改他,然后在模拟器中运行他。但是,为了达到目的,你需要首先反安装ApiDemos的预装版本。如果你试图在你的开发环境中运行或者修改ApiDemos,而不是首先卸载预装版本,你会获得一个安装错误。
为了获取更多关于反安装和重新安装ApiDemos的信息,请参考问题主题文章“ I can't install ApiDemos apps in my IDE because of a signing error”。
调试
Android有一套相当广泛的工具来帮助你调试程序:
* DDMS——一款图形程序,支持端口映射(以便你在IDE中设置代码断点)、模拟器上屏幕捕获、线程堆栈信息以及其他特性。你也能运行logcat检索你的日志信息。欲知更多信息请查看相关主题。
* logcat——转储系统消息的日志。这些消息包括当模拟器抛出一个异常时堆栈跟踪,以及日志信息。为了运行logcat请参阅更过信息。
*Android Lod——一个日志记录类,用于输出消息到一个模拟器的日志文件。你能通过在DDMS上运行logcat来实时阅读消息。需要加入几个方法调用到你的代码。
为了使用这个Log类,你仅仅需要调用Log.v()(详细),Log.d()(调式),Log.i()(信息),Log.w()(警告)或者Log.e(错误),根据你的设计的日志消息的重要性来区别调用。
Log.i("MyActivity", "MyClass.getView() — Requesting item number " + position)
你能使用logcat来读这些消息。
* TraceView——Android能保存大量方法调用和次数到一个日志文件中,这个日志文件你能调用Traceview在图形阅读器中浏览。欲知更多信息请查看相关信息主题。
* Eclipse插件——Eclipse的Android插件整合了诸如ADB、DDMS、Logcat输出等工具。欲知更多信息请查看相关信息主题。
* Debug和Test Device设置——Android开放了几种设置,可以获取有用的信息,比如CPU使用率、帧速率。请参见下面的“Debug and Test Settings on the Emulator”
同时,请参见文档的问题解决章节,这些章节指出了为什么程序不能显示在模拟器中,或者为什么不能启动。
2008年11月18日星期二
签名Android应用程序
签名你的应用程序
Android系统要求所有已安装的程序必须数字签名——没有适当数字签名系统不会安装或运行一个应用程序。这适用于任何地方运行的Android系统,不管是模拟器还是实际设备。基于这个原因,在你能在一个模拟器或者设备上运行或者调试应用程序前,必须为你的应用程序设置签名。
理解Android应用程序签名的要点是:
所有应用必须签名,不签名系统不会安装这个应用程序。
你可使用自签名证书来签名你的应用程序,而不需要证书管理机构。
系统仅在安装时验证签名证书的有效期。如果应用程序的签名证书在应用程序安装后有效,应用程序的功能正常使用。
你可使用标准工具——Keytool和Jarsigner——产生钥匙并签名你的应用程序的.apk文件。
当调试时,AndroidSDK工具辅助你签名你的应用程序。Eclipse的ADT插件和Ant编译工具提供两种签名模式:调试牧师和发布模式。
调试模式:编译使用Keytool工具(这个工具包含在JDK中)以创建一个keystore和带有一个明确别名和密码的key。每次编译,这个工具使用调试key来签名应用程序的.apk文件。因为密码已知,这个工具不必每次编译时提醒你keystore/key的密码。
发布模式:当你的应用程序准备发布时,你在发布签名模式编译你的应用程序。在发布模式,这个工具编译你的.apk而不签名。你必须使用Keytool产生你自己的keystore/key,然后使用Jarsigner工具(也包含在JDK中)来签名.apk文件。
签名的基本设置
为了支持keystore和调试key的产生,你应该首先保证Keytool能在你的SDK中获取。通常情况下,只要你保证你的JAVA_HOME环境变量被设置,并且指向合适的JDK,你就能告诉SDK编译工具怎么找到Keytool。还有一种办法,你可将Keytool的JDK版本加入你的PATH变量中。
如果你在提供Gnu编译器的某个Linux版本上开发,你必须指定系统使用Keytool的JDK版本,而不是GCJ版本。若Keytool已经存在于你的PATH,他可以指向/usr/bin/keytool的链接。这种情况下,检查链接目标保证指向JDK的Keytool。
在Eclipse/ADT中签名
如果你在Eclipse中开发,并且已经按照上述步骤设置Keytool,那么签名默认采用调试模式。当你运行和调试你的应用程序时,ADT签名.apk文件,并在模拟中中安装。不需要你做指定操作,因为ADT已经成功访问了Keytool。
为了在发布模式中编译你的应用程序,右击Package面板的项目,然后选择Android Tools>Export Application Package菜单,另外一种方法:你能Manifest编辑器总页面"Android Manifest Overview"里面执行"Exporting the unsigned .apk"超级链接。在你保存了输出文件.apk后,你需要在发布前携带你自己的key使用Jarsigner来签名.apk。如果你没有一个key,你可以使用Kestroe携带合适参数来创建keystore和key。如果你已经有一个Key,比如一个公司的Key,你能使用它来签名.apk文件。
在Ant中签名
如果你使用Ant生成你的.apk文件,debug签名模式默认开启,前提是你通过包含在最新SDK中的活动创建(activitycreator)工具产生的build.xml文件。当你运行Ant使用bulid.xml来编译你的应用程序,这个生成脚本产生一个keystore/key并签名.apk文件。你自己不需要做什么特定操作。
为了在发布模式下编译你的应用程序,你需要做的所有事情是使用Ant命令指定一个生成目标“release”。例如,如果你从包含build.xml文件的目录中运行Ant,Ant命令看起来是这样:
ant release
这个生成描述脚本编译应用程序为.apk,而没有签名。当编译.apk后你需要在发布前携带你自己的key使用Jarsigner来签名.apk。如果你没有一个key,你可以使用Kestroe携带合适参数来创建keystore和key。如果你已经有一个Key,比如一个公司的Key,你能使用它来签名.apk文件。
Debug认证过期
自签名认证使用DEBUG模式下签名你的医用程序,自签名认证从创建时间开始1年后过期。
当认证过期时,你将会得到一个创建错误。在Ant中编译,错误看起来这样:
debug:
[echo] Packaging bin/samples-debug.apk, and signing it with a debug key...
[exec] Debug Certificate expired on 8/4/08 3:43 PM
在Eclipse/ADT中,你在Android控制台会看到一个类似的错误。
为了纠正这个问题,简单删除debug.keystore 文件即可。在Linux/Mac OSX操作系统中,这个文件存储在 ~/.android目录中,而在win XP操作系统,这个文件保存在C:\Documents and Settings\\Local Settings\Application Data\Android目录中。在Vista,这个文件保存在C:\Users\\AppData\Local\Android目录中。
下次你生成时,生成工具将会重新产生新的keystore和调试key.
注意,如果你的开发机器正在使用非阳历地区,生成工具可能错误地产生一个已经过期的调试认证,为此,当你试图编译你的应用程序时你会得到一个错误。欲知进一步的信息,请阅读问题主题文章“I can't compile my app because the build tools generated an expired debug certificate”。
Android系统要求所有已安装的程序必须数字签名——没有适当数字签名系统不会安装或运行一个应用程序。这适用于任何地方运行的Android系统,不管是模拟器还是实际设备。基于这个原因,在你能在一个模拟器或者设备上运行或者调试应用程序前,必须为你的应用程序设置签名。
理解Android应用程序签名的要点是:
所有应用必须签名,不签名系统不会安装这个应用程序。
你可使用自签名证书来签名你的应用程序,而不需要证书管理机构。
系统仅在安装时验证签名证书的有效期。如果应用程序的签名证书在应用程序安装后有效,应用程序的功能正常使用。
你可使用标准工具——Keytool和Jarsigner——产生钥匙并签名你的应用程序的.apk文件。
当调试时,AndroidSDK工具辅助你签名你的应用程序。Eclipse的ADT插件和Ant编译工具提供两种签名模式:调试牧师和发布模式。
调试模式:编译使用Keytool工具(这个工具包含在JDK中)以创建一个keystore和带有一个明确别名和密码的key。每次编译,这个工具使用调试key来签名应用程序的.apk文件。因为密码已知,这个工具不必每次编译时提醒你keystore/key的密码。
发布模式:当你的应用程序准备发布时,你在发布签名模式编译你的应用程序。在发布模式,这个工具编译你的.apk而不签名。你必须使用Keytool产生你自己的keystore/key,然后使用Jarsigner工具(也包含在JDK中)来签名.apk文件。
签名的基本设置
为了支持keystore和调试key的产生,你应该首先保证Keytool能在你的SDK中获取。通常情况下,只要你保证你的JAVA_HOME环境变量被设置,并且指向合适的JDK,你就能告诉SDK编译工具怎么找到Keytool。还有一种办法,你可将Keytool的JDK版本加入你的PATH变量中。
如果你在提供Gnu编译器的某个Linux版本上开发,你必须指定系统使用Keytool的JDK版本,而不是GCJ版本。若Keytool已经存在于你的PATH,他可以指向/usr/bin/keytool的链接。这种情况下,检查链接目标保证指向JDK的Keytool。
在Eclipse/ADT中签名
如果你在Eclipse中开发,并且已经按照上述步骤设置Keytool,那么签名默认采用调试模式。当你运行和调试你的应用程序时,ADT签名.apk文件,并在模拟中中安装。不需要你做指定操作,因为ADT已经成功访问了Keytool。
为了在发布模式中编译你的应用程序,右击Package面板的项目,然后选择Android Tools>Export Application Package菜单,另外一种方法:你能Manifest编辑器总页面"Android Manifest Overview"里面执行"Exporting the unsigned .apk"超级链接。在你保存了输出文件.apk后,你需要在发布前携带你自己的key使用Jarsigner来签名.apk。如果你没有一个key,你可以使用Kestroe携带合适参数来创建keystore和key。如果你已经有一个Key,比如一个公司的Key,你能使用它来签名.apk文件。
在Ant中签名
如果你使用Ant生成你的.apk文件,debug签名模式默认开启,前提是你通过包含在最新SDK中的活动创建(activitycreator)工具产生的build.xml文件。当你运行Ant使用bulid.xml来编译你的应用程序,这个生成脚本产生一个keystore/key并签名.apk文件。你自己不需要做什么特定操作。
为了在发布模式下编译你的应用程序,你需要做的所有事情是使用Ant命令指定一个生成目标“release”。例如,如果你从包含build.xml文件的目录中运行Ant,Ant命令看起来是这样:
ant release
这个生成描述脚本编译应用程序为.apk,而没有签名。当编译.apk后你需要在发布前携带你自己的key使用Jarsigner来签名.apk。如果你没有一个key,你可以使用Kestroe携带合适参数来创建keystore和key。如果你已经有一个Key,比如一个公司的Key,你能使用它来签名.apk文件。
Debug认证过期
自签名认证使用DEBUG模式下签名你的医用程序,自签名认证从创建时间开始1年后过期。
当认证过期时,你将会得到一个创建错误。在Ant中编译,错误看起来这样:
debug:
[echo] Packaging bin/samples-debug.apk, and signing it with a debug key...
[exec] Debug Certificate expired on 8/4/08 3:43 PM
在Eclipse/ADT中,你在Android控制台会看到一个类似的错误。
为了纠正这个问题,简单删除debug.keystore 文件即可。在Linux/Mac OSX操作系统中,这个文件存储在 ~/.android目录中,而在win XP操作系统,这个文件保存在C:\Documents and Settings\
下次你生成时,生成工具将会重新产生新的keystore和调试key.
注意,如果你的开发机器正在使用非阳历地区,生成工具可能错误地产生一个已经过期的调试认证,为此,当你试图编译你的应用程序时你会得到一个错误。欲知进一步的信息,请阅读问题主题文章“I can't compile my app because the build tools generated an expired debug certificate”。
2008年11月13日星期四
创建Android运行配置
创建Android运行配置
在你在Eclipse上运行并调试Android应用程序前,你必须为之创建一个运行配置。一个运行配置指定运行项目,启动的活动,使用的模拟器选项,等等。
为了针对你的应用程序创建运行配置,根据你的Eclipse版本选择合适的步骤:
1、打开运行配置管理器
(1)3.3
选择Run > Open Run Dialog... 或者Run > Open Debug Dialog...
(2)3.4
选择Run > Run Configurations... 或者Run > Debug Configurations...
2、在左边的工程类型列表中,定位the Android Application条目,双击之或者右键> New, 以创建一个新的运行配置.
3、键入一个配置名称
4、在Android可选页, 浏览确定启动的项目和活动.
5、在Target可选页, 设置喜好的屏幕和网络特性,以及其他模拟器启动选项.
6、你也可以根据喜好在Common可选页设置额外的选项.
7、点击Apply以保存运行配置,或者选择 Run 或者 Debug 按钮.
运行和调试一个Android应用程序
一旦你为你的应用程序设置了项目和运行配置,你可以根据下面的描述运行或者调试之。
从Eclipse主菜单,根据情况选择Run > Run 或者 Run > Debug子菜单,可运行或者调试激活的运行配置。
注意:激活的运行配置是在运行配置管理器中最后选择的一项运行配置。这不一定匹配你在Eclipse导航窗体中选择的应用程序(如果有的话)。
为了设置或者修改激活的运行配置,你要使用运行配置管理器。怎样访问运行配置管理器,要知道更多信息请阅读“Creating a Launch Configuration”(创建Android运行配置)。
运行或者调试应用程序触发以下操作:
1、如果还没有模拟器在运行,则启动模拟器。
2、如果比较最后的编译文件,程序代码已经改变,则重新编译,然后在模拟器中安装应用程序。
3、使用“Run”菜单启动应用程序。
4、使用“Debug”菜单启动应用程序进入“Wait for debugger”(等待调试)模式,然后打开调试试图,针对应用程序使用EclipseJAVA调试器。
在你在Eclipse上运行并调试Android应用程序前,你必须为之创建一个运行配置。一个运行配置指定运行项目,启动的活动,使用的模拟器选项,等等。
为了针对你的应用程序创建运行配置,根据你的Eclipse版本选择合适的步骤:
1、打开运行配置管理器
(1)3.3
选择Run > Open Run Dialog... 或者Run > Open Debug Dialog...
(2)3.4
选择Run > Run Configurations... 或者Run > Debug Configurations...
2、在左边的工程类型列表中,定位the Android Application条目,双击之或者右键> New, 以创建一个新的运行配置.
3、键入一个配置名称
4、在Android可选页, 浏览确定启动的项目和活动.
5、在Target可选页, 设置喜好的屏幕和网络特性,以及其他模拟器启动选项.
6、你也可以根据喜好在Common可选页设置额外的选项.
7、点击Apply以保存运行配置,或者选择 Run 或者 Debug 按钮.
运行和调试一个Android应用程序
一旦你为你的应用程序设置了项目和运行配置,你可以根据下面的描述运行或者调试之。
从Eclipse主菜单,根据情况选择Run > Run 或者 Run > Debug子菜单,可运行或者调试激活的运行配置。
注意:激活的运行配置是在运行配置管理器中最后选择的一项运行配置。这不一定匹配你在Eclipse导航窗体中选择的应用程序(如果有的话)。
为了设置或者修改激活的运行配置,你要使用运行配置管理器。怎样访问运行配置管理器,要知道更多信息请阅读“Creating a Launch Configuration”(创建Android运行配置)。
运行或者调试应用程序触发以下操作:
1、如果还没有模拟器在运行,则启动模拟器。
2、如果比较最后的编译文件,程序代码已经改变,则重新编译,然后在模拟器中安装应用程序。
3、使用“Run”菜单启动应用程序。
4、使用“Debug”菜单启动应用程序进入“Wait for debugger”(等待调试)模式,然后打开调试试图,针对应用程序使用EclipseJAVA调试器。
2008年11月11日星期二
Android开发步骤
在Eclipse上开发应用
为了在EclipseIDE上开发Andriod应用程序,你必须首先创建一个android项目,然后设置一个运行配置,你就可以编写、运行、调试你的应用程序。
以下提供的指导假设已经安装ADT插件。如果还没有安装ADT插件,你应该首先安装ADT插件。
创建Android项目
ADT插件提供一个新项目向导,该向导用于快速新建或者在已有代码基础上创建Eclipse项目,步骤如下:
1. 选择菜单 File > New > Project
2. 选择 Android > Android Project, 然后按Next
3. 在Contents下设置项目信息:
* 为了从新代码开始项目. 选择 Create new project in workspace
键入项目名称,基础包名称,单个活动类名称(以便创建一个JAVA桩文件),和用于你的应用程序的名字
* 为了从已存在代码开始项目,选择 Create project from existing source. 如果你打算基于SDK的简单例子创建并运行,你就使用这个选项. 例子应用程序存在于SDK的samples/ 目录
浏览包含已存在源代码的目录,然后点击OK. 如果该目录包含一个有效的manifest文件,那么ADT插件为你自动填充包、活动、应用程序名称
4. 选择 Finish.
ADT插件根据项目类型为你自动创建这些文件夹和文件:
* src/ 包含.java 活动文件
* res/ 包含资源.
* AndroidManifest.xml 项目的manifest文件
为了在EclipseIDE上开发Andriod应用程序,你必须首先创建一个android项目,然后设置一个运行配置,你就可以编写、运行、调试你的应用程序。
以下提供的指导假设已经安装ADT插件。如果还没有安装ADT插件,你应该首先安装ADT插件。
创建Android项目
ADT插件提供一个新项目向导,该向导用于快速新建或者在已有代码基础上创建Eclipse项目,步骤如下:
1. 选择菜单 File > New > Project
2. 选择 Android > Android Project, 然后按Next
3. 在Contents下设置项目信息:
* 为了从新代码开始项目. 选择 Create new project in workspace
键入项目名称,基础包名称,单个活动类名称(以便创建一个JAVA桩文件),和用于你的应用程序的名字
* 为了从已存在代码开始项目,选择 Create project from existing source. 如果你打算基于SDK的简单例子创建并运行,你就使用这个选项. 例子应用程序存在于SDK的samples/ 目录
浏览包含已存在源代码的目录,然后点击OK. 如果该目录包含一个有效的manifest文件,那么ADT插件为你自动填充包、活动、应用程序名称
4. 选择 Finish.
ADT插件根据项目类型为你自动创建这些文件夹和文件:
* src/ 包含.java 活动文件
* res/ 包含资源.
* AndroidManifest.xml 项目的manifest文件
2008年11月6日星期四
android架构
android架构:
applications:
一套核心应用程序,包括:电子邮件客户端、短信程序、日历、地图、浏览器、通讯录等,所有应用程序使用JAVA开发。
Application Framework
开发人员拥有在applications中使用的API的完全使用能力。应用程序架构用于简化组件的重用,一些应用程序开放他们的功能API,另外一些应用程序使用那些功能API,但是会受限于框架规定安全约束。这些机制允许用户更换组件。
应用程序架构是一系列的服务和系统,包括:
(1)Views
提供一套丰富的扩展的视图,用于构建应用程序,包括:列表、表格、文本框、按钮、甚至嵌入式浏览器
(2)Content Provider
用于应用程序访问其他应用程序的数据,比如通讯录,或者,共享自己的数据
(3)Resource Manager
提供访问非代码的资源机制,比如本地化字符、图形、布局文件
(4)Notification Manager
为应用程序提供在状态栏显示自定义警告信息的能力
(5)Activity Manager
管理应用程序的生命周期,提供后退导航机制
Libraries
提供一套C/C++库,以供Andriod系统不同组件调用,库的开放途径是通过Application Framework,下面列举核心库:
(1)System C Library
基于BSD实现的C标准库(libc),专门针对嵌入式linux设备调整
(2)Media Library
基于PacketVideo的OpenCORE,这个库支持多数公共的视频音频格式的播放、录制,以及静态图片格式,包括MPEG4 H.264 MP3 AAC AMR JPG PNG
(3)Surface Manager
管理显示子系统的入口,无缝组合用于多应用程序的2D、3D图形层
(4)LibWebCore
一个现代Web浏览器引擎,该引擎增强andriod浏览器和嵌入式Web视图
(5)SGL
基本的2D图形引擎
(6)3D Libraries
一个基于OpenGL ES 1.0API的实现,该库使用硬件加速或者内含的高度优化的3D光栅刷新软件
(7)FreeType
位图、矢量字体渲染
(8)SQLite
用于所有应用程序的、功能强大的、轻量级关系数据库引擎
Android Runtime
Android包含一套核心库,该库提供能在核心JAVA语言核心库中获得的大多数功能。每个Android程序都运行在他自己的进程中,即Dalvik虚拟机的自主实例。Dalvik编写目的是:让一个设备能有效运行在多个虚拟机上。使用Dalvik执行文件格式(.dex)的Dalvik虚拟机针对小内存占用而优化。这个虚拟机是基于注册机制的,运行的类使用JAVA语言编译器编译,该编译器使用内置的DX工具转换成.dex文件格式。Dalvik虚拟机依赖LINUX内核,该内核提供诸如线程、低级别内存管理的基本功能
Linux Kernel
Android 基于提供诸如安全、内存管理、进程管理、网络堆栈、驱动模型功能的LINUX2.6。该内核也起着在硬件和软件堆栈之间的抽象隔离层的作用。
applications:
一套核心应用程序,包括:电子邮件客户端、短信程序、日历、地图、浏览器、通讯录等,所有应用程序使用JAVA开发。
Application Framework
开发人员拥有在applications中使用的API的完全使用能力。应用程序架构用于简化组件的重用,一些应用程序开放他们的功能API,另外一些应用程序使用那些功能API,但是会受限于框架规定安全约束。这些机制允许用户更换组件。
应用程序架构是一系列的服务和系统,包括:
(1)Views
提供一套丰富的扩展的视图,用于构建应用程序,包括:列表、表格、文本框、按钮、甚至嵌入式浏览器
(2)Content Provider
用于应用程序访问其他应用程序的数据,比如通讯录,或者,共享自己的数据
(3)Resource Manager
提供访问非代码的资源机制,比如本地化字符、图形、布局文件
(4)Notification Manager
为应用程序提供在状态栏显示自定义警告信息的能力
(5)Activity Manager
管理应用程序的生命周期,提供后退导航机制
Libraries
提供一套C/C++库,以供Andriod系统不同组件调用,库的开放途径是通过Application Framework,下面列举核心库:
(1)System C Library
基于BSD实现的C标准库(libc),专门针对嵌入式linux设备调整
(2)Media Library
基于PacketVideo的OpenCORE,这个库支持多数公共的视频音频格式的播放、录制,以及静态图片格式,包括MPEG4 H.264 MP3 AAC AMR JPG PNG
(3)Surface Manager
管理显示子系统的入口,无缝组合用于多应用程序的2D、3D图形层
(4)LibWebCore
一个现代Web浏览器引擎,该引擎增强andriod浏览器和嵌入式Web视图
(5)SGL
基本的2D图形引擎
(6)3D Libraries
一个基于OpenGL ES 1.0API的实现,该库使用硬件加速或者内含的高度优化的3D光栅刷新软件
(7)FreeType
位图、矢量字体渲染
(8)SQLite
用于所有应用程序的、功能强大的、轻量级关系数据库引擎
Android Runtime
Android包含一套核心库,该库提供能在核心JAVA语言核心库中获得的大多数功能。每个Android程序都运行在他自己的进程中,即Dalvik虚拟机的自主实例。Dalvik编写目的是:让一个设备能有效运行在多个虚拟机上。使用Dalvik执行文件格式(.dex)的Dalvik虚拟机针对小内存占用而优化。这个虚拟机是基于注册机制的,运行的类使用JAVA语言编译器编译,该编译器使用内置的DX工具转换成.dex文件格式。Dalvik虚拟机依赖LINUX内核,该内核提供诸如线程、低级别内存管理的基本功能
Linux Kernel
Android 基于提供诸如安全、内存管理、进程管理、网络堆栈、驱动模型功能的LINUX2.6。该内核也起着在硬件和软件堆栈之间的抽象隔离层的作用。
2008年11月4日星期二
android是什么
android是什么?
是用于移动设备一组软件。
包括开放式操作系统、中间件、关键应用程序。
android SDK提供在android平台上使用JAVA语言开发应用程序的工具和API
android特点:
1、Application Framework提供组件的复用和替换
2、Dalvik virtual machine针对移动设备最优化
3、Integrated browser基于WebKit引擎
4、Optimized graphics强力提供自建2D图形库,3D图形基于OpenGL ES 1.0规范
5、SQLite用于结构化数据存储
6、Meddia Support用于通用视频音频,更提供现有的图形格式支持,包括MPEG4, H.264, MP3, AAC, AMR, JPG, PNG, GIF
7、GSM Telephony使用硬件支持
8、Bluetooth,EDGE,3G,and WIFI使用硬件支持
9、Camera,GPS,compass(指南针),and accelerometer(加速计)使用硬件支持
10、Rich development environment包括设备模拟器、调试工具、内存和性能分析、eclipse插件
是用于移动设备一组软件。
包括开放式操作系统、中间件、关键应用程序。
android SDK提供在android平台上使用JAVA语言开发应用程序的工具和API
android特点:
1、Application Framework提供组件的复用和替换
2、Dalvik virtual machine针对移动设备最优化
3、Integrated browser基于WebKit引擎
4、Optimized graphics强力提供自建2D图形库,3D图形基于OpenGL ES 1.0规范
5、SQLite用于结构化数据存储
6、Meddia Support用于通用视频音频,更提供现有的图形格式支持,包括MPEG4, H.264, MP3, AAC, AMR, JPG, PNG, GIF
7、GSM Telephony使用硬件支持
8、Bluetooth,EDGE,3G,and WIFI使用硬件支持
9、Camera,GPS,compass(指南针),and accelerometer(加速计)使用硬件支持
10、Rich development environment包括设备模拟器、调试工具、内存和性能分析、eclipse插件
2008年11月2日星期日
转:Linux高性能计算集群
1 集群
1.1 什么是集群
简单的说,集群(cluster)就是一组计算机,它们作为一个整体向用户提供一组网络资源。这些单个的计算机系统就是集群的节点(node)。一个理想的集群是,用户从来不会意识到集群系统底层的节点,在他/她们看来,集群是一个系统,而非多个计算机系统。并且集群系统的管理员可以随意增加和删改集群系统的节点。
1.2 为什么需要集群
集群并不是一个全新的概念,其实早在七十年代计算机厂商和研究机构就开始了对集群系统的研究和开发。由于主要用于科学工程计算,所以这些系统并不为大家所熟知。直到Linux集群的出现,集群的概念才得以广为传播。
对集群的研究起源于集群系统的良好的性能可扩展性(scalability)。提高CPU主频和总线带宽是最初提供计算机性能的主要手段。但是这一手段对系统性能的提供是有限的。接着人们通过增加CPU个数和内存容量来提高性能,于是出现了向量机,对称多处理机(SMP)等。但是当CPU的个数超过某一阈值,象SMP这些多处理机系统的可扩展性就变的极差。主要瓶颈在于CPU访问内存的带宽并不能随着CPU个数的增加而有效增长。与SMP相反,集群系统的性能随着CPU个数的增加几乎是线性变化的。图1显示了这中情况。
图1. 几种计算机系统的可扩展性

集群系统的优点并不仅在于此。下面列举了集群系统的主要优点:
- 高可扩展性:如上所述。
- 高可用性:集群中的一个节点失效,它的任务可以传递给其他节点。可以有效防止单点失效。
- 高性能:负载平衡集群允许系统同时接入更多的用户。
- 高性价比:可以采用廉价的符合工业标准的硬件构造高性能的系统。
1.2.1 集群系统的分类
虽然 根据集群系统的不同特征可以有多种分类方法,但是一般我们把集群系统分为两类:
- 高可用(High Availability)集群,简称HA集群。这类集群致力于提供高度可靠的服务。
- 高性能计算(High Perfermance Computing)集群,简称HPC集群。这类集群致力于提供单个计算机所不能提供的强大的计算能力。
2 高可用集群
2.1 什么是高可用性
计算机系统的可用性(availability)是通过系统的可靠性(reliability)和可维护性(maintainability)来度量的。工程上通常用平均无故障时间(MTTF)来度量系统的可靠性,用平均维修时间(MTTR)来度量系统的可维护性。于是可用性被定义为:
MTTF/(MTTF+MTTR)*100% |
业界根据可用性把计算机系统分为如下几类:
表1. 系统可用性分类
可用比例 (Percent Availability) | 年停机时间 (downtime/year) | 可用性分类 |
99.5 | 3.7天 | 常规系统(Conventional) |
99.9 | 8.8小时 | 可用系统(Available) |
99.99 | 52.6分钟 | 高可用系统(Highly Available) |
99.999 | 5.3分钟 | Fault Resilient |
99.9999 | 32秒 | Fault Tolerant |
对于关键业务,停机通常是灾难性的。因为停机带来的损失也是巨大的。下面的统计数字列举了不同类型企业应用系统停机所带来的损失。
表 2. 停机给企业带来的损失
应用系统 | 每分钟损失(美元) |
呼叫中心(Call Center) | 27000 |
企业资源计划(ERP)系统 | 13000 |
供应链管理(SCM)系统 | 11000 |
电子商务(eCommerce)系统 | 10000 |
客户服务(Customer Service Center)系统 | 27000 |
随着企业越来越依赖于信息技术,由于系统停机而带来的损失也越拉越大。
2.2 高可用集群
高可用集群就是采用集群技术来实现计算机系统的高可用性。高可用集群通常有两种工作方式:
- 容错系统:通常是主从服务器方式。从服务器检测主服务器的状态,当主服务工作正常时,从服务器并不提供服务。但是一旦主服务器失效,从服务器就开始代替主服务器向客户提供服务。
- 负载均衡系统:集群中所有的节点都处于活动状态,它们分摊系统的工作负载。一般Web服务器集群、数据库集群和应用服务器集群都属于这种类型。
关于高可用集群的讨论很多,这里就不进行深入的阐述了。
3 高性能计算集群
3.1 什么是高性能计算集群
简单的说,高性能计算(High-Performance Computing)是计算机科学的一个分支,它致力于开发超级计算机,研究并行算法和开发相关软件。高性能计算主要研究如下两类问题:
- 大规模科学问题,象天气预报、地形分析和生物制药等;
- 存储和处理海量数据,象数据挖掘、图象处理和基因测序;
顾名思义,高性能集群就是采用集群技术来研究高性能计算。
3.2 高性能计算分类
高性能计算的分类方法很多。这里从并行任务间的关系角度来对高性能计算分类。
3.2.1 高吞吐计算(High-throughput Computing)
有一类高性能计算,可以把它分成若干可以并行的子任务,而且各个子任务彼此间没有什么关联。象在家搜寻外星人( SETI@HOME -- Search for Extraterrestrial Intelligence at Home )就是这一类型应用。这一项目是利用Internet上的闲置的计算资源来搜寻外星人。SETI项目的服务器将一组数据和数据模式发给Internet上参加SETI的计算节点,计算节点在给定的数据上用给定的模式进行搜索,然后将搜索的结果发给服务器。服务器负责将从各个计算节点返回的数据汇集成完整的数据。因为这种类型应用的一个共同特征是在海量数据上搜索某些模式,所以把这类计算称为高吞吐计算。所谓的Internet计算都属于这一类。按照Flynn的分类,高吞吐计算属于SIMD(Single Instruction/Multiple Data)的范畴。
3.2.2 分布计算(Distributed Computing)
另一类计算刚好和高吞吐计算相反,它们虽然可以给分成若干并行的子任务,但是子任务间联系很紧密,需要大量的数据交换。按照Flynn的分类,分布式的高性能计算属于MIMD(Multiple Instruction/Multiple Data)的范畴。
3.3 Linux高性能集群系统
当论及Linux高性能集群时,许多人的第一反映就是Beowulf。起初,Beowulf只是一个著名的科学计算集群系统。以后的很多集群都采用Beowulf类似的架构,所以,实际上,现在Beowulf已经成为一类广为接受的高性能集群的类型。尽管名称各异,很多集群系统都是Beowulf集群的衍生物。当然也存在有别于Beowulf的集群系统,COW和Mosix就是另两类著名的集群系统。
3.3.1 Beowulf集群
简单的说,Beowulf是一种能够将多台计算机用于并行计算的体系结构。通常Beowulf系统由通过以太网或其他网络连接的多个计算节点和管理节点构成。管理节点控制整个集群系统,同时为计算节点提供文件服务和对外的网络连接。它使用的是常见的硬件设备,象普通PC、以太网卡和集线器。它很少使用特别定制的硬件和特殊的设备。Beowulf集群的软件也是随处可见的,象Linux、PVM和MPI。
3.3.2 Beowulf集群和COW集群
象Beowulf一样,COW(Cluster Of Workstation)也是由最常见的硬件设备和软件系统搭建而成。通常也是由一个控制节点和多个计算节点构成。COW和Beowulf的主要区别在于:
- COW中的计算节点主要都是闲置的计算资源,如办公室中的桌面工作站,它们就是普通的PC,采用普通的局域网进行连接。因为这些计算节点白天会作为工作站使用,所以主要的集群计算发生在晚上和周末等空闲时间。而Beowulf中的计算节点都是专职于并行计算,并且进行了性能优化。它们采用高速网(Myrinet或Giganet)上的消息传递(PVM或MPI)进行进程间通信(IPC)。
- 因为COW中的计算节点主要的目的是桌面应用,所以它们都具有显示器、键盘和鼠标等外设。而Beowulf的计算节点通常没有这些外设,对这些计算节点的访问通常是在管理节点上通过网络或串口线实现的。
- 因为连接COW中计算节点的通常是普通的局域网,所以COW上的高性能应用通常是象SETI@HOME 这样的SIMD的高吞吐计算。而Beowulf无论从硬件、网络和软件上都对需要频繁交换数据的MIMD应用做了特别的优化。
3.3.3 Mosix集群
实际上把Mosix集群放在高性能集群这一节是相当牵强的,但是和Beowulf等其他集群相比, Mosix集群确实是种非常特别的集群, 它致力于在Linux系统上实现集群系统的单一系统映象SSI(Single System Image)。Mosix集群将网络上运行Linux的计算机连接成一个集群系统。系统自动均衡节点间的负载。因为Mosix是在Linux系统内核中实现的集群,所以用户态的应用程序不需要任何修改就可以在Mosix集群上运行。通常用户很少会注意到Linux和Mosix的差别。对于他来说,Mosix集群就是运行Linux的一台PC。尽管现在存在着不少的问题,Mosix始终是引人注目的集群系统。
2008年10月28日星期二
系统工程师综合面试
1、系统工程师职位说明书和职责要求:
(1)项目的需求管理工作
需求管理工作属于SE的主要工作,包括原始需求的分析(原始需求一般来源于售前和售后工程师,原则上SE不直接参与原始需求的收集),通过正常的渠道流程保持与用户的沟通,确认最终的用户需求,形成需求规格说明书,并对需求的开发实现进行跟踪验证,达到公司及事业部的各项考核指标。
(2)协助项目经理做好项目的管理工作,侧重于研发管理方面的工作,包括:
a)指定产品的研发技术规划,SE是项目的技术专家,在项目的技术体制、总体架构上起决定作用。作为CCB的重要成员,SE需要对技术评审的方案进行严格把关;
b)做好项目各个基线规划,作出明确的版本功能计划和进度要求,能够对基线进行严密控制;
c)做好产品的技术策划工作,对于一些关键和组网比较特殊,需求变动较大的方案,SE通过参与评审和组织审核等手段,进行技术上的把关和控制,使项目的实施具有可操作性;
d)定期更新和提供产品的技术资料,包括业务规范、信令流程等,对相关技术资料的正确性和及时性负责。
(3)对本项目的软件设计缺陷质量问题负责,通过不断的改进措施,提高产品自身的质量水平。
(4)参与项目计划的制定,负责相关研发技术的计划审核。
(5)协助项目线对重要的技术难点进行技术攻关。
(6)新技术的跟踪,产品的持续发展,需要持续的技术动力,SE在新技术的跟踪和引入上负有主要的职责。
2、系统工程师综合素质考评要素和问题
(1)沟通能力
a)您最近工作过的单位的基本情况是什么?请简单产品和市场情况。
b)您目前工作有哪些主要内容?
c)请介绍你的成长过程。
(2)分析能力
a)您在您应聘岗位上的工作经历有哪些不足?有什么改进措施?如果您到我司工作,那么您还有没有更好的改进措施?
b)北京移动提出一个需求:要求在奥运期间提供实时电话查询奥运赛事的功能,你怎么分析这个需求?
c)您刚才介绍了你的成长过程,您认为您的成长关键点是什么?为什么?
(3)自信心
a)您对幸福怎么看的?李嘉诚和一个儿孙满堂整天乐呵呵的80岁老人,哪个更幸福?
b)你认为您能胜任这份工作吗?凭什么?
(4)计划控制
a)举例说明您曾经做过的一个成功计划及实施过程。
b)假如您今天晚会有一场非常重要的约会,说说您打算怎么去应对?是随机应变?还是事先做好策划?
c)您对下属布置的任务时着重要求什么?完成任务的时间给得宽松还是紧凑?
(5)决策
a)我最近看上了苹果的IPHONE手机,要5500元,但是月收入只有5000,如果您是我,你会怎么办?
b)上司突然让你负责一个紧急项目,需要立即决策,您立即着手收集信息,到了您认为您掌握了4/3的情况的时候,您会怎么做?
c)市场部提出一个需求,要求1个月完成,但是实际上根据现有资源情况至少需要3个月,应该怎么办?
(1)项目的需求管理工作
需求管理工作属于SE的主要工作,包括原始需求的分析(原始需求一般来源于售前和售后工程师,原则上SE不直接参与原始需求的收集),通过正常的渠道流程保持与用户的沟通,确认最终的用户需求,形成需求规格说明书,并对需求的开发实现进行跟踪验证,达到公司及事业部的各项考核指标。
(2)协助项目经理做好项目的管理工作,侧重于研发管理方面的工作,包括:
a)指定产品的研发技术规划,SE是项目的技术专家,在项目的技术体制、总体架构上起决定作用。作为CCB的重要成员,SE需要对技术评审的方案进行严格把关;
b)做好项目各个基线规划,作出明确的版本功能计划和进度要求,能够对基线进行严密控制;
c)做好产品的技术策划工作,对于一些关键和组网比较特殊,需求变动较大的方案,SE通过参与评审和组织审核等手段,进行技术上的把关和控制,使项目的实施具有可操作性;
d)定期更新和提供产品的技术资料,包括业务规范、信令流程等,对相关技术资料的正确性和及时性负责。
(3)对本项目的软件设计缺陷质量问题负责,通过不断的改进措施,提高产品自身的质量水平。
(4)参与项目计划的制定,负责相关研发技术的计划审核。
(5)协助项目线对重要的技术难点进行技术攻关。
(6)新技术的跟踪,产品的持续发展,需要持续的技术动力,SE在新技术的跟踪和引入上负有主要的职责。
2、系统工程师综合素质考评要素和问题
(1)沟通能力
a)您最近工作过的单位的基本情况是什么?请简单产品和市场情况。
b)您目前工作有哪些主要内容?
c)请介绍你的成长过程。
(2)分析能力
a)您在您应聘岗位上的工作经历有哪些不足?有什么改进措施?如果您到我司工作,那么您还有没有更好的改进措施?
b)北京移动提出一个需求:要求在奥运期间提供实时电话查询奥运赛事的功能,你怎么分析这个需求?
c)您刚才介绍了你的成长过程,您认为您的成长关键点是什么?为什么?
(3)自信心
a)您对幸福怎么看的?李嘉诚和一个儿孙满堂整天乐呵呵的80岁老人,哪个更幸福?
b)你认为您能胜任这份工作吗?凭什么?
(4)计划控制
a)举例说明您曾经做过的一个成功计划及实施过程。
b)假如您今天晚会有一场非常重要的约会,说说您打算怎么去应对?是随机应变?还是事先做好策划?
c)您对下属布置的任务时着重要求什么?完成任务的时间给得宽松还是紧凑?
(5)决策
a)我最近看上了苹果的IPHONE手机,要5500元,但是月收入只有5000,如果您是我,你会怎么办?
b)上司突然让你负责一个紧急项目,需要立即决策,您立即着手收集信息,到了您认为您掌握了4/3的情况的时候,您会怎么做?
c)市场部提出一个需求,要求1个月完成,但是实际上根据现有资源情况至少需要3个月,应该怎么办?
2008年2月15日星期五
了解Erlang语言
我了解Erlang语言最早是由erlangC公式知道的,当时是查找排班系统用到的erlang c公式,查出了Erlang语言,顺便看到这是一个用于并发系统的语言,但是当时没有十分在意。
今天无聊上itpub网站看到很多人推崇这种语言,说是一种智能语言,类似于LISP,有望替代JAVA,最初由爱立信发明用于电信领域,不知道是不是具体用于电话排队。据说有好多优点:并发、分布式、健壮、软实时性、热代码升级、递增式代码装载、高效外部接口。
找到官方网站,看了一点手册和入门指导的内容,准备下载编译环境,可以有54兆,太大,放弃了。不得已,阅读几则itpub的erlang语言入门文档,才明白该语言是函数为基础,所谓函数式编程可能就是这种风格,函数式编程起源于50年前的lisp,lisp继承者是Common Lisp,人工智能方面的专家用得多,.net的F#和JAVA的javaFX也是函数式变成语言。
下载手册,提取关于命令解析器、数组、元组、常量、模块、函数、内建函数、判断语句、选择语句、变量、终端输出、分布式编程、高端函数、进程、消息传递的内容。以此强化自己对该语言的理解。
1、The Erlang Shell
外壳,命令解析器;和unix的众多shell、windows的cmd类似,可以在其中直接写erlang语言代码并运行他们看他们的执行结果,在unix中执行erl启动命令解析器,windows中运行安装后的图标。在命令解析器中一个典型的效果是输入数字和运算符能直接输出计算结果,当作计算器使用。
2、Modules
Erlang代码被划分为模块,一个模块由一系列的属性和函数申明组成,每句以英文句号结束。模块属性定义模块的一定的特征,包括一个标记和一个值,如:-tag(value).。tag必须是一个原子,而value必须是一串文字。模块属性包括:
-module(Module).模块声明,定义模块的名称,Module应该和文件(扩展名为erl)名称相同。是唯一的必须属性。
-export(Functions).引出函数,声明模块内部的函数,表示模块外部可见,可由其他模块引用或者直接在shell调用
-import(Module,Functions).引入指定模块的函数。被引入的函数能被本模块调用,相当于本地函数
-compile(Options).编译选项。
-vsn(Vsn).模块版本。
3、Functions
函数声明:由分号分隔的一到多个函数子句构成,结尾是英文句号,比如:
Name(Pattern1,...) [when GuardSeq] ->
Body;
其中Name是函数名称,Pattern1等是参数,GuardSeq是可选的保护序列,Body是函数体,->用于分隔函数体。函数体由一到多个表达式组成,表达式之间以逗号隔开。
函数执行:前提是使用-export(Functions).引出函数,执行格式为Module.Function(Pattern),其中Module为模块名称,Function为函数名称,Pattern为参数列表。
尾递归函数:函数体的最后一句调用自身;尾递归不消耗系统资源,所以尾递归可无限调用,比如
loop(N) ->
io:format("~w~n", [N]),
loop(N+1).
内建函数:BIFs,由C代码实现,完成erlang代码困难或者不能的事情。大多数内建函数属于erlang模块,也有少量属于lists/ets等模块。属于erlang模块的BIFs自动引入,而不需要显示import。
4、Atoms
原子,有人译作常量,是erlang语言的一种数据类型,以小写字母组成,只是表示简单的名称,没有其他意义,和有值的变量不同。
5、Tuples
元组,把关联的东东绑定在一块便于理解,使用大括号标记,元组的元素之间以逗号隔开,比如:{chongqing,{c,12}},表示重庆的温度为12摄氏度,其中12和原子c是一个元组,然后和原子chongqing在组合成一个元组。
(待续)
今天无聊上itpub网站看到很多人推崇这种语言,说是一种智能语言,类似于LISP,有望替代JAVA,最初由爱立信发明用于电信领域,不知道是不是具体用于电话排队。据说有好多优点:并发、分布式、健壮、软实时性、热代码升级、递增式代码装载、高效外部接口。
找到官方网站,看了一点手册和入门指导的内容,准备下载编译环境,可以有54兆,太大,放弃了。不得已,阅读几则itpub的erlang语言入门文档,才明白该语言是函数为基础,所谓函数式编程可能就是这种风格,函数式编程起源于50年前的lisp,lisp继承者是Common Lisp,人工智能方面的专家用得多,.net的F#和JAVA的javaFX也是函数式变成语言。
下载手册,提取关于命令解析器、数组、元组、常量、模块、函数、内建函数、判断语句、选择语句、变量、终端输出、分布式编程、高端函数、进程、消息传递的内容。以此强化自己对该语言的理解。
1、The Erlang Shell
外壳,命令解析器;和unix的众多shell、windows的cmd类似,可以在其中直接写erlang语言代码并运行他们看他们的执行结果,在unix中执行erl启动命令解析器,windows中运行安装后的图标。在命令解析器中一个典型的效果是输入数字和运算符能直接输出计算结果,当作计算器使用。
2、Modules
Erlang代码被划分为模块,一个模块由一系列的属性和函数申明组成,每句以英文句号结束。模块属性定义模块的一定的特征,包括一个标记和一个值,如:-tag(value).。tag必须是一个原子,而value必须是一串文字。模块属性包括:
-module(Module).模块声明,定义模块的名称,Module应该和文件(扩展名为erl)名称相同。是唯一的必须属性。
-export(Functions).引出函数,声明模块内部的函数,表示模块外部可见,可由其他模块引用或者直接在shell调用
-import(Module,Functions).引入指定模块的函数。被引入的函数能被本模块调用,相当于本地函数
-compile(Options).编译选项。
-vsn(Vsn).模块版本。
3、Functions
函数声明:由分号分隔的一到多个函数子句构成,结尾是英文句号,比如:
Name(Pattern1,...) [when GuardSeq] ->
Body;
其中Name是函数名称,Pattern1等是参数,GuardSeq是可选的保护序列,Body是函数体,->用于分隔函数体。函数体由一到多个表达式组成,表达式之间以逗号隔开。
函数执行:前提是使用-export(Functions).引出函数,执行格式为Module.Function(Pattern),其中Module为模块名称,Function为函数名称,Pattern为参数列表。
尾递归函数:函数体的最后一句调用自身;尾递归不消耗系统资源,所以尾递归可无限调用,比如
loop(N) ->
io:format("~w~n", [N]),
loop(N+1).
内建函数:BIFs,由C代码实现,完成erlang代码困难或者不能的事情。大多数内建函数属于erlang模块,也有少量属于lists/ets等模块。属于erlang模块的BIFs自动引入,而不需要显示import。
4、Atoms
原子,有人译作常量,是erlang语言的一种数据类型,以小写字母组成,只是表示简单的名称,没有其他意义,和有值的变量不同。
5、Tuples
元组,把关联的东东绑定在一块便于理解,使用大括号标记,元组的元素之间以逗号隔开,比如:{chongqing,{c,12}},表示重庆的温度为12摄氏度,其中12和原子c是一个元组,然后和原子chongqing在组合成一个元组。
(待续)
2008年1月29日星期二
到广发证券办理股票转托管
昨天公司停电,回到家,老婆才让到广发证券办理转托管手续,说实在不想出去,温度为0到3度,天还下着雪。
和孩子一块儿去的,打的到石桥铺。石桥铺的雪更大,到证券公司看到股市也在下雪——降得厉害。这些都不管紧要了,反正只有200股深发展,还是办转托管手续重要,但手续确实麻烦,帐户5年了没有动了,密码也忘记了,还需要首先清空密码,然后查询账户的股票,办理第三方银行资金托管...
到家里才仔细清理帐户上的股票,深圳帐户有220股深发展,22股深发SFC1,22股深发SFC2,居然多了两只,后来才搞清楚深发SFC1、深发SFC2是购股权证,深发SFC1已经在12月26日行权,过期了相当于损失(42-19)×22=506元,这才后悔一直没有管股票帐户,老婆提醒若深发展在12月份卖出,有42元,现在只有34元,相当于损失(42-34)×220=1760元。老婆进而假设:如果我在两年前股市开始涨时候关注股市,至少也有10万元的收入了,她还举例他们单位有几个买高级车的就是股市赚的。
其实,我对股市有股莫名的恐惧,记得是2003年2月份,好像也很冷了,我一天上午买了两只股票,一只深发展,17.2元买入,另外一只禄碱化工,22.5元买入;结果几天后受政策影响大盘从3000多点跳水,一直到以后的1000点,看我的股票买入价格就是曲线的顶点。后来我卖出了其他股票,就留下了深发展和禄碱化工,具有象征意义,同时,决心不再炒股了。
和孩子一块儿去的,打的到石桥铺。石桥铺的雪更大,到证券公司看到股市也在下雪——降得厉害。这些都不管紧要了,反正只有200股深发展,还是办转托管手续重要,但手续确实麻烦,帐户5年了没有动了,密码也忘记了,还需要首先清空密码,然后查询账户的股票,办理第三方银行资金托管...
到家里才仔细清理帐户上的股票,深圳帐户有220股深发展,22股深发SFC1,22股深发SFC2,居然多了两只,后来才搞清楚深发SFC1、深发SFC2是购股权证,深发SFC1已经在12月26日行权,过期了相当于损失(42-19)×22=506元,这才后悔一直没有管股票帐户,老婆提醒若深发展在12月份卖出,有42元,现在只有34元,相当于损失(42-34)×220=1760元。老婆进而假设:如果我在两年前股市开始涨时候关注股市,至少也有10万元的收入了,她还举例他们单位有几个买高级车的就是股市赚的。
其实,我对股市有股莫名的恐惧,记得是2003年2月份,好像也很冷了,我一天上午买了两只股票,一只深发展,17.2元买入,另外一只禄碱化工,22.5元买入;结果几天后受政策影响大盘从3000多点跳水,一直到以后的1000点,看我的股票买入价格就是曲线的顶点。后来我卖出了其他股票,就留下了深发展和禄碱化工,具有象征意义,同时,决心不再炒股了。
2008年1月26日星期六
如何让孩子养成好习惯
小鱼儿一直没有养成刷牙的习惯,可能是刷牙比较困难;卫生习惯也不好,经常吃手指,还经常跪在地上,打骂都不能纠正。
昨天,我教他洗脸后,他自己洗脚,后来我发现他居然不穿拖鞋就从卫生间出来,踮起脚走路,我大吼,让他重新写,隔了好久,他还在洗脚,明显对抗我。再次吼他,他才洗完上床。我发现他贴身裤子洗脚时湿了,让他换,他不换,还哭,我只好规定他1分钟换好,不然,打他。我离开他的房间,等了一分钟,期间他一直哭,我以为他没有换,进去一看已经换了,给他盖好,他还在哭,再离开他的房间,等他住口,等了5分钟还哭,我想打他,老婆吩咐不理他,结果等了20分钟,他断断续续不哭了,是睡着了。
哭时不理他也许是让孩子养成好习惯的一种方法。
昨天,我教他洗脸后,他自己洗脚,后来我发现他居然不穿拖鞋就从卫生间出来,踮起脚走路,我大吼,让他重新写,隔了好久,他还在洗脚,明显对抗我。再次吼他,他才洗完上床。我发现他贴身裤子洗脚时湿了,让他换,他不换,还哭,我只好规定他1分钟换好,不然,打他。我离开他的房间,等了一分钟,期间他一直哭,我以为他没有换,进去一看已经换了,给他盖好,他还在哭,再离开他的房间,等他住口,等了5分钟还哭,我想打他,老婆吩咐不理他,结果等了20分钟,他断断续续不哭了,是睡着了。
哭时不理他也许是让孩子养成好习惯的一种方法。
2008年1月25日星期五
跨站点的网页子窗口和网页父窗口的不能直接交互
网页子窗口和网页父窗口的交互包括四种情况:
1、应用实例内的网页子窗口和网页父窗口的交互
2、同站点不同端口的网页子窗口和网页父窗口的交互
3、不同站点的网页子窗口和网页父窗口的交互
编写父窗口test1.html,JAVASCRIPT代码如下:
html
head
script language="JavaScript"
function getname(str) {
alert("哈罗! "+ str+"!");
}
/script
/head
body
a href="test2.html" onmouseover="window.status='Just another stupid link...'; return true"test url
/a
Please enter your name:
form
input type="text" name="name" onblur="getname(this.value)" value=""
/form
form name="form1"
input name="txt"
input type="button" onclick="window.open('http://192.168.1.2:7001/test/test2.html')" value="opennewwindow"
/form
/body
/html
编写子窗口test2.html,JAVASCRIPT代码如下:
html
head
script language="JavaScript"
function pushbutton() {
alert("嗨! 你好");
}
/script
/head
body
form
input type="button" name="Button1" value="Push me" onclick="pushbutton()"
/form
form name="form2"
input name="txt2"
input type="button" onclick="opener.document.form1.txt.value=txt2.value" value="passtoparent"
/form
/body
/html
准备两个WEB SERVER,都使用TOMCAT4.1,第一个地址为http://192.168.1.1:8080/test/,第二个地址为http://192.168.1.2:7001/test/,另外在第二个服务器准备第二端口,地址为http://192.168.1.2:8080/test/,如下是实验操作和结果:
1、把test1.html和test2.html都放到//192.168.1.1:8080/test/,并test1.html文件中的“http://192.168.1.2:7001”修改为“//192.168.1.1:8080/test/”,实验结果是子窗口能返回结果到父窗口;
2、把test1.html都放到//192.168.1.2:8080/test/,把test2.html都放到//192.168.1.2:7001/test/,实验结果是子窗口能返回结果到父窗口;
3、把test1.html都放到http://192.168.1.1:8080/test/,把test2.html都放到//192.168.1.2:7001/test/,实验结果是子窗口不能返回结果到父窗口.
1、应用实例内的网页子窗口和网页父窗口的交互
2、同站点不同端口的网页子窗口和网页父窗口的交互
3、不同站点的网页子窗口和网页父窗口的交互
编写父窗口test1.html,JAVASCRIPT代码如下:
html
head
script language="JavaScript"
function getname(str) {
alert("哈罗! "+ str+"!");
}
/script
/head
body
a href="test2.html" onmouseover="window.status='Just another stupid link...'; return true"test url
/a
Please enter your name:
form
input type="text" name="name" onblur="getname(this.value)" value=""
/form
form name="form1"
input name="txt"
input type="button" onclick="window.open('http://192.168.1.2:7001/test/test2.html')" value="opennewwindow"
/form
/body
/html
编写子窗口test2.html,JAVASCRIPT代码如下:
html
head
script language="JavaScript"
function pushbutton() {
alert("嗨! 你好");
}
/script
/head
body
form
input type="button" name="Button1" value="Push me" onclick="pushbutton()"
/form
form name="form2"
input name="txt2"
input type="button" onclick="opener.document.form1.txt.value=txt2.value" value="passtoparent"
/form
/body
/html
准备两个WEB SERVER,都使用TOMCAT4.1,第一个地址为http://192.168.1.1:8080/test/,第二个地址为http://192.168.1.2:7001/test/,另外在第二个服务器准备第二端口,地址为http://192.168.1.2:8080/test/,如下是实验操作和结果:
1、把test1.html和test2.html都放到//192.168.1.1:8080/test/,并test1.html文件中的“http://192.168.1.2:7001”修改为“//192.168.1.1:8080/test/”,实验结果是子窗口能返回结果到父窗口;
2、把test1.html都放到//192.168.1.2:8080/test/,把test2.html都放到//192.168.1.2:7001/test/,实验结果是子窗口能返回结果到父窗口;
3、把test1.html都放到http://192.168.1.1:8080/test/,把test2.html都放到//192.168.1.2:7001/test/,实验结果是子窗口不能返回结果到父窗口.
2008年1月24日星期四
看了iphone后的遐想
看了大名鼎鼎的iphone,一个追求时尚的同事操在手上,像一块很薄的长方糕点,特别是屏幕上显示高清晰度的食品照片的时候更像。
更令人惊奇的是,看起来它只是一个屏幕,没有按键,同事拿住它在上面摸了几下就出来了一张风景照,再摸就换了一张。
当同事打开百度搜索后,我拿在手中,很轻,然后感到似乎我在使用我的X60在上网,网页上的条目显示很清晰,字体也很大,阅读起来一点也不吃力。
于是,我似乎想到我不久就会抛弃我的X60,而使用iphone,我办公就用它,写文档依然使用word,但是,不再敲键盘了,使用手指在花哨的扑克背面画着文字和图形;想听音乐的时候我就在某个角落点两下;想炒股的时候调出行情软件,分析一番,又在某处点两下,就出来一个交易软件;想在网上买东西更容易了,调出网页只需要再摸几下就行了;甚至,我在开网店,有客户了,iphone说“客户找你”,于是拿起iphone,使用即时通讯工具软件,看到客户头像,她在说她的需求,沟通后,我找到物流公司,物流公司不到5分钟就上门取货,准备运给刚才交谈的客户。
更令人惊奇的是,看起来它只是一个屏幕,没有按键,同事拿住它在上面摸了几下就出来了一张风景照,再摸就换了一张。
当同事打开百度搜索后,我拿在手中,很轻,然后感到似乎我在使用我的X60在上网,网页上的条目显示很清晰,字体也很大,阅读起来一点也不吃力。
于是,我似乎想到我不久就会抛弃我的X60,而使用iphone,我办公就用它,写文档依然使用word,但是,不再敲键盘了,使用手指在花哨的扑克背面画着文字和图形;想听音乐的时候我就在某个角落点两下;想炒股的时候调出行情软件,分析一番,又在某处点两下,就出来一个交易软件;想在网上买东西更容易了,调出网页只需要再摸几下就行了;甚至,我在开网店,有客户了,iphone说“客户找你”,于是拿起iphone,使用即时通讯工具软件,看到客户头像,她在说她的需求,沟通后,我找到物流公司,物流公司不到5分钟就上门取货,准备运给刚才交谈的客户。
2008年1月23日星期三
创业要面向因特网
前天看到C2C在2006年的交易金额达到600亿,预计今天有100%增长,网民开始正式接受网购。
我感到自己有一股强烈的创业冲动,初略想想,应该首先考虑以下三个方向:
1、饰品网店
载体平台选用taobao或者易趣,内容为饰品,比如精致打火机、瑞士军刀。
自认为开网店需注意几个方面的事情;一是采购,采购饰品通过阿里巴巴较贵,到产家直接购买较好;二是宣传,主要是互联网上的宣传,打广告,上搜索引擎,门户美化都很重要;三是物流,物流是客户直接感受的重要环节,很多人对网购映像不好主要就是因为物流服务不好,还容易损坏东西。
2、垂直社区
社会交往网络很热了,这是流行趋势。大二全的社交网络比不多别人,而且myspace等有先发优势,既然如此,就搞小社区,针对某类人群,比如登山爱好者。
3、生活小知识
维基百科也很热了,同理,我考虑针对生活小知识,主要是健康、饮食、衣着,人气上升可能很快,毕竟人人关心,有人气就加入广告联盟,然后考虑其他盈利方式。
我感到自己有一股强烈的创业冲动,初略想想,应该首先考虑以下三个方向:
1、饰品网店
载体平台选用taobao或者易趣,内容为饰品,比如精致打火机、瑞士军刀。
自认为开网店需注意几个方面的事情;一是采购,采购饰品通过阿里巴巴较贵,到产家直接购买较好;二是宣传,主要是互联网上的宣传,打广告,上搜索引擎,门户美化都很重要;三是物流,物流是客户直接感受的重要环节,很多人对网购映像不好主要就是因为物流服务不好,还容易损坏东西。
2、垂直社区
社会交往网络很热了,这是流行趋势。大二全的社交网络比不多别人,而且myspace等有先发优势,既然如此,就搞小社区,针对某类人群,比如登山爱好者。
3、生活小知识
维基百科也很热了,同理,我考虑针对生活小知识,主要是健康、饮食、衣着,人气上升可能很快,毕竟人人关心,有人气就加入广告联盟,然后考虑其他盈利方式。
2008年1月20日星期日
转:排班系统的需求
排班系统的需求源自于运营规模扩大和便于座席代表管理的公平与公开,目的在于提升运营效能,提高客户服务满意度和提高座席代表工作满意度。在呼叫中心规模未达到50人之前,一定具备简单的班表,工作表单由人工排定,如果有换班或是优先选择的状况,都是经过班长和座席代表沟通协调之后,手工调整班表。至于是否公平,是否符合工作量或服务水平,答案仅仅存在于负责排班表的班长或是专责人员的脑海中,这在人员和绩效管理上容易出现问题。
排班系统并不是魔术盒,它不会自动告诉你该怎么设定不同时段的班别,每个班别又该有多少人,它只是一个工具,因为呼叫中心都是从小规模开始慢慢增长,经理人对于如何管理座席代表以及能够监控服务水平有其一定的经验累积,当规模扩大到50人以上的时候,已经不是做什么(WHAT)的问题,而是如何快速做好(HOW FAST)的问题。
排班系统并不是魔术盒,它不会自动告诉你该怎么设定不同时段的班别,每个班别又该有多少人,它只是一个工具,因为呼叫中心都是从小规模开始慢慢增长,经理人对于如何管理座席代表以及能够监控服务水平有其一定的经验累积,当规模扩大到50人以上的时候,已经不是做什么(WHAT)的问题,而是如何快速做好(HOW FAST)的问题。
订阅:
博文 (Atom)