|
|
|
|
|
|
|
|
cuihai | 2011-03-03 12:15 |
三、开发工具箱 Android 设计哲学 即使平台之间有很大的不同,但是如何利用API 创建应用程序的学习过程是大同小异的。一般来说,有两个步骤:首先,应该知道怎么用API 实现你的功能。其次,要了解平台间的细微差别。换句话说,首先你应该学会如何创建应用程序(了解应用程序的基本结构等),然后就要学会根据具体情况实现这个应用程序。 相比而言,第二阶段(学习使用正确的方法来实现应用程序)通常需要很长一段时间,在这个过程中你会不断地写代码,犯错误,然后从错误中吸取教训。显然,这不是一个有效的学习方法,本小节和下面的一些连接针对这向你伸出援助之手,教你怎么学习创建你的应用程序。 在此之前,先讲一个要点:成功的应用程序往往提供一个突出的用户体验。当Android团队构建了一个有着健壮核心的系统时,大多数的用户体验将来源于用户和应用程序之间的的交互。因此,我们鼓励你们花时间去构建应用程序优秀的用户体验。 显著的用户体验体现在三个核心特征上:1、快速;2、响应;3、无缝。当然,自从计算机出现以后,每一个平台都曾经有过类似的三种性质。尽管如此,每个平台实现这些特性的方式也有所不同;下面将会简单的介绍在Android 平台下面你的应用程序将如何达到这些要求。 快速(Fast) Android 程序执行应该是很快的。当然,准确来说它的程序应该执行的很有效率(有效率才会快)。在目前的计算机世界里哟这样一个倾向:假设摩尔定律能够最终解决我们所有的问题。当这种倾向遇到嵌入式应用程序的时候,摩尔定律就变得有些复杂了。 与桌面和服务应用程序不一样,摩尔定律在移动设备应用程序上面不是真正适用的。摩尔定律实际上是关于电子晶体管集成密度的,它原本的含义是:随着时间的流逝,在给定的电路尺寸上面可以集成更多的电路。对于桌面和服务应用陈旭来说,它的含义是: 你可以打包更多的“速度”在一个大小差不多的芯片上面,速度的提高,其他相应的一些性能也会显著的提高。对以像手机这样的嵌入式应用程序来说,相反的,使用摩尔定律是为了使芯片变得更小。这样,随着芯片密度的提高,相同功能的芯片会变得越来越小,功耗会越来越低,从而使手机做的越来越小,电池的持续的时间越来越长。这样就导致手持嵌入式设备相对于桌面系统来说正在以一种比较慢二实际的速度在增涨。因而,对于嵌入式设备来说,摩尔定律就意味着更多的功能和更少的功耗,速度只是次要的。这就是我们要写出高效代码的原因:你不能天真的认为电话在速度上面的增涨速度和桌面、服务应用程序是一样的。一般来说,高效的代码意味着最小的内存占用,意味着紧凑的风格,意味着避免了因为某种语言和编码习惯对性能的影响。我们用面向对象这个概念来理解,大部分这样的工作都是在方法层面上,与实际的代码,循环等等相类似。 在 “编写高效Android” 一文中,我们会对此做详细的介绍。 响应(Responsive) 我们有可能能够编写赢得世界上所有的性能测试的代码,但是用户使用起来会感到很恼火,这是因为应用程序没有足够的响应性——让人感觉反映迟钝,在关键的时刻失灵,或者处理输入太慢。在Android 平台下,那些响应性不够的应用程序会经常弹出"Application Not Responding" (ANR)这样的致命消息。 通常,这会在应用程序不能响应用户的输入的情况下发生。例如,你的应用程序在一些I/O 操作上(如网络接口调用)阻塞,这是主线程将不会处理用户的输入事件,一段时间之后应用系统就会认为你的程序挂起了,就会给一个选项给用户询问是否结束它。同样的,如果你的应用程序花费很多时间去构建内存中的一个结构、或者计算游戏的下一步,这时系统同样会认为程序已经挂起。当碰到上面情况的时候,要确保计算的高效性,但是即使是最高效的代码也需要花费时间。 在上面两个例子中,问题的解决方案是建立一个子线程来处理大部分的工作,这样就能保证你的主线程(响应用户界面事件)一直运行,这样防止系统认为你的程序已经僵化。因为这种线程的实现一般在“类”(CLASS)这个层次上,你可以把响应当成是类的问题来处理(这里,可以和方法层次描述的基本性能相比较)。 这里只是一个简单的介绍,在 “构建响应Android 应用程序” 一文中对于应用程序的响应性有详细的介绍。 无缝性(Seamless) 即使是你的应用程序执行很快,并且具有很高的响应性,它仍然有可能让用户苦恼。一个常见的例子是后台进程(比如Android 的 Service 和 BroadcastReceiver)对某些事件可能会突然弹出一个UI 响应。这似乎是无关紧要的,并且一般开发者会认为这这是正常的,因为他们花费了戴亮时间去测试和使用自己的应用程序。可是,Android 应用程序模型的构建是能够允许用户在不同的应用程序之间进行流畅的切换。这就意味着,当你的后台进程实际上弹出那个UI 的时候,用户可能正在系统的其他部分中,做一些其他的事情,如在接电话。想像一下,如果SMS 服务每次都会在文本消息传入时弹出一个对话框,这很快就会使用户崩溃。这就是为什么Android 标准对于这些事件使用的是通知(Notifications)机制;这使用户能够自己控制。 这仅仅是一个例子,相似的例子数不胜数。比如,如果Activities 没有正确的实现onPause()方法和其他生命周期方法,这将会导致数据丢失。或者如果你的应用程序有意的暴露数据给其他应用程序使用,你应该使用一个ContentProvider,而不是用一个路人皆可见的未加工过的文件或者数据库。 这些例子有一个共同的特点,他们都涉及到程序与程序或则程序与系统之间的交互。系统被设计为将多个应用程序视为一种松耦合组件的联合,而不是大块的代码黑盒。这就允许作为开发人员的你将整个系统看作是一个这些组件的大联合。这允许你干净地封装,无缝地和其他应用程序结合,因而你能设计自己喜欢的程序。 这使一种组件层次的概念(与性能和响应的类层次和方法层次相对应)。至于怎样编写无缝性能很高的代码, “与系统相结合” 一文中将会对此做出介绍,提供代码提示和最佳实例。 构建自定义组件 Android 中,你的应用程序程序与View 类组件有着一种固定的联系,例如按钮(Button)、文本框(TextView), 可编辑文本框(EditText), 列表框(ListView), 复选框(CheckBox),单选框(RadioButton), 滚动条(Gallery), 微调器(Spinner), 等等,还有一些比较先进的有着特殊用途的View 组件,例如 AutoCompleteTextView, ImageSwitcher 和TextSwitcher。除此之外,种类繁多的像 线性布局(LinearLayout), 框架布局(FrameLayout), 这样的布局组件(Layout)也被认为是View 组件,他们是从View类派生过来的。 你的应用程序就是这些控制组件和布局组件以某种方式结合显示在屏幕上,一般来说这些组件对你来说基本够用,但是你也应该知道你是可以通过类继承创建属于自己的组件,一般可以继承像View、Layouts(布局组件)这样的组件,甚至可以是一些比较高级的控制类组件。下面我们说一下为什么要继承: · 你可以为实现某种功能创建一个完全自定义风格的组件,例如用二维的图形创建控制组件实现声音的控制,就像电子控制一样。 · 你可以把几种组件结合形成一个新的组件,你的组件可能同时包含ComboBox (一个能输入的文本列表)和dual-pane selector control(左右两个List 窗口, 你可以分配窗口每一项的从属关系)等等。 · 你可以创建自己的布局组件(Layout)。SDK 中的布局组件已经提供了一系列的选项让你打造属于自己的应用程序,但是高级的开发人员会发现根据现有的 Layout 组件开发新的Layout 组件是很有必要的,甚至是完全从底层开发新的组 件。 · 你可以覆盖一个现有组件的显示或功能。例如,改变EditText(可编辑文本)组件在屏幕上的显示方式(可以参考Notepad 的例子,里面教你如何创建一个下划线的显示页面)。 · 你可以捕获像按键按下这样的事件,以一些通用的方法来处理这些事件(一个游戏的例子)。 为了实现某种目标你可能很有必要扩展一个已经存在的View 组件,下面我们结合一些例子教你如何去做。 基本方法(The Basic Approach ) 下面的一些步骤都比较概括,教你如何创建自己的组件: 1. 让你的类(Class)继承一个现有的View 类或View 的子类。 2. 重载父类的一些方法:需要重载的父类方法一般以‘on’开头,如onDraw(), onMeasure()和 onKeyDown()等等。 o 这个在Activity 或则 ListActivity 派生中同样适用,你需要重载一些生命 周期函数和一些其他功能性的HOOK 函数。 3. 使用你的继承类:一旦你的继承类创建完成,你可以在基类能够使用的地方使用你的继承类,但完成功能就是你自己编写的了。 继承类能够定义在activities 里面,这样你能够方便的调用,但是这并不是必要的(或许在你的应用程序中你希望创建一个所有人都可以使用的组件)。 完全自定义组件(Fully Customized Components) 完全自定义组件的方法可以创建一些用于显示的图形组件(graphical components),也许是一个像电压表的图形计量器,或者想卡拉OK 里面显示歌词的小球随着音乐滚动。 无论那种方式,你也不能单纯的利用组件的结合完成,无论你怎么结合这些现有的组件。 幸运的是,你可以以你自己的要求轻松地创建完全属于自己的组件,你会发现不够用的只是你的想象力、屏幕的尺寸和处理器的性能(记住你的应用程序最后只会在那些性能低于桌面电脑的平台上面运行)。 下面简单介绍如何打造完全自定义的组件: 1. 最为通用的VIEW 类的父类毫无疑问是View 类,因此,最开始你要创建一个基于此类的一个子类。 2. 你可以写一个构造函数从XML 文件中提取属性和参数,当然你也可以自己定义这些属性和参数(也许是图形计量器的颜色和尺寸,或者是指针的宽度和幅度等等) 3. 你可能有必要写自己的事件监听器,属性的访问和修改函数和一些组件本身的功能上的代码。 4. 如果你希望组件能够显示什么东西,你很有可能会重载 onMeasure() 函数,因而你就不得不重载 onDraw() 函数。当两个函数都用默认的,那么 onDraw()函数将不会做任何事情,并且默认的 onMeasure() 函数自动的设置了一个100x100 —的尺寸,这个尺寸可能并不是你想要的。 5. 其他有必要重载的on... 系列函数都需要重新写一次。 onDraw()和onMeasure() onDraw()函数将会传给你一个 Canvas 对象,通过它你可以在二维图形上做任何事情,包括其他的一些标准和通用的组件、文本的格式,任何你可以想到的东西都可以通过它实现。 注意: 这里不包括三维图形如果你想使用三维的图形,你应该把你的父类由View 改为SurfaceView 类,并且用一个单独的线程。可以参考GLSurfaceViewActivity 的例子。 onMeasure() 函数有点棘手,因为这个函数是体现组件和容器交互的关键部分,onMeasure()应该重载,让它能够有效而准确的表现它所包含部分的测量值。这就有点复杂了,因为我们不但要考虑父类的限制(通过onMeasure()传过来的),同时我们应该知道一旦测量宽度和高度出来后,就要立即调用setMeasuredDimension() 方法。 概括的来讲,执行onMeasure()函数分为一下几个阶段: 1. 重载的onMeasure()方法会被调用,高度和宽度参数同时也会涉及到 (widthMeasureSpec 和heighMeasureSpec 两个参数都是整数类型),同时你应该考虑你产品的尺寸限制。这里详细的内容可以参考View.onMeasure(int,int) (这个连接内容详细的解释了整个measurement 操作)。 2. 你的组件要通过onMeasure()计算得到必要的measurement 长度和宽度从而来显示你的组件,它应该与规格保持一致,尽管它可以实现一些规格以外的功能(在这个例子里,父类能够选择做什么,包括剪切、滑动、提交异常或者用不同的参数又一次调用onMeasure()函数)。 3. 一旦高度和宽度计算出来之后,必须调用setMeasuredDimension(int width, int height),否则就会导致异常。 一个自定义组件的例子(A Customized Component Example) 在 API Demos 中的CustomView 提供了以一个自定义组件的例子,这个自定义组件在LabelView 类中定义。 LabelView 例子涉及到了自定义组件的方方面面: · 首先让自定义组件从View 类中派生出来。 · 编写带参数的构造函数(参数可以来源于XML 文件)。这里面的一些处理都已经在View 父类中完成,但是任然有些Labelview 使用的自定义组件特有的新的参数需要处理。 · 一些标准的Public 函数,例如setText(), setTextSize(), setTextColor() · 重载onMeasure()方法来确定组件的尺寸(注意:在LabelView 中是通过一个私有函数measureWidth()来实现的) · 重载onDraw()函数把Lable 显示在提供的canvas 上。 在例子中,你可以通过custom_view_1.xml 看到自定义组件LabelView 的用法。在XML文件中特别要注意的是android:和app:两个参数的混合运用,app:参数表示应用程序中被认为是LabelView 组件的个体,这些也会作为资源在R 类中定义。 组件混合技术Compound Components (or Compound Controls) 如果你不想创建一个完全自定义的组件,而是由几个现有组件的组合产生的新的组件,那么混合组件技术就更加适合。简单的来说,这样把几个现有的组件融合到一个逻辑组合里面可以封装成一个新的组件。例如,一个Combo Box 组件可以看作是是一个EditText 和一个带有弹出列表的Button 组件的混合体。如果你点击按钮为列表选择一项, 在Android 中,其实还有其他的两个View 类可以做到类似的效果: Spinner 和 AutoCompleteTextView,,但是Combo Box 作为一个例子更容易让人理解。 下面简单的介绍如何创建组合组件: 1. 一般从Layout 类开始,创建一个Layout 类的派生类。也许在Combo box 我们会选择水平方向的LinearLayout 作为父类。记住,其他的Layout 类是可以嵌套到里面的,因此混合组件可以是任何组件的混合。注意,正如Activity 一样,你既可以使用外部XML 文件来声明你的组件,也可以嵌套在代码中。 2. 在新的混合组件的构造函数中,首先,调用所有的父类的构造函数,传入对应的参数。然后可以设置你的混合组件的其他的一些方面,在哪创建EditText 组件,又在哪创建PopupList 组件。注意:你同时也可以在XML 文件中引入一些自己的属性和参数,这些属性和参数也可以被你的混合组件所使用。 3. 你也可以创建时间监听器去监听新组件中View 类触发的事件,例如,对List 选项单击事件的监听,你必须在此时间发生后更新你EditText 的值。 4. 你可能创建自己的一些属性,带有访问和修改方法。例如,允许设置EditText 初始值并且提供访问它的方法。 5. 在Layout 的派生类中,你没有必要去重载onDraw()和onMeasure()方法,因为Layout 会有比较好的默认处理。但是,如果你觉得有必要你也可以重载它。 6. 你也可能重载一些on 系列函数,例如通过onKeyDown()的重载,你可以通过按某个键去选择列表中的对应的值。 总之,把Layout 类作为基类有下面几个优点: · 正如activity 一样,你也可以通过XML 文件去声明你的新组件,或者你也可以在代码中嵌套。 · onDraw()函数和onMeasure()函数是没有必要重载的,两个函数已经做得 很好了。 · 你可以很快的创建你的混合组件,并且可以像单一组件那样使用。 混合组件的例子(Examples of Compound Controls) In the API Demos project 在API Demos 工程中,有两个List 类的例子——Example 4 和Example 6,里面的SpeechView 组件是从LinearLayout 类派生过来,实现显示演讲显示功能,对应的原代码是List4.java 和List6.java。 调整现有组件(Tweaking an Existing Component) 在某些情况下,你可能有更简单的方法去创建你的组件。如果你应经有了一个非常类似的组件,你所要做的只是简单的从这个组件派生出你的组件,重在其中一些有必要修改的方法。通过完全自定义组件的方法你也可以同样的实现,但通过冲View 派生产生新的组件,你可以简单获取一些已经存在的处理机制,这些很可能是你所想要的,而没有必要从头开始。 例如,在SDK 中有一个NotePad 的例子(NotePad application )。该例子演示了很多Android 平台实用的细节,例如你会学到从EditView 派生出能够自动换行的记事本。这还不是一个完美的例子,因为相比早期的版本来说,这些API 已经感变了很多,但它确实说明了一些问题。 如果你还未查看该程序,现在你就可以在Eclipse 中导入记事本例程(或仅通过提供的链接查看相应的源代码)。特别是查看NoteEditor.java 中的MyEditText 的定义。 下面有几点要注意的地方: 1. 声明(The Definition) 这个类是通过下面一行代码来定义的: public static class MyEditText extends EditText o 它是定义在NoteEditor activity 类里面的,但是它是共有的 (public),因此如果有必要,它可以通过NoteEditor.MyEditText 从 NoteEditor 外面来调用。 o 它是static 类(静态类),意味着不会出现所谓的通过父类访问数据的 “虚态方法”, 这样就使该类成为一个可以不严重依赖NoteEditor 的单独 类。对于不需要从外部类访问的内联类的创建,这是一个很清晰地思路,保 证所产生的类很小,并且允许它可以被其他的类方便的调用。 o 它是EditText 类的扩展,它是我们选择的用来自定义的父类。当我们 完成以后,新的类就可以作为一个普通的EditText 来使用。 2. 类的初始化 一般来说,父类是首先调用的。进一步来说,这不是一个默认的构造函数,而是 一个带参数的构造函数。因为EditText 是使用从XML 布局文件提取出来的参 数进行创建,因此我们的构造函数也要取出参数并且将这些参数传递给父类。 3. 方法重载 在本例中,仅对onDraw()一个方法进行重载。但你可以很容易地为你的定制组 件重载其他需要的方法。 对于记事本例子来说,通过重载onDraw()方法我们可以在EidtView 的画布 (canvas)上绘制蓝色的线条(canvas 类是通过重写的onDraw()方法传递)。 该函数快要结束时要调用super.onDraw()函数。父类的方法是应该调用,但 是在这个例子里面,我们是在我们划好了蓝线之后调用的。 4. 使用定制组件 现在,我们已经有自己定制的组件了,但是应该怎样使用它呢?在记事本例子中, 定制的组件直接在预定义的布局文件中使用,让我们看一看res/layout 目录 中的note_editor.xml 文件。 class="com.android.notepad.NoteEditor$MyEditText" id="@+id/note" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="@android:drawable/empty" android:padding="10dip" android:scrollbars="vertical" android:fadingEdge="vertical" /> o 该自定义组件在XML 中是作为一个一般的View 类来创建的,并且是通过 全路径包来描述的。注意这里内联类是通过NoteEditor$MyEditText 来 表示的,这是Java 编程中引用内联类的标准方法。 o 在定义中的其他属性和参数将传递给定制组件的构造函数,然后才传到 EditText 构造函数中,因此这些参数也是你使用EditText 组件的参数。注意, 这里你也可以增加你自己的参数,我们将在下面讨论这个问题。 这就是你全部需要做的,诚然这是一个简单的例子。但问题的关键是:你的需求有多复杂,那么你的自定义组件就有多么复杂。 一个更为复杂的组件可能需要重载更多的on 系列函数,并且还要很多特有的函数来充分实现自定义组件的功能。唯一的限制就是你的想象力和你需要组件去执行什么工作。 现在开始你的组件化之旅吧 如你所见,Android 提供了一种精巧而又强大的组件模型,让你尽可能的完成你的工作。从简单的组件调整到组件混合,甚至完全自定义组件,灵活的运用这些技术,你应该可以得到一个完全符合你外观要求的的Android 程序 Android 平台的可选API Android 适用于各种各样的手机,从最低端直到最高端的智能手机。核心的Android API在每部手机上都可使用,但任然有一些API 接口有一些特别的适用范围:这就是所谓的“可选API”。这些API 之所以是“可选的”,主要是因为一个手持设备并不一定要完全支持这类API,甚至于完全不支持。例如,一个手持设备可能没有GPS 或Wi-FI 的硬件。在这个条件下,这类功能的API 任然存在,但不会以相同的方式来工作。例如Location API 任然在没有GPS 的设备上存在,但极有可能完全没有安装功能提供者,意味着这类API 就不能有效地使用。 你的应用应该无障碍地运行或连接在一个可能不支持你API 的设备,因为你的设备上有这些上层接口(the classes)。当然执行起来可能什么也不会做,或者抛出一个异常。 每个API 会做些什么我们可以参考这些API 的说明文档,你应该编写你的程序来适当的处理这类问题。 Wi-Fi API Wi-Fi API 为应用程序提供了一种与那些带有Wi-FI 网络接口的底层无线堆栈相互交流的手段。几乎所有的请求设备信息都是可利用的,包括网络的连接速度、IP 地址、当前状态等等,还有一些其他可用网络的信息。一些可用的交互操作包括扫描、添加、保存、结束和发起连接。 Wi-Fi API 在 android.net.wifi 包中。 定位服务(Location-Based Services) 定位服务允许软件获取手机当前的位置信息。这包括从全球定位系统卫星上获取地理位置,但相关信息不限于此。例如,未来其他定位系统可能会运营,届时,对其相应的API 接口也会加入到系统中。定位服务的API 在android.location 包中。 多媒体API(Media APIs) 多媒体API 主要用于播放媒体文件。这同时包括对音频(如播放MP3 或其他音乐文件以及游戏声音效果等)和视频(如播放从网上下载的视频)的支持,并支持"播放URI地址"(Note:URI 即是统一资源识别地址)模式-在网络上直接播放的流媒体。技术上来说,多媒体API 并不是“可选的”,因为它总是要用到。但是不同的硬件环境上面可能有不同的编解码的硬件机制,因而它又是“可选的”。 多媒体API 在 android.media 包中。 基于OpenGL 的3D 图形(3D Graphics with OpenGL) Android 的主要用户接口框架是一个典型的面向控件的类继承系统。但不要让表面的情况迷惑了你,因为在它下面是一种非常快的2D 和3D 组合的图形引擎,并且支持硬件加速。用来访问平台3D 功能的API 接口是OpenGL ES API。和多媒体API 一样,OpenGL 也不是严格意义上的“可选”,因为这些API 会总是存在并且实现那些固定的功能。但是,一些设备可能有硬件加速环节,使用它的时候就会影响你的应用程序的表现。 l 用于HTTP请求中的常用头 • Accept: text/html,image/* • Accept-Charset: ISO-8859-1 • Accept-Encoding: gzip,compress • Accept-Language: en-us,zh-cn • Host: www.it315.org:80 • If-Modified-Since: Tue, 11 Jul 2000 18:23:51 GMT • Referer: http://www.it315.org/index.jsp • User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0) • Cookie • Connection: close/Keep-Alive • Date: Tue, 11 Jul 2000 18:23:51 GMT l HTTP请求中的常用响应头 • Location: http://www.it315.org/index.jsp • Server:apache tomcat • Content-Encoding: gzip • Content-Length: 80 • Content-Language: zh-cn • Content-Type: text/html; charset=GB2312 • Last-Modified: Tue, 11 Jul 2000 18:23:51 GMT • Refresh: 1;url=http://www.it315.org • Content-Disposition: attachment; filename=aaa.zip • Transfer-Encoding: chunked • Set-Cookie:SS=Q0=5Lb_nQ; path=/search • Expires: -1 • Cache-Control: no-cache • Pragma: no-cache • Connection: close/Keep-Alive • Date: Tue, 11 Jul 2000 18:23:51 GMT l 通用信息头指既能用于请求,又能用于响应的一些消息头。 • Cache-Control: no-cache • Pragma: no-cache • Connection: close/Keep-Alive • Date: Tue, 11 Jul 2000 18:23:51 GMT 数据的存储与访问 接口编程会对软件有影响,少用接口编程。 多用内部类,少创建类。 Androd的文件输出流:context.openFileOutput(“文件名称(不能带有路径)”,操作模式); 操作模式:context.MODE_PRIVATE:如果有新的文件内容会覆盖原来的文件内容,而且只能被创建这个文件的应用所访问。其它应用是不能被访问,因为私有了。而且默认也是这种私有的操作模式。如果文件不存在,会自动创建。如果这个文件已经存在,这个模式会把新的几个问题覆盖旧的内容。 openFileOutput()方法的第一参数用于指定文件名称,不能包含路径分隔符“/” ,如果文件不存在,Android 会自动创建它。创建的文件保存在/data/data/ SAX解析xml文档: //创 建SAX工厂 SAXParserFactory factory = SAXParserFactory.newInstance(); //创建解析器 SAXParser saxParser = factory.newSAXParser(); 创建一个类myDefaultHandler,继承DefaultHandler saxParser.parse(inputStream,handler); 在myDefaultHandler类中重写charactors()方法、startDocument()方法、startElement()方法、endElement()方法。 在startDocument方法中创建一个存xml文件数据的集合对象。 在startElement方法中判断开始标签是否为我们要的数据的开始标签,如果是就创建对象来存取。还要用一个变量记住所经过的标签。 在charactors方法中,判断startElement元素的标签是否为内容字符串的开始标签,如果是就可以new String();获得里面的数据。 在endElement方法中,把记住标签的变量置空。最后还要判断结束标签是否为一个完整对象的结束标签,如果是就可以把这个对象加到集合中,再把这个对象置空。 DOM解析xml文档 先用获得读取xml文件的流。 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document dom = builder.parser(inputStream); Pull解析xml文档 Pull解析器也是基于事件驱动解析。 思路:首先获得pull解析器,然后设置读入的流setIput(inputStream,”UTF-8”);就开始触发第一个事件。得到一个int常量,然后就是用while循环,判断是否为结束文档事件,只要不是结束文档事件,就不要结束。在这个循环中完成数据的获得。 代码如下: public static List XmlPullParser parser = Xml.newPullParser(); try { parser.setInput(inStream, "UTF-8"); int eventType = parser.getEventType(); Person currentPerson = null; List while (eventType != XmlPullParser.END_DOCUMENT) { switch (eventType) { case XmlPullParser.START_DOCUMENT://文档开始事件,可以进行数据初始化处理 persons = new ArrayList break; case XmlPullParser.START_TAG://开始元素事件 String name = parser.getName(); if (name.equalsIgnoreCase("person")) { currentPerson = new Person(); currentPerson.setId(new Integer(parser.getAttributeValue(null, "id"))); } else if (currentPerson != null) { if (name.equalsIgnoreCase("name")) { currentPerson.setName(parser.nextText());// 如果后面是Text元素,即返回它的值 } else if (name.equalsIgnoreCase("age")) { currentPerson.setAge(new Short(parser.nextText())); } } break; case XmlPullParser.END_TAG://结束元素事件 if (parser.getName().equalsIgnoreCase("person") && currentPerson != null) { persons.add(currentPerson); currentPerson = null; }break; } eventType = parser.next(); } inStream.close(); return persons; } catch (Exception e) { e.printStackTrace(); } return null; } 生成xml文件 代码如下: public static String writeXML(List XmlSerializer serializer = Xml.newSerializer(); try { //也可以输出要指定的SD卡文件 File file = new File(Enviroment.getExternalStorageDirectory(),”XXX.xml”); FileOutputStream out = new FileOutputStream(file); serializer.setOutput(writer); serializer.startDocument("UTF-8", true); //第一个参数为命名空间,如果不使用命名空间,可以设置为null serializer.startTag("", "persons"); for (Person person : persons){ serializer.startTag("", "person"); serializer.attribute("", "id", person.getId().toString()); serializer.startTag("", "name"); serializer.text(person.getName()); serializer.endTag("", "name"); serializer.startTag("", "age"); serializer.text(person.getAge().toString()); serializer.endTag("", "age"); serializer.endTag("", "person"); } serializer.endTag("", "persons"); serializer.endDocument(); return writer.toString(); } catch (Exception e) { e.printStackTrace(); } return null; } 第六天 第三个视频: 使用广播: 要继承广播接收者类:BroadcastReceiver,如: ,只要继承这个类,就能完成对短信的窃听。 订阅广播接收者有两种方式:其一: SmsMessage能把PDU(移动的数据格式)数据,转换成短能被阅读的信息。代码如: 短信接收权限: 广播被分为两种不同的类型:“普通广播(Normal broadcasts)”和“有序广播(Ordered broadcasts)”。前者是完全异步的,所有接收者(逻辑上)都在同一时刻运行,对消息传递的效率而言这是很好的做法,但缺点是:接收者不能传递处理结果给下一个接收者,并且无法终止广播Intent的传播;然而后者是逐个执行接收者——系统会按照接收者声明的优先级别(声明在intent-filter元素的android:priority属性中,数越大优先级别越高,取值范围:-1000到1000。也可以调用IntentFilter对象的setPriority()进行设置),按顺序逐次执行。 Context.sendBroadcast() 发送的是普通广播,所有订阅者都有机会获得并进行处理。 Context.sendOrderedBroadcast() 发送的是有序广播,系统会根据接收者声明的优先级别按顺序逐个执行接收者,前面的接收者有权终止广播,如果广播被前面的接收者终止,后面的接收者就再也无法获取到广播。对于有序广播,前面的接收者可以将处理结果存放进广播Intent,然后传给下一个接收者。 在Android中,程序的响应(Responsive)被活动管理器(Activity Manager)和窗口管理器(Window Manager)这两个系统服务所监视。当BroadcastReceiver在10秒内没有执行完毕,Android会认为该程序无响应。所以在BroadcastReceiver里不能做一些比较耗时的操作,否侧会弹出ANR(Application No Response)的对话框。如果需要完成一项比较耗时的工作,应该通过发送Intent给Service,由Service来完成。而不是使用子线程的方法来解决,因为BroadcastReceiver的生命周期很短(在onReceive() 执行后BroadcastReceiver 的实例就会被销毁),子线程可能还没有结束它就先结束了。当然如果BroadcastReceiver结束了,它的宿主进程还在运行,子线程还会继续执行。但宿主进程此时很容易在系统需要内存时被优先杀死,因为它属于空进程(没有任何活动组件的进程)。 每次广播消息到来时都会创建BroadcastReceiver实例并执行onReceive() 方法。 开发调用者跟服务进行通信的应用: 开发在线音乐播放器,与服务器进行播放和停止的操作,就可以用到。 比如:多线程上传下载 |