1. Android Junit1.1常见的测试在介绍Android Junit前先介绍一下常见的测试分类。
根据是否知道源码分类
黑盒测试:不知道源码,是以用户的角度,从输入数据与输出数据的对应关系出发进行测试的。
白盒测试:知道源码,又称结构测试、透明盒测试、逻辑驱动测试或基于代码的测试。
根据测试粒度分类
方法测试: FunctionTest,粒度最低,测试单个方法
单元测试: JunitTest方法里面会调用其他的方法
联调测试: IntegrationTest后台与前台集成,各模块之间的集成测试
根据测试次数分类:
冒烟测试:顾名思义,把设备点冒烟的,随机的去点Android下提供给我们一种冒烟测试的功能:猴子测试,在命令行输入adb shell ,进入Linux内核
测试整个系统: monkey 1000(事件的数量)
测试某个程序:monkey -p 包名事件的数量
压力测试: PressureTest ,给后台用的,主体向被观察者布置一定量任务和作业,借以观察个体完成任务的行为。
1.2Android Junit在实际开发中,开发android软件的过程需要不断地进行测试。而使用Junit测试框架,则是正规Android开发的必用技术,在Junit中可以得到组件,可以模拟发送事件和检测程序处理的正确性。
下面以一个Android为例来介绍Android Junit的使用方法。
可以新建一个Android Application Project也可以直接用存在的工程。
在工程中新建一个类MyTest继承android.test.AndroidTestCase
在该类中可以写测试方法,跟普通java工程使用junit类似。执行的时候右击要执行的方法名->Run as->Android Junit Test 。
在Android清单文件中追加2条信息,如下图高亮部分。
:android:targetPackage 的值可以是任意项目的任意包名,而instrumentation标签中的android:name属性值是由测试框架提供的,值必须为android.test.InstrumentationTestRunner。同样的uses-library属性的值也是由测试框架提供的,必须为android.test.runner。
1.3Android Junit拓展知识:拓展知识并不是必须要求掌握的,心有余力之时可以作为进一步提升的参考。
Android 测试环境的核心是一个Instrumentation框架,在这个框架下,你的测试应用程序可以精确控制应用程序。使用Instrumentation, 你可以在主程序启动之前,创建模拟的系统对象,如Context;控制应用程序的多个生命周期;发送UI事件给应用程序;在执行期间检查程序状态。 Instrumentation框架通过将主程序和测试程序运行在同一个进程来实现这些功能。
通过在测试工程的manifest文件中添 加<instrumentation>元素来指定要测试的应用程序。这个元素的特性指明了要测试的应用程序包名,以及告诉Android如何运行测试程序。下面的图片概要的描述了Android的测试环境:
在 Android中,测试程序也是Android程序,因此,它和被测试程序的书写方式有很多相同的地方。SDK工具能帮助你同时创建主程序工程及它的测试工程。你可以通过Eclipse的ADT插件或者命令行来运行Android测试。Eclipse的ADT提供了大量的工具来创建测试用例,运行以及查看结果。
Android提供了基于JUnit测试框架的测试API来书写测试用例和测试程序。另外,Android还提供了强大的Instrumentation框架,允许测试用例访问程序的状态及运行时对象。
1.3.1 Android Junit中的主要核心API JUnit TestCase类
继承自JUnit的TestCase,不能使用Instrumentation框架。但这些类包含访问系统对象(如Context)的方法。使用 Context,你可以浏览资源,文件,数据库等等。基类是AndroidTestCase,一般常见的是它的子类,和特定组件关联。
子类有:
1、ApplicationTestCase——测试整个应用程序的类。它允许你注入一个模拟的Context到应用程序中,在应用程序启动之前初始化测试参数,并在应用程序结束之后销毁之前检查应用程序。
2、 ProviderTestCase2——测试单个ContentProvider的类。因为它要求使用MockContentResolver,并注入一个IsolatedContext,因此Provider的测试是与OS孤立的。
3、ServiceTestCase——测试单个Service的类。你可以注入一个模拟的Context或模拟的Application(或者两者),或者让Android为你提供Context和MockApplication。
Instrumentation TestCase类
继承自JUnit TestCase类,并可以使用Instrumentation框架,用于测试Activity。使用Instrumentation,Android可 以向程序发送事件来自动进行UI测试,并可以精确控制Activity的启动,监测Activity生命周期的状态。
基类是InstrumentationTestCase。它的所有子类都能发送按键或触摸事件给UI。子类还可以注入一个模拟的Intent。
子类有:
1、ActivityTestCase——Activity测试类的基类。
2、SingleLaunchActivityTestCase——测试单个Activity的类。它能触发一次setup()和tearDown(),而不是每个方法调用时都触发。如果你的测试方法都是针对同一个Activity的话,那就使用它吧。
3、SyncBaseInstrumentation——测试Content Provider同步性的类。它使用Instrumentation在启动测试同步性之前取消已经存在的同步对象。
4、ActivityUnitTestCase——对单个Activity进行单一测试的类。使用它,你可以注入模拟的Context或Application,或者两者。它用于对Activity进行单元测试。
不同于其它的Instrumentation类,这个测试类不能注入模拟的Intent。
5、ActivityInstrumentationTestCase2——在正常的系统环境中测试单个Activity的类。你不能注入一个模拟的 Context,但你可以注入一个模拟的Intent。另外,你还可以在UI线程(应用程序的主线程)运行测试方法,并且可以给应用程序UI发送按键及触 摸事件。
Assert类
Android还继承了JUnit的Assert类,其中,有两个子类,MoreAsserts和ViewAsserts。
1、MoreAsserts类包含更多强大的断言方法,如assertContainsRegex(String, String),可以作正则表达式的匹配。
2、ViewAsserts类包含关于Android View的有用断言方法,如assertHasScreenCoordinates(View, View, int, int),可以测试View在可视区域的特定X、Y位置。这些Assert简化了UI中几何图形和对齐方式的测试。
Mock对象类
Android 有一些类可以方便的创建模拟的系统对象,如Application,Context,Content Resolver和Resource。Android还在一些测试类中提供了一些方法来创建模拟的Intent。因为这些模拟的对象比实际对象更容易使 用,因此,使用它们能简化依赖注入。你可以在android.test和android.test.mock中找到这些类。
它们是:
1、IsolatedContext——模拟一个Context,这样应用程序可以孤立运行。与此同时,还有大量的代码帮助我们完成与Context的通信。这个类在单元测试时很有用。
2、RenamingDelegatingContext——当修改默认的文件和数据库名时,可以委托大多数的函数到一个存在的、常规的Context上。使用这个类来测试文件和数据库与正常的系统Context之间的操作。
3、MockApplication,MockContentResolver,MockContext,MockDialogInterface,MockPackageManager,MockResources ——创建模拟的系统对象的类。它们只暴露那些对对象的管理有用的方法。这些方法的默认实现只是抛出异常。你需要继承这些类并重写这些方法。
Instrumentation TestRunner
Android 提供了自定义的运行测试用例的类,叫做InstrumentationTestRunner。这个类控制应用程序处于测试环境中,在同一个进程中运行测试 程序和主程序,并且将测试结果输出到合适的地方。IntrumentationTestRunner在运行时对整个测试环境的控制能力的关键是使用 Instrumentation。注意,如果你的测试类不使用Instrumentation的话,你也可以使用这个TestRunner。
当你运行一个测试程序时,首先会运行一个系统工具叫做Activity Manager。Activity Manager使用Instrumentation框架来启动和控制TestRunner,这个TestRunner反过来又使用 Intrumentation来关闭任何主程序的实例,然后启动测试程序及主程序(同一个进程中)。这就能确保测试程序与主程序间的直接交互。
1.3.2在测试环境中工作对Android程序的测试都包含在一个测试程序里,它本身也是一个Android应用程序。测试程序以单独的Android工程存在,与正常的Android程序有着相同的文件和文件夹。测试工程通过在manifest文件中指定要测试的应用程序。
每个测试程序包含一个或多个针对特定类型组件的测试用例。测试用例里定义了测试应用程序某些部分的测试方法。当你运行测试程序,Android会在相同进程里加载主程序,然后触发每个测试用例里的测试方法。
1.3.3测试工程为了开始对一个Android程序测试,你需要使用Android工具创建一个测试工程。工具会创建工程文件夹、文件和所需的子文件夹。工具还会创建一个manifest文件,指定被测试的应用程序。
1.3.4测试用例一个测试程序包含一个或多个测试用例,它们都继承自Android TestCase类。选择一个测试用例类取决于你要测试的Android组件的类型以及你要做什么样的测试。一个测试程序可以测试不同的组件,但每个测试用例类设计时只能测试单一类型的组件。
一些Android组件有多个关联的测试用例类。在这种情况下,在可选择的类间,你需要判断你要进行的测试类型。例如,对于Activity来说,你有两个选择,ActivityInstrumentationTestCase2和ActivityUnitTestCase。
ActivityInstrumentationTestCase2设计用于进行一些功能性的测试,因此,它在一个正常的系统环境中测试Activity。你可以注入模拟的Intent,但不能是模拟的Context。一般来说,你不能模拟Activity间的依赖关系。相比而言,ActivityUnitTestCase设计用于单元测试,因此,它在一个孤立的系统环境中测试Activity。换句话说,当你使用这个测试类时,Activity不能与其它Activity交互。
作为一个经验法则,如果你想测试Activity与Android的交互的话,使用ActivityInstrumentationTestCase2。如果你想对一个Activity做回归测试的话,使用ActivityUnitTestCase。
1.3.5测试方法每个测试用例类提供了可以建立测试环境和控制应用程序的方法。例如,所有的测试用例类都提供了JUnit的setUp()方法来搭建测试环境。另外,你可以添加方法来定义单独的测试。当你运行测试程序时,每个添加的方法都会运行一次。如果你重写了setUp()方法,它会在每个方法运行前运行。相似 的,tearDown()方法会在每个方法之后运行。
测试用例类提供了大量的对组件启动和停止控制的方法。由于这个原因,在运行测试之 前,你需要明确告诉Android启动一个组件。例如,你可以使用getActivity()来启动一个Activity。在整个测试用例期间,你只能调 用这个方法一次,或者每个测试方法一次。甚至你可以在单个测试方法中,调用它的finishing()来销毁Activity,然后再调用 getActivity()重新启动一个。
1.3.6运行测试并查看结果编译完测试工程后,你就可以使用系统工具Activity Manager来运行测试程序。你给Activity Manager提供了TestRunner的名(一般是InstrumentationTestRunner,在程序中指定);名包括被测试程序的包名和 TestRunner的名。Activity Manager加载并启动你的测试程序,杀死主程序的任何实例,然后在测试程序的同一个进程里加载主程序,然后传递测试程序的第一个测试用例。这个时 候,TestRunner会接管这些测试用例,运行里面的每个测试方法,直到所有的方法运行结束。
如果你使用Eclipse,结果会在JUnit的面板中显示。如果你使用命令行,将输出到STDOUT上。
1.3.7测试什么?除了一些功能测试外,这里还有一些你应该考虑测试的内容:
Activity生命周期事件:你应该测试Activity处理生命周期事件的正确性。例如,一个Activity应该在pause或destroy事件 时保存它的状态。记住一点的是屏幕方向的改变也会引发当前Activity销毁,因此,你需要测试这种偶然情况确保不会丢失应用程序状态。
数据库操作:你应该确保数据库操作能正确处理应用程序状态的变化。使用android.test.mock中的模拟对象。
屏幕大小和分辨率:在发布程序之前,确保在所有要运行的屏幕大小和分辨率上测试通过。你可以使用AVD来测试,或者使用真实的目标设备进行测试。
1.3.8 UI线程中测试Activity运行在程序的UI线程里。一旦UI初始化后,例如在Activity的onCreate()方法后,所有与UI的交互都必须运行在UI线程里。当你正常运行程序时,它有权限可以访问这个线程,并且不会出现什么特别的事情。当你运行测试程序时,这一点发生了变化。在带有instrumentation的类里,你可以触发方法在UI线程里运行。其它的测试用例类不允许这么做。为了一个完整的测试方法都在UI线程里运行,你可以使用@UIThreadTest来声明线程。注意,这将会在UI线程里运行方法里所有的语句。不与UI交 互的方法不允许这么做;例如,你不能触发Instrumentation.waitForIdleSync()。
如果让方法中的一部分代码运行在UI线程的话,创建一个匿名的Runnable对象,把代码放到run()方法中,然后把这个对象传递给appActivity.runOnUiThread(),在这里,appActivity就是你要测试的app对象。
2. Android中的LogCat和LogAndroid的Logcat用于显示系统的调试信息,Log是android.util.Log包下的类,用于日志的输出。下面内容将分别介绍LogCat和Log的使用,同时补充了在Android系统中调用LogCat日志的相关知识作为拓展内容。
2.1 LogCat的使用Android LogCat的获取有两种方式:1、DDMS提供的LogCat视图2、通过adb命令行
DDMS提供的LogCat视图如下
如果该视图没有打开,点击window->show view->other->android->Logcat来进行选择。
视图的左侧可以选择或者添加过滤信息,运行一个应用程序时,此处会默认创建一个该包的过滤。视图的右上角区域用于选择LogCat的log级别,共有verbose、debug、info、warn、error、assert6个可选项。如图所示:
。
该视图的主体部分是log的详细信息,包括错误级别(Level)、时间(Time)、进程ID(PID)、线程ID(TID)、应用程序包名(Application)、标签(Tag)、日志正文(Text)。
:其中的TID并不等同于Java中的Thread.currentThread().getId(),而是我们Linux中的Thread ID,跟PID相同。
:Level的错误级别有V(verbose)、D(debug)、I(info)、W(warn)、E(error)。
错误级别 | 意义 | 严重等级 | 显示颜色 |
verbose | 所有信息都显示 | 最低 | 黑色 |
debug | 显示调试信息 | 第四严重 | 蓝色 |
info | 普通信息 | 第三严重 | 绿色 |
warn | 警告信息 | 第二严重 | 黄色 |
error | 错误信息 | 最高 | 红色 |
通过命令行调用LogCat
在控制台中输入adb logcat 然后按回车键即可看到LogCat信息,如果需要终止按Ctrl+C键即可。
执行adb logcat >D:/a.txt 则将日志输出到D:/a.txt文件中。按Ctrl+C键终止日志的输出。
:上面介绍的只是adb logcat命令的最简单用法,其实该命令还有多种可选参数供选择,这里就不再详细说明。
2.2 Log的使用android.util.Log常用的方法有以下5个:Log.v() Log.d() Log.i() Log.w() 以及 Log.e() 。根据首字母对应VERBOSE,DEBUG,INFO, WARN,ERROR。
1、Log.v 的调试颜色为黑色的,任何消息都会输出,这里的v代表verbose啰嗦的意思,平时使用就是Log.v("","");
2、Log.d的输出颜色是蓝色的,仅输出debug调试的意思,但他会输出上层的信息,过滤起来可以通过DDMS的Logcat标签来选择.
3、Log.i的输出为绿色,一般提示性的消息information,它不会输出Log.v和Log.d的信息,但会显示i、w和e的信息
4、Log.w的意思为橙色,可以看作为warning警告,一般需要我们注意优化Android代码,同时选择它后还会输出Log.e的信息。
5、Log.e为红色,可以想到error错误,这里仅显示红色的错误信息,这些错误就需要我们认真的分析,查看栈的信息了。
2.3Android程序获取LogCat信息下面通过创建一个Android工程来演示如何在代码中实时获取LogCat信息。
创建一个新工程,这里工程名为LogCat
在这个工程中使用默认的MainActivity.java类和默认的布局文件。
修改布局文件
修改MainActivity.java代码
代码第一部分:
代码第二部分:
代码第三部分(完):
将项目运行在模拟器上,并点击按钮
运行结果如图所示:
3. Android系统中文件的权限安卓是基于linux开发的,因此介绍android系统文件的权限,需要从介绍linux说起。linux系统权限简介:
一个文件一共有三个组别:用户、群组、其它
其中每个组包含三种权限:读r、写w、执行x
也就是说一个文件共有9个权限属性。
从左往右一到三位是[用户],四到六位是[群组],七到九位是[其它]
举例:通过DDMS的File Explorer查看文件信息,可以看到某个文件的权限为:rw-r--rwx
他的意思就是[用户]对其享有读写权限,[群组]享有读权限,[其它]享有读写执行权限。
4. Android下的文件存储在Android系统中我们常用的数据存储方式有4种。1、存储中手机内存中(ROM)2、存储在SD卡中3、存储在SharedPreferences中4、存储在SQLite数据库中。在本文档中只介绍前3种数据存储方式,而SQLite将在下一篇中做详细说明。
4.1保存文件到手机内存通过一个模拟用户登录的案例来介绍如何将文件保存到手机内存中。
新创建一个Android工程,如图。
使用默认的布局文件盒默认的Activity。修改布局文件。
在该布局文件中采用了LinearLayout布局。
布局文件第一部分:
: 属性说明,在以后的文档中对新出现的属性都会进行详细介绍,而已经使用过的属性则不再重复介绍。
上面布局文件中的android:orientation属性在LinearLayout布局中必须指定,有两个可选项:vertical和horizontal,分别代表垂直布局和水平布局。
android:layout_marginTop="10dp" 代表该组件头部距离上一个组件的间隔为10dp。
布局文件第二部分(完):
: 上面的LinearLayout布局中嵌套了LinearLayout组件。第二个LinearLayout布局采用水平方向。
android:gravity="right"的意思是在当前容器内的子元素右靠起的方式布局。
编写Activity代码实现登录功能
Activity主要功能是完成用户的登陆过程,在该过程中需要将用户的数据保存到手机内存(ROM而不是RAM)中。根据分层设计的思想,将有关文件的读、写操作封装为一个工具类来实现。该工具类在下一步会详细列出,这里先引用。
MainActivity.java代码第一部分:
MainActivity.java代码第二部分:
MainActivity.java代码第三部分(完):
创建一个新包,包名为com.itheima.rom.service,然后创建SafeFileService.java类
: 这里的*Service类并不是Android中的Service类,而只是对业务逻辑的抽取而命名的。在以后的学习中会遇到大量的*Service,这些Service则是Android API中很重要的一部分。有关Service会在以后的文档中作详细的介绍。
SafeFileService.java代码第一部分:
SafeFileService.java代码第二部分:
SafeFileService.java代码第三部分(完):
将项目部署到AVM上,并进行测试
运行截图1:
输入用户名(wzy)和密码(123456),不选择保存密码选项:
发现登录成功了。然后退出当前应用并在此打开该程序的界面的时候发现用户名和密码没用回显,这结果是正确的因为我们上次登录的时候没有勾选“保存密码”选项。这一次我们登录前勾选“保存密码”选项,然后再退出程序,重新打开应用界面,发现用户名和密码已经成功回显。
: 在该案例中我们将用户名和密码以文件的形式保存在内存中,并且用户名和密码只是用“:”分隔开,这是严重bug的设计,如果用户的用户名或者密码中有“:”字符,那么该程序就无法获取正确的答案。之所以这么做是因为这个项目只是单纯为了演示如何将用户信息保存在手机内存中。
该案例会在data/data/com.itheima.rom/files文件夹中创建一个info.txt文件。如下图。
4.2保存文件到SD卡在该案例中我们依然采用4.1章节的案例。只需要对SafeFileService.java文件进行修改,将文件的保存路径改为SD卡。如何在SD卡上读、写文件是我们这章节的重点的内容。
:如果想将数据文件保存到SD卡的前提是得为你的Android虚拟机创建一个SD卡,为了演示我们分配32M内存空间即可。分配太多会影响虚拟机的启动时间。下图演示了如何分配SD卡空间。
将4.1章节中的SaveFileService类进行修改
也可以重新复制一下这个类,并起名为SaveFileServiceSD,然后只需将MainActivity类中的SaveFileService全部替换为SaveFileServiceSD,这样我们的两个文件都将保存下来,本人就是这么干的。
修改该类中的saveFile方法,这也是重点内容。
:上面这个方法对SDCard进行了写入文件的操作,因此需要需要在清单文件中添加权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
:如果不加该权限,保存文件会失败,但是很遗憾的是这时候系统并没有报任何异常。
修改该类中的saveFile方法。
代码修改起来很简单,只需要修改一个地方即可。在第一个方法中已经展示了如何判断SDCard是否处于可用状态,处于节省篇幅的考虑以后的方法中就不再做判断,如果是在实际开发中,所有关于SDCard的读、写操作都建议大家进行判断。
修改该类中的findUser方法。
同样的只需要修改一个地方即可。
运行该应用于模拟器上。输入正确的用户名和密码,并勾选“保存密码”选项。然后
打开DDMS的File Explorer 窗口。可以看到sdcard目录下产生了info.txt文件。
当取消“保存密码”选项后,在点击登录,然后再打开DDMS的File Explorer窗口,发现该文件已经被删除了。
:当我们的应用界面处于打开状态时,我们重新部署该程序到模拟器上经常会有类似如下异常产生。产生这样的异常很好解决,直接在的模拟器中将该应用退出。然后再部署一般就可以解决问题。
4.3使用SharedPreferencesSharedPreferences是Android平台上一个轻量级的存储类,主要是保存一些常用的配置,它提供了Android平台常规的Long、Int、String字符串型的保存,它是什么样的处理方式呢?SharedPreferences类似过去Windows系统上的ini配置文件,但是它分为多种权限,可以全局共享访问,整体效率来看不是特别的高,对于常规的轻量级而言比SQLite要好不少,如果真的存储量不大可以考虑自己定义文件格式。xml 处理时Dalvik会通过自带底层的本地XML Parser解析,比如XMLpull方式,这样对于内存资源占用比较好。
这种方式应该是用起来最简单的Android读写外部数据的方法了。他以一种简单、 透明的方式来保存一些用户个性化设置的字体、颜色、位置等参数信息。一般的应用程序都会提供“设置”或者“首选项”的这样的界面,那么这些设置最后就可以通过Preferences来保存,而程序员不需要知道它到底以什么形式保存的,保存在了什么地方。当然,如果你愿意保存其他的东西,也没有什么限制。只是在性能上不知道会有什么问题。
在Android系统中该文件保存在:/data/data/PACKAGE_NAME /shared_prefs 目录下。
下面通过修改4.1章节中的案例来演示如何使用SharedPreferences。在此案例中对用户数据的操作就不再需要SaveFileService类,直接在MainActivity类中使用SharedPreferences API即可。由于操作SharedPreferences不需要权限,因此清单文件中关于写SDCard的权限可以删除,当然放在那里不删除也是可以的。
修改后的MainActivity如下:
MainActivity.java代码第一部分:
MainActivity.java代码第二部分:
MainActivity.java代码第三部分:
运行该程序,实现效果跟4.1、4.2章节是相同的,因此效果图就不再展示。
:运行上面程序后系统会自动创建一个文件:/data/data/com.itheima.rom/shared_prefs/info.xml
文件目录结构如下图。
5. 获取SD卡和内存的空间信息本章节将通过案例演示在Android中如何获取SDCard和手机内存的总空间和可用空间等信息。
创建一个新的Android工程,工程名字为《获取存储空间大小》,包名为:com.itheima.storageSize
这里使用默认生成的布局文件和Activity类。
修改布局文件activity_main.xml
布局文件第一部分:
布局文件第二部分(完):
编写业务代码
业务代码第一部分:
业务代码第二部分:
:在上述方法中,我们使用了两种方法分别计算SDCard的内存信息。其中第一种方法是JDK API提供的,第二种方法是Android API提供的。这两种方法获取到的总容量和可用容量信息在日志中输出见下图。发现得到的结果是一样的。在我们Android的开发中自己比较推荐使用第二种方法。因为第二种方法Google工程师专门针对Android系统设计的。相对更加的适用,在看Android源码的时候我们也能发现Android系统自己计算内存容量的时候使用的就是第二种方法。
业务代码第三部分(完):
运行该程序,分别获取SD卡信息和内存信息。效果如图。
:上面布局文件中出现了如下的属性:
在LinearLayout的android:orientation="horizontal"(或者默认)的情况下,如果android:layout_width="0dp" 那么就必须添加android:layout_weight="1"属性,属性值是float类型的。下面将重点说明该属性的真实含义。
5.1 android:layout_weight属性详解Layout_weight属性的作用:它是用来分配剩余空间的一个属性,你可以设置他的权重。通过几个小例子来说明该属性的用法,比如有如下布局文件:
运行结果是:
看上面代码发现只有Button2使用了Layout_weight属性,并赋值为了1,而Button1和Button3没有设置Layout_weight这个属性,根据API,可知,他们默认是0。
下面我就来讲,Layout_weight这个属性的真正的意思:Android系统先按照你设置的3个Button高度Layout_height值wrap_content,给你分配好他们3个的高度,
然后会把剩下来的屏幕空间全部赋给Button2,因为只有他的权重值是1,这也是为什么Button2占了那么大的一块空间。
通过上面的例子我相信大家对该属性已经有了一定的了解,那么再看下面的例子,相信大家会得到进一步的了解。
布局文件(完):
运行效果如下:
对于上面运行效果的分析:三个文本框的属性 layout_width=“wrap_content ”时,系统先给3个TextView分配他们的宽度值wrap_content(宽度足以包含他们的内容1,2,3即可),然后会把剩下来的屏幕空间按照1:2:3的比列分配给3个textview,所以就出现了上面的图像。
修改上面的布局文件,当layout_width=“fill_parent”时,如果分别给三个TextView设置他们的Layout_weight为1、2、2的话,就会出现下面的效果:
我们会发现1的权重小,反而分的多了,这是为什么呢?我们可以简单的理解为当layout_width=“fill_parent”时,weight值越小权重越大,优先级越高,而其真正的原因是layout_width="fill_parent"的原因造成的。依照上面理解我们来分析:
系统先给3个textview分配他们所要的宽度fill_parent,也就是说每一都是填满他的
父控件,这里就是屏幕的宽度那么这时候的剩余空间=1个parent_width-3个parent_width=-2个parent_width (parent_width指的是屏幕宽度 )。
那么第一个TextView的实际所占宽度应该=parent_width + 他所占剩余空间的权重比列1/5 * 剩余空间大小(-2 parent_width)=3/5parent_width,同理第二个TextView的实际所占宽度=parent_width+ 2/5*(-2parent_width)=1/5parent_width;第三个TextView的实际所占宽度=parent_width + 2/5*(-2parent_width)=1/5parent_width;所以就是3:1:1的比列显示了。
6. XML文件的读取和序列化在Android平台上可以使用Simple API for XML(SAX) 、Document Object Model(DOM)和Android自带的pull解析器解析XML文件。
1. SAX解析XML文件
SAX是一个解析速度快并且占用内存少的xml解析器,非常适合用于Android等移动设备。 SAX解析XML文件采用的是事件驱动,也就是说,它并不需要解析完整个文档,在按内容顺序解析文档的过程中,SAX会判断当前读到的字符是否合法XML语法中的某部分,如果符合就会触发事件。所谓事件,其实就是一些回调(callback)方法,这些方法(事件)定义在ContentHandler接口。
2. DOM解析XML文件
DOM解析XML文件时,会将XML文件的所有内容读取到内存中,然后允许您使用DOM API遍历XML树、检索所需的数据。使用DOM操作XML的代码看起来比较直观,并且,在某些方面比基于SAX的实现更加简单。但是,因为DOM需要将XML文件的所有内容读取到内存中,所以内存的消耗比较大,特别对于运行Android的移动设备来说,因为设备的资源比较宝贵,所以建议还是采用SAX来解析XML文件,当然,如果XML文件的内容比较小采用DOM是可行的。
3.Pull解析器解析XML文件
Pull解析器的运行方式与SAX解析器相似。它提供了类似的事件,如:开始元素和结束元素事件,使用parser.next()可以进入下一个元素并触发相应事件。事件将作为数值代码被发送,因此可以使用一个switch对感兴趣的事件进行处理。当元素开始解析时,调用parser.nextText()方法可以获取下一个Text类型元素的值。
4.SAX和PULL区别
SAX解析器的工作方式是自动将事件推入事件处理器进行处理,因此你不能控制事件的处理主动结束;而Pull解析器的工作方式为允许你的应用程序代码主动从解析器中获取事件,正因为是主动获取事件,因此可以在满足了需要的条件后不再获取事件,结束解析。
你随便找个sax和pull的例子比较一下就可以发现,pull是一个while循环,随时可以跳出,而sax不是,sax是只要解析了,就必须解析完成。
: 在本文档中我们将通过一个案例来重点讨论Android自带的pull解析器的使用。首先介绍在Android中XML的序列化。
演示XML的序列化
创建一个工程,使用其默认的布局文件和Activity类。布局文件如下:
Activity类比较简单,出于篇幅考虑,这里只给出核心方法。
makeXML方法:
:由于此方法中对SDCard进行了写操作,因此必须加上如下权限:android.permission.WRITE_EXTERNAL_STORAGE。
运行上面程序后发现在/mnt/sdcard下面多了一个sms.xml文件。将该文件导出到电脑中打开查看如下内容:
演示XML的Pull解析
已知weather.xml文件中存储着天气信息,使用pull解析xml,将天气信息解析出来并显示。weather.xml内容如下:
布局文件比较简单,出于节省篇幅的考虑就不再给出。直接给出java代码。首先是Weather.java类,该类主要用户对数据的封装,这里只给出该类的属性。
创建PullService.java类,该类用于完成解析XML的业务逻辑。
将weather.xml文件拷贝到src目录下,MainActivity.java中修改pullXML方法
运行结果如图:
7. 案例-学生管理系统该案例的重点在于xml的解析和序列化,同时该案例的布局方案也是值得学习的知识点。系统运行如下图:
需求说明:1、点击添加按钮,将文本框中的数据添加到页面
- 点击保存数据按钮,将页面中的所有数据保存到xml文件中
- 点击清空数据,将页面中的所有数据清空,xml中的数据不作处理
- 点击恢复数据,将xml文件中的数据展示在页面
使用工程默认的布局文件(第一部分):
布局文件第二部分:
布局文件第三部分:
布局文件第四部分(完):
创建User类,其属性信息和在XML中的格式分别如下:
为了方便演示,这里将核心业务写在默认的MainActivity类中:
代码第一部分:
代码第二部分:
该部分代码主要对布局文件中元素进行了初始化操作。
代码第三部分:
代码第四部分:saveData()方法,在界面点击添加按钮的业务逻辑。
代码第五部分(核心代码):seriaXML(List<User> users)方法的实现
代码第六部分:实现清除数据功能,对应的是clearData()方法,这里的业务逻辑比较简单,只需要清楚界面的数据就行,其实就是将页面LinearLayout容器里面的所有子元素清除即可。
代码第七部分:实现恢复数据功能,也就是点击恢复数据按钮后将xml文件中的数据显示在界面。
代码第八部分(核心功能):实现pullXML()方法,也就是将xml文件中的数据用pull方式解析出来,并封装在List<User>集合中。